From c78d9bcdbe29c40bb85a6cd199cefa6de60e7033 Mon Sep 17 00:00:00 2001 From: fusiled Date: Mon, 27 Jul 2026 22:29:21 -0500 Subject: [PATCH] SimoSim: allow to dump parameters --- flake.lock | 6 +- include/Simo/core/Time.h | 16 ++++++ include/Simo/module/Module.h | 5 ++ include/Simo/parameter/Parameter.h | 16 ++++++ include/Simo/parameter/ParameterTrie.h | 29 +++++++++- src/SimoSim/Config.h | 2 + src/SimoSim/SimoSim.cc | 74 +++++++++++++++++++++++-- src/module/Module.cc | 2 +- tests/SimoSim/test_config.yaml | 1 + tests/core/TimePeriod/TimePeriodTest.cc | 22 ++++++++ tests/statistics/StatisticsTest.cc | 2 +- 11 files changed, 162 insertions(+), 13 deletions(-) diff --git a/flake.lock b/flake.lock index 8f0c823..1ed53f4 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1784432872, - "narHash": "sha256-n3gKTBIV4ZA5VQpUakffBe3KGu4+mhPoA34rrqS0GkA=", + "lastModified": 1785104993, + "narHash": "sha256-eKbrvPoAOFutbYMdbB3r5EQVmFxKv24iKqHPPUXA0gM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "fd1462031fdee08f65fd0b4c6b64e22239a77870", + "rev": "8623c4c20aa4ca2f5fb81510d2944066c3fb0d96", "type": "github" }, "original": { diff --git a/include/Simo/core/Time.h b/include/Simo/core/Time.h index d70e6ad..bc56ad9 100644 --- a/include/Simo/core/Time.h +++ b/include/Simo/core/Time.h @@ -161,6 +161,22 @@ struct from { } }; +template <> +struct from { + template + static void op(Time& value, is_context auto&& ctx, auto&& it, auto end) { + auto wrapper = custom_t{value, + [](Time& output, TimeValue input) { + output = Time{input.time, input.unit}; + }, + [](const Time& input) { + return TimeValue{.time = input.to_picoseconds(), + .unit = Time::Unit::PS}; + }}; + from::template op(wrapper, ctx, it, end); + } +}; + template struct to { template diff --git a/include/Simo/module/Module.h b/include/Simo/module/Module.h index cb5de6b..8f1eaad 100644 --- a/include/Simo/module/Module.h +++ b/include/Simo/module/Module.h @@ -78,6 +78,11 @@ class SIMO_PUBLIC Parameters { [[nodiscard]] std::optional get_subtree( const std::string& name) const; + template + void visit(Function f) { + trie.visit(f); + } + protected: Parameter::ParameterTrie trie; std::string name_; diff --git a/include/Simo/parameter/Parameter.h b/include/Simo/parameter/Parameter.h index d4790ab..b8db6d3 100644 --- a/include/Simo/parameter/Parameter.h +++ b/include/Simo/parameter/Parameter.h @@ -55,10 +55,16 @@ class Parameter { return self; } + /// Set stored value from glaze generic representation [[nodiscard]] virtual std::expected value_from_generic( const glz::generic_u64& glz_value) = 0; + /// Produce glaze generic representation of the value + [[nodiscard]] + virtual std::expected value_to_generic() + const = 0; + protected: bool has_value_ = false; }; @@ -101,6 +107,16 @@ class ParameterTyped : public Parameter { return this; } + std::expected value_to_generic() + const override { + if (!has_value()) { + return glz::generic_u64(nullptr); + } + glz::generic_u64 out; + out = value_; + return out; + } + [[nodiscard]] T value() const { return value_; } [[nodiscard]] bool validate() const override { diff --git a/include/Simo/parameter/ParameterTrie.h b/include/Simo/parameter/ParameterTrie.h index e3ce417..64ee6d9 100644 --- a/include/Simo/parameter/ParameterTrie.h +++ b/include/Simo/parameter/ParameterTrie.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "Parameter.h" @@ -106,7 +107,7 @@ class SIMO_PUBLIC ParameterTrie { template [[nodiscard]] bool all(Function f) const { - if (value != nullptr && value->has_value() && !f(*value)) { + if (value != nullptr && !f(*value)) { return false; } for (const auto& snd : children | std::views::values) { @@ -117,6 +118,30 @@ class SIMO_PUBLIC ParameterTrie { return true; } + /// Use function on all the elements of the trie + /// The function must accept a T* argument + template + void visit(Function f) const { + visit_impl("", f); + } + + template + void visit_impl(std::string_view name, Function f) const { + if (value != nullptr) { + if (!value->has_value()) { + f(name, nullptr); + } else { + f(name, value.get()); + } + } + for (const auto& [child_name, sub_tree] : children) { + std::string sub_tree_name = + (name.empty() ? "" : std::string(name) + PARAMETER_NODE_SEPARATOR) + + child_name; + sub_tree.visit_impl(sub_tree_name, f); + } + } + std::unique_ptr value; protected: @@ -140,4 +165,4 @@ class SIMO_PUBLIC ParameterTrie { }; } // namespace Simo::Parameter -#endif // SIMO_PARAMETERTRIE_HH \ No newline at end of file +#endif // SIMO_PARAMETERTRIE_HH diff --git a/src/SimoSim/Config.h b/src/SimoSim/Config.h index e14e941..7deff1d 100644 --- a/src/SimoSim/Config.h +++ b/src/SimoSim/Config.h @@ -18,6 +18,7 @@ #define SIMO_CONFIG_HH #include +#include #include #include #include @@ -47,6 +48,7 @@ namespace SimoSim::Config { struct SimulationInfo { Simo::Time time; + std::optional dump_parameters_path; }; struct Config { diff --git a/src/SimoSim/SimoSim.cc b/src/SimoSim/SimoSim.cc index 0fe5a49..e66297d 100644 --- a/src/SimoSim/SimoSim.cc +++ b/src/SimoSim/SimoSim.cc @@ -17,7 +17,9 @@ #include #include +#include #include +#include #include #include #include @@ -88,6 +90,60 @@ void print_system_ports( } } +struct ParamDumpPair { + std::string name; + glz::generic_u64 param; +}; + +void dump_parameters( + const std::unordered_map& module_map, + std::filesystem::path dump_path) { + std::ofstream out_file(dump_path); + if (!out_file.is_open()) { + std::cout << "Cannot open file " << dump_path << " to dump parameters\n"; + return; + } + std::vector vect; + for (const auto& [module_name, module_param_pair] : module_map) { + const auto& [module, params] = module_param_pair; + params->visit([&module, &vect](std::string_view name, + Simo::Parameter::Parameter* param) { + const auto param_wrapper = param->value_to_generic(); + if (!param_wrapper) { + std::cerr << param_wrapper.error() << "\n"; + return; + } + vect.emplace_back(std::string(module->name()) + "/" + name, + param_wrapper.value()); + }); + if (auto ec = glz::write_file_yaml(vect, dump_path.string())) { + std::cerr << "Error when dumping YAML parameters at " << dump_path + << " :\n"; + std::cerr << glz::format_error(ec) << "\n"; + return; + } + } +} + +std::expected read_config( + std::filesystem::path config_path) { + std::ifstream in_file(config_path); + if (!in_file.is_open()) { + return std::unexpected(std::format( + "Cannot open file {} to read configuration", config_path.c_str())); + } + std::stringstream buffer; + buffer << in_file.rdbuf(); + std::string config_str = buffer.str(); + SimoSim::Config::Config cfg; + if (auto ec = glz::read_yaml(cfg, config_str)) { + return std::unexpected( + std::format("Error during YAML config parsing of file {} :\n{}", + config_path.c_str(), glz::format_error(ec, config_str))); + } + return cfg; +} + int main(const int argc, char* argv[]) { std::filesystem::path config_path; std::vector collection_search_paths; @@ -102,7 +158,7 @@ int main(const int argc, char* argv[]) { ->check(CLI::ExistingFile); app.add_option( "--search-path", collection_search_paths, - "directory where to look for shared object containing collections") + "directory where to look for hared object containing collections") ->check(CLI::ExistingDirectory); app.add_flag("-v,--verbose", verbosity, "Increase verbosity level (e.g., -v, -vv, -vvv)"); @@ -153,13 +209,15 @@ int main(const int argc, char* argv[]) { } } - SimoSim::Config::Config cfg; - if (auto ec = glz::read_file_yaml(cfg, config_path.c_str())) { - std::cerr << "Error during YAML config parsing of file " << config_path - << " :\n"; - std::cerr << glz::format_error(ec) << "\n"; + auto expected_cfg = read_config(config_path); + if (!expected_cfg.has_value()) { + std::cerr << expected_cfg.error(); return INVALID_CONFIG_FILE; } + SimoSim::Config::Config cfg = expected_cfg.value(); + + const std::filesystem::path dump_parameters_path = + cfg.simulation.dump_parameters_path.value_or(""); std::vector> unrecognized_module_types; std::vector duplicate_module_names; @@ -221,6 +279,10 @@ int main(const int argc, char* argv[]) { return INITIALIZATION_FAILED; } + if (!dump_parameters_path.empty()) { + dump_parameters(module_map, dump_parameters_path); + } + if (print_ports) { print_system_ports(module_map); } diff --git a/src/module/Module.cc b/src/module/Module.cc index 905ed22..b3736f1 100644 --- a/src/module/Module.cc +++ b/src/module/Module.cc @@ -106,4 +106,4 @@ void Module::populate_default_log_levels() { logger.populate_default_log_levels(); } -} // namespace Simo \ No newline at end of file +} // namespace Simo diff --git a/tests/SimoSim/test_config.yaml b/tests/SimoSim/test_config.yaml index 668aa5b..bf7c842 100644 --- a/tests/SimoSim/test_config.yaml +++ b/tests/SimoSim/test_config.yaml @@ -34,3 +34,4 @@ simulation: time: time: 100 unit: NS + dump_parameters_path: "parameters_dump.yaml" diff --git a/tests/core/TimePeriod/TimePeriodTest.cc b/tests/core/TimePeriod/TimePeriodTest.cc index b43ac78..42089c1 100644 --- a/tests/core/TimePeriod/TimePeriodTest.cc +++ b/tests/core/TimePeriod/TimePeriodTest.cc @@ -14,13 +14,20 @@ #define BOOST_TEST_MODULE SimoTimePeriod #include +#include #include +#include #include #include "Simo/Simo.h" #include "support/BoostInclude.h" namespace Simo::Tests { +struct TimeWithFollowingMember { + Time time; + std::string label; +}; + BOOST_AUTO_TEST_CASE(TimeUnitConversions) { using Simo::Time; @@ -111,4 +118,19 @@ BOOST_AUTO_TEST_CASE(TimeJsonSerializationAndParsing) { BOOST_CHECK(invalid_error); BOOST_CHECK_EQUAL(unchanged.to_picoseconds(), 99U); } + +BOOST_AUTO_TEST_CASE(TimeYamlParsingPreservesParentMapping) { + TimeWithFollowingMember parsed; + constexpr std::string_view yaml = R"(time: + time: 7 + unit: NS +label: parsed +)"; + + const auto parse_error = glz::read_yaml(parsed, yaml); + + BOOST_CHECK(!parse_error); + BOOST_CHECK_EQUAL(parsed.time.to_picoseconds(), 7'000U); + BOOST_CHECK_EQUAL(parsed.label, "parsed"); +} } // namespace Simo::Tests diff --git a/tests/statistics/StatisticsTest.cc b/tests/statistics/StatisticsTest.cc index de0aab9..17fd570 100644 --- a/tests/statistics/StatisticsTest.cc +++ b/tests/statistics/StatisticsTest.cc @@ -401,7 +401,7 @@ BOOST_AUTO_TEST_CASE(CollectorParametersValidation) { using Simo::Modules::Core::Collector; Collector::Parameters params; - BOOST_CHECK_EQUAL(params.check(), true); + BOOST_CHECK_EQUAL(params.check(), false); params.get