diff --git a/corsika/detail/framework/random/RNGManager.inl b/corsika/detail/framework/random/RNGManager.inl index 749107cd9206adb3e3e3fc78b9e666f59f7b7f27..a7385ab32ade1a74c8b2f0d5713b43ec8f5a297e 100644 --- a/corsika/detail/framework/random/RNGManager.inl +++ b/corsika/detail/framework/random/RNGManager.inl @@ -22,10 +22,20 @@ namespace corsika { rngs[pStreamName] = std::move(rng); } - inline RNG& RNGManager::GetRandomStream(std::string const& pStreamName) { - return rngs.at(pStreamName); + inline RNG& RNGManager::GetRandomStream(std::string const& pStreamName) { + if (IsRegistered(pStreamName)) { + return rngs.at(pStreamName); + } else { // this stream name is not in the map + throw std::runtime_error("'" + pStreamName + "' is not a registered stream."); + } + } + + + inline bool RNGManager::IsRegistered(std::string const& pStreamName) const { + return rngs.count(pStreamName) > 0; } + inline std::stringstream RNGManager::dumpState() const { std::stringstream buffer; for (auto const& [streamName, rng] : rngs) { @@ -41,11 +51,15 @@ namespace corsika { inline void RNGManager::SeedAll() { std::random_device rd; - + std::seed_seq sseq{rd(), rd(), rd(), rd(), rd(), rd()}; for (auto& entry : rngs) { - std::seed_seq sseq{rd(), rd(), rd(), rd(), rd(), rd()}; - entry.second.seed(sseq); + std::vector<std::uint32_t> seeds(1); + sseq.generate(seeds.begin(), seeds.end()); + std::uint32_t seed = seeds[0]; + C8LOG_TRACE("Random seed stream {} seed {}", entry.first, seed); + entry.second.seed(seed); } } + } // namespace corsika diff --git a/corsika/detail/modules/pythia8/Random.inl b/corsika/detail/modules/pythia8/Random.inl deleted file mode 100644 index cfd19bb19cb768a79c303760e34733f5e23f0f15..0000000000000000000000000000000000000000 --- a/corsika/detail/modules/pythia8/Random.inl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu - * - * See file AUTHORS for a list of contributors. - * - * This software is distributed under the terms of the GNU General Public - * Licence version 3 (GPL Version 3). See file LICENSE for a full version of - * the license. - */ - -#pragma once - -#include <corsika/modules/pythia8/Random.hpp> - -namespace corsika::pythia8 { - - double Random::flat() { return fDist(fRNG); } - -} // namespace corsika::pythia8 diff --git a/corsika/framework/logging/Logging.h b/corsika/framework/logging/Logging.h new file mode 100644 index 0000000000000000000000000000000000000000..814f35de4de5c025c3bab8eaa7fdf51e135e7010 --- /dev/null +++ b/corsika/framework/logging/Logging.h @@ -0,0 +1,189 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +/** + * @File Logging.h + * + * CORSIKA8 logging utilities. + * + * See testLogging.cc for a complete set of examples for + * how the logging functions should be used. + */ + +#pragma once + +// Configure some behaviour of sdlog. +// This must be done before spdlog is included. + +// use the coarse system clock. This is *much* faster +// but introduces a real time error of O(10 ms). +#define SPDLOG_CLOCK_COARSE + +// do not create a default logger (we provide our own "corsika" logger) +#define SPDLOG_DISABLE_DEFAULT_LOGGER + +// use __PRETTY_FUNCTION__ instead of __FUNCTION__ where +// printing function names in trace statements. This is much +// nicer than __FUNCTION__ under GCC/clang. +#define SPDLOG_FUNCTION __PRETTY_FUNCTION__ + +// if this is a Debug build, include debug messages in objects +#ifdef DEBUG +// trace is the highest level of logging (ALL messages will be printed) +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE +#else // otherwise, remove everything but "critical" messages +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_CRITICAL +#endif + +#include <spdlog/fmt/ostr.h> // will output whenerver a streaming operator is found +#include <spdlog/sinks/stdout_color_sinks.h> +#include <spdlog/spdlog.h> + +namespace corsika::logging { + + // bring spdlog into the corsika::logging namespace + using namespace spdlog; + + /* + * The default pattern for CORSIKA8 loggers. + */ + const std::string default_pattern{"[%n:%^%-8l%$] %v"}; + + /** + * Create a new C8-style logger. + * + * Use this if you are explicitly (and can guarantee) that you + * are creating a logger for the first time. It is recommended + * that for regular usage, the `GetLogger` function is used instead + * as that will also create the logger if it has not yet been created. + * + * Calling `CreateLogger` twice to create the same logger will + * result in an spdlog duplicate exception. + * + * @param name The unique name of the logger. + * @param defaultlog If True, set this as the default logger. + * @returns The constructed and formatted logger. + */ + inline auto CreateLogger(std::string const& name, bool const defaultlog = false) { + + // create the logger + // this is currently a colorized multi-threading safe logger + auto logger = spdlog::stdout_color_mt(name); + + // set the default C8 format + logger->set_pattern(default_pattern); + + // if defaultlog is True, we set this as the default spdlog logger. + if (defaultlog) { spdlog::set_default_logger(logger); } + + // and return the logger + return logger; + } + + /** + * Get a smart pointer to an existing logger. + * + * This should be the default method for code to obtain a + * logger. If the logger *does not* exist, it is *created* and + * returned to the caller. + * + * This should be preferred over `CreateLogger`. + * + * @param name The name of the logger to get. + * @param defaultlog If True, make this the default logger. + * @returns The constructed and formatted logger. + */ + inline auto GetLogger(std::string const& name, bool const defaultlog = false) { + + // attempt to get the logger from the registry + auto logger = spdlog::get(name); + + // weg found the logger, so just return it + if (logger) { + return logger; + } else { // logger was not found so create it + return CreateLogger(name, defaultlog); + } + } + + /** + * The default "corsika" logger. + */ + inline auto corsika = GetLogger("corsika", true); + + /** + * Set the default log level for all *newly* created loggers. + * + * @param name The minimum log level required to print. + * + */ + inline auto SetDefaultLevel(level::level_enum const minlevel) -> void { + spdlog::set_level(minlevel); + } + + /** + * Set the log level for the "corsika" logger. + * + * @param name The minimum log level required to print. + * + */ + inline auto SetLevel(level::level_enum const minlevel) -> void { + corsika->set_level(minlevel); + } + + /** + * Set the log level for a specific logger. + * + * @param logger The logger to set the level of. + * @param name The minimum log level required to print. + * + */ + template <typename TLogger> + inline auto SetLevel(TLogger& logger, level::level_enum const minlevel) -> void { + logger->set_level(minlevel); + } + + /** + * Add the source (filename, line no) info to the logger. + * + * @param logger The logger to set the level of. + * + */ + template <typename TLogger> + inline auto AddSourceInfo(TLogger& logger) -> void { + logger->set_pattern("[%n:%^%-8l%$(%s:%!:%#)] %v"); + } + + /** + * Reset the logging pattern to the default. + * + * @param logger The logger to set the level of. + * + */ + template <typename TLogger> + inline auto ResetPattern(TLogger& logger) -> void { + logger->set_pattern(default_pattern); + } + +// define our macro-style loggers +#define C8LOG_TRACE SPDLOG_TRACE +#define C8LOG_DEBUG SPDLOG_DEBUG +#define C8LOG_INFO SPDLOG_INFO +#define C8LOG_WARN SPDLOG_WARN +#define C8LOG_ERROR SPDLOG_ERROR +#define C8LOG_CRITICAL SPDLOG_CRITICAL + +// and the specific logger versions +#define C8LOG_LOGGER_TRACE SPDLOG_LOGGER_TRACE +#define C8LOG_LOGGER_DEBUG SPDLOG_LOGGER_DEBUG +#define C8LOG_LOGGER_INFO SPDLOG_LOGGER_INFO +#define C8LOG_LOGGER_WARN SPDLOG_LOGGER_WARN +#define C8LOG_LOGGER_ERROR SPDLOG_LOGGER_ERROR +#define C8LOG_LOGGER_CRITICAL SPDLOG_LOGGER_CRITICAL + +} // namespace corsika::logging diff --git a/corsika/framework/random/RNGManager.hpp b/corsika/framework/random/RNGManager.hpp index 6e822466ecd3c2b4673d8c84089f09a30d44eddb..4778c179dd172ab13499ef5eded3db66ebec70f6 100644 --- a/corsika/framework/random/RNGManager.hpp +++ b/corsika/framework/random/RNGManager.hpp @@ -9,6 +9,7 @@ #pragma once #include <corsika/framework/utility/Singleton.hpp> +#include <corsika/framework/logging/Logging.h> #include <map> #include <random> #include <string> diff --git a/modules/conex b/modules/conex index c17fc68bd35612e1b610ce2b43e84e91577b8a86..c6b0301c0f8eecf530ce8498d47cb1b56e070d37 160000 --- a/modules/conex +++ b/modules/conex @@ -1 +1 @@ -Subproject commit c17fc68bd35612e1b610ce2b43e84e91577b8a86 +Subproject commit c6b0301c0f8eecf530ce8498d47cb1b56e070d37 diff --git a/tests/framework/testRandom.cpp b/tests/framework/testRandom.cpp index 7a894c9e0c3b0b4c7679fd007bbe76e32effefb4..d094582a69f191ba755f2ce2979901f07e8227b2 100644 --- a/tests/framework/testRandom.cpp +++ b/tests/framework/testRandom.cpp @@ -15,26 +15,41 @@ #include <iostream> #include <limits> #include <random> -#include <type_traits> + using namespace corsika; + SCENARIO("random-number streams can be registered and retrieved") { GIVEN("a RNGManager") { RNGManager& rngManager = RNGManager::GetInstance(); - WHEN("a sequence is registered by name") { - rngManager.RegisterRandomStream("stream_A"); + WHEN("the sequence name is not registered") { + REQUIRE(rngManager.IsRegistered("stream_A") == false); - THEN("the sequence can be retrieved") { - REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A")); - } + THEN("a sequence is registered by name") { + rngManager.RegisterRandomStream("stream_A"); - THEN("an unknown sequence cannot be retrieved") { - REQUIRE_THROWS(rngManager.GetRandomStream("stream_UNKNOWN")); - } + THEN("the sequence can be retrieved") { + REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A")); - // seeding not covered yet + THEN("we can check that the sequence exists") { + REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A")); + + THEN("an unknown sequence cannot be retrieved") { + REQUIRE(rngManager.IsRegistered("stream_A") == true); + + THEN("an unknown sequence cannot be retrieved") { + REQUIRE_THROWS(rngManager.GetRandomStream("stream_UNKNOWN")); + + THEN("an unknown sequence is not registered") { + REQUIRE(rngManager.IsRegistered("stream_UNKNOWN") == false); + } + } + } + } + } + } } } }