diff --git a/Framework/CMakeLists.txt b/Framework/CMakeLists.txt index 836d6711076f67f13e889f6d051b4222a6e04e9d..a60b8f06a0ae5e6f8d060042ef7153ba70623edb 100644 --- a/Framework/CMakeLists.txt +++ b/Framework/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory (Logging) add_subdirectory (StackInterface) add_subdirectory (ProcessSequence) add_subdirectory (Cascade) +add_subdirectory (Random) diff --git a/Framework/Random/CMakeLists.txt b/Framework/Random/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..3c1947d2508b318eafe6ab426bceac9af7b9e752 --- /dev/null +++ b/Framework/Random/CMakeLists.txt @@ -0,0 +1,49 @@ + +set ( + CORSIKArandom_SOURCES + RNGManager.cc + ) + +set ( + CORSIKArandom_HEADERS + RNGManager.h + ) + +set ( + CORSIKArandom_NAMESPACE + corsika/random + ) + +add_library (CORSIKArandom STATIC ${CORSIKArandom_SOURCES}) +CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKArandom ${CORSIKArandom_NAMESPACE} ${CORSIKArandom_HEADERS}) + +target_include_directories ( + CORSIKArandom + PUBLIC + $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include> + $<INSTALL_INTERFACE:include/> + ) + +# target dependencies on other libraries (also the header onlys) +# none + +install ( + TARGETS CORSIKArandom + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + PUBLIC_HEADER DESTINATION include/${CORSIKArandom_NAMESPACE} + ) + + +# -------------------- +# code unit testing +add_executable (testRandom testRandom.cc) + +target_link_libraries ( + testRandom + CORSIKArandom + CORSIKAthirdparty # for catch2 + ) + +add_test (NAME testRandom COMMAND testRandom) + diff --git a/Framework/Random/RNGManager.cc b/Framework/Random/RNGManager.cc new file mode 100644 index 0000000000000000000000000000000000000000..01bc247b40232137fca29578125fb31b777f63c9 --- /dev/null +++ b/Framework/Random/RNGManager.cc @@ -0,0 +1,24 @@ +#include <corsika/random/RNGManager.h> + +void corsika::random::RNGManager::RegisterRandomStream(std::string const& pStreamName) { + corsika::random::RNG rng; + + if (auto const& it = seeds.find(pStreamName); it != seeds.end()) { + rng.seed(it->second); + } + + rngs[pStreamName] = std::move(rng); +} + +corsika::random::RNG& corsika::random::RNGManager::GetRandomStream(std::string const& pStreamName) { + return rngs.at(pStreamName); +} + +std::stringstream corsika::random::RNGManager::dumpState() const { + std::stringstream buffer; + for (auto const& [streamName, rng] : rngs) { + buffer << '"' << streamName << "\" = \"" << rng << '"' << std::endl; + } + + return buffer; +} diff --git a/Framework/Random/RNGManager.h b/Framework/Random/RNGManager.h new file mode 100644 index 0000000000000000000000000000000000000000..52e53cd7570b3d7318aa1424c04cbe0c3b2c30c7 --- /dev/null +++ b/Framework/Random/RNGManager.h @@ -0,0 +1,44 @@ +#ifndef _include_RNGManager_h_ +#define _include_RNGManager_h_ + +#include <map> +#include <random> +#include <sstream> +#include <string> + +/*! + * With this class modules can register streams of random numbers. + */ + +namespace corsika::random { + + using RNG = std::mt19937; //!< the actual RNG type that will be used + + class RNGManager { + std::map<std::string, RNG> rngs; + std::map<std::string, std::seed_seq> seeds; + + public: + /*! + * This function is to be called by a module requiring a random-number + * stream during its initialization. + * + * \throws sth. when stream \a pModuleName is already registered + */ + void RegisterRandomStream(std::string const& pStreamName); + + /*! + * returns the pre-stored stream of given name \a pStreamName if + * available + */ + RNG& GetRandomStream(std::string const& pStreamName); + + /*! + * dumps the names and states of all registered random-number streams + * into a std::stringstream. + */ + std::stringstream dumpState() const; + }; + +} // namespace Random +#endif diff --git a/Framework/Random/testRandom.cc b/Framework/Random/testRandom.cc new file mode 100644 index 0000000000000000000000000000000000000000..f2d6fcdcad92c34bfb65f18e5701fe93ff1616f9 --- /dev/null +++ b/Framework/Random/testRandom.cc @@ -0,0 +1,26 @@ +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one + // cpp file +#include <catch2/catch.hpp> + +#include <corsika/random/RNGManager.h> +#include <iostream> + +using namespace corsika::random; + +SCENARIO("random-number streams can be registered and retrieved") { + GIVEN("a RNGManager") { + RNGManager rngManager; + + WHEN("a sequence is registered by name") { + rngManager.RegisterRandomStream("stream_A"); + + THEN("the sequence can be retrieved") { + REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A")); + } + + THEN("an unknown sequence cannot be retrieved") { + REQUIRE_THROWS(rngManager.GetRandomStream("stream_UNKNOWN")); + } + } + } +}