diff --git a/include/micm/process/chemical_reaction_builder.hpp b/include/micm/process/chemical_reaction_builder.hpp index 2d270fdeb..dfe3af0fa 100644 --- a/include/micm/process/chemical_reaction_builder.hpp +++ b/include/micm/process/chemical_reaction_builder.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -122,7 +123,7 @@ namespace micm if (has_scope_) throw std::system_error( make_error_code(MicmProcessErrc::InvalidConfiguration), - "SetPhase and SetAerosolScope are mutually exclusive and should not be used togethe."); + "SetPhase and SetAerosolScope are mutually exclusive and should not be used together."); phase_ = phase; has_phase_ = true; @@ -160,7 +161,7 @@ namespace micm /// @param phase Phase whose name is used in the scope prefix void Scope(Species& species, const Phase& phase) { - species.name_ = scope_ + "." + phase.name_ + "." + species.name_; + species.name_ = JoinStrings({scope_, phase.name_, species.name_}); } }; diff --git a/include/micm/system/system.hpp b/include/micm/system/system.hpp index 28caf3a54..04dab22d6 100644 --- a/include/micm/system/system.hpp +++ b/include/micm/system/system.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -16,7 +17,7 @@ namespace micm { Phase gas_phase_{}; std::unordered_map phases_{}; - std::unordered_map others_{}; + std::vector others_{}; }; /// @brief Represents the complete chemical state of a grid cell @@ -28,9 +29,8 @@ namespace micm Phase gas_phase_; /// @brief Additional phases (e.g., aqueous, aerosol), mapped by name and representing non-gas phase std::unordered_map phases_; - /// @brief Tracks non-phase elements (e.g., number concentrations) associated with a model. - /// Elements are mapped using a prefix specific to the model's name and representation. - std::unordered_map others_; + /// @brief Tracks non-phase elements (e.g., number concentrations) associated with a model + std::vector others_; /// @brief Default constructor System() = default; @@ -39,7 +39,7 @@ namespace micm System( const Phase& gas_phase, const std::unordered_map& phases, - const std::unordered_map& others) + const std::vector& others) : gas_phase_(gas_phase), phases_(phases), others_(others) @@ -58,7 +58,7 @@ namespace micm System( Phase&& gas_phase, std::unordered_map&& phases, - const std::unordered_map& others) + std::vector&& others) : gas_phase_(std::move(gas_phase)), phases_(std::move(phases)), others_(std::move(others)) @@ -109,12 +109,9 @@ namespace micm inline size_t System::StateSize() const { - size_t state_size = gas_phase_.StateSize(); - for (const auto& phase : phases_) - { - state_size += phase.second.StateSize(); - } - state_size += others_.size(); + std::size_t state_size = gas_phase_.StateSize() + others_.size(); + for (const auto& [key, phase] : phases_) + state_size += phase.StateSize(); return state_size; } @@ -127,22 +124,32 @@ namespace micm inline std::vector System::UniqueNames( const std::function& variables, const std::size_t i)> f) const { - std::vector names = gas_phase_.UniqueNames(); - for (const auto& phase : phases_) - { - for (const auto& species_name : phase.second.UniqueNames()) - names.push_back(phase.first + "." + species_name); - } - for (const auto& other : others_) + std::vector names; + names.reserve(StateSize()); + + auto gas_names = gas_phase_.UniqueNames(); + names.insert(names.end(), + std::make_move_iterator(gas_names.begin()), + std::make_move_iterator(gas_names.end())); + + for (const auto& [key, phase] : phases_) { - names.push_back(other.first + "." + other.second); + auto phase_names = phase.UniqueNames(); + for (auto& species_name : phase_names) + names.push_back(JoinStrings({key, std::move(species_name)})); } + + names.insert(names.end(), others_.begin(), others_.end()); + if (f) { - const auto orig_names = names; - for (std::size_t i = 0; i < orig_names.size(); ++i) - names[i] = f(orig_names, i); + std::vector reordered; + reordered.reserve(names.size()); + for (std::size_t i = 0; i < names.size(); ++i) + reordered.push_back(f(names, i)); + return reordered; } + return names; } diff --git a/include/micm/util/utils.hpp b/include/micm/util/utils.hpp new file mode 100644 index 000000000..bdef7bc65 --- /dev/null +++ b/include/micm/util/utils.hpp @@ -0,0 +1,25 @@ +// Copyright (C) 2023-2025 University Corporation for Atmospheric Research +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +namespace micm +{ + inline std::string JoinStrings(const std::vector& names) + { + std::string result; + for (size_t i = 0; i < names.size(); ++i) + { + if (!names[i].empty()) + { + if (!result.empty()) + result += "."; + result += names[i]; + } + } + return result; + } + +} // namespace micm \ No newline at end of file diff --git a/test/unit/process/CMakeLists.txt b/test/unit/process/CMakeLists.txt index 4d096e98f..ae481890f 100644 --- a/test/unit/process/CMakeLists.txt +++ b/test/unit/process/CMakeLists.txt @@ -1,6 +1,6 @@ ################################################################################ # Tests -create_standard_test(NAME aerosol_scope SOURCES test_process_configuration.cpp) +create_standard_test(NAME process_configuration SOURCES test_process_configuration.cpp) create_standard_test(NAME process SOURCES test_process.cpp) create_standard_test(NAME process_set SOURCES test_process_set.cpp) diff --git a/test/unit/solver/test_state.cpp b/test/unit/solver/test_state.cpp index e8ffac483..c1808fd6c 100644 --- a/test/unit/solver/test_state.cpp +++ b/test/unit/solver/test_state.cpp @@ -283,13 +283,13 @@ TEST(State, SetSingleConcentration) TEST(State, SetConcentrationByElementSingleValue) { - micm::SystemParameters params; - params.others_["modal.aitken"] = "number_concentration"; - params.others_["modal.accumulation"] = "number_concentration"; - std::string aitken_num_conc = "modal.aitken.number_concentration"; std::string accum_num_conc = "modal.accumulation.number_concentration"; + micm::SystemParameters params; + params.others_.push_back(aitken_num_conc); + params.others_.push_back(accum_num_conc); + micm::State state{ micm::StateParameters{ .number_of_rate_constants_ = 10, .variable_names_{ "foo", "bar", "baz", "quz", aitken_num_conc, accum_num_conc }, @@ -306,11 +306,11 @@ TEST(State, SetConcentrationByElementSingleValue) TEST(State, SetConcentrationByElementVector) { - micm::SystemParameters params; - params.others_["modal.aitken"] = "number_concentration"; - std::string aitken_num_conc = "modal.aitken.number_concentration"; + micm::SystemParameters params; + params.others_.push_back(aitken_num_conc); + micm::State state{ micm::StateParameters{ .number_of_rate_constants_ = 10, .variable_names_{ "foo", "bar", "baz", "quz", aitken_num_conc }, diff --git a/test/unit/system/test_system.cpp b/test/unit/system/test_system.cpp index 3ea876644..d6837d27b 100644 --- a/test/unit/system/test_system.cpp +++ b/test/unit/system/test_system.cpp @@ -101,16 +101,11 @@ TEST(System, ConstructorWithParameterizedSpecies) TEST(System, OthersIsStoredAndAccessible) { SystemParameters params; - params.others_["modal.aitken"] = "number_concentration"; - params.others_["modal.accumulation"] = "number_concentration"; - + params.others_.emplace_back("modal.aitken.number_concentration"); + params.others_.emplace_back("modal.accumulation.number_concentration"); System sys(params); - - ASSERT_EQ(sys.others_.size(), 2); - EXPECT_EQ(sys.others_.at("modal.aitken"), "number_concentration"); - EXPECT_EQ(sys.others_.at("modal.accumulation"), "number_concentration"); - auto names = sys.UniqueNames(); + EXPECT_NE(std::find(names.begin(), names.end(), "modal.aitken.number_concentration"), names.end()); EXPECT_NE(std::find(names.begin(), names.end(), "modal.accumulation.number_concentration"), names.end()); } \ No newline at end of file diff --git a/test/unit/util/CMakeLists.txt b/test/unit/util/CMakeLists.txt index 3b641a830..89a8f1600 100644 --- a/test/unit/util/CMakeLists.txt +++ b/test/unit/util/CMakeLists.txt @@ -6,4 +6,5 @@ create_standard_test(NAME sparse_matrix_standard_compressed_column_ordering SOUR create_standard_test(NAME sparse_matrix_standard_compressed_row_ordering SOURCES test_sparse_matrix_standard_ordering_row.cpp) create_standard_test(NAME sparse_matrix_vector_compressed_column_ordering SOURCES test_sparse_matrix_vector_ordering_column.cpp) create_standard_test(NAME sparse_matrix_vector_compressed_row_ordering SOURCES test_sparse_matrix_vector_ordering_row.cpp) +create_standard_test(NAME utils SOURCES test_utils.cpp) create_standard_test(NAME vector_matrix SOURCES test_vector_matrix.cpp) \ No newline at end of file diff --git a/test/unit/util/test_utils.cpp b/test/unit/util/test_utils.cpp new file mode 100644 index 000000000..27333705b --- /dev/null +++ b/test/unit/util/test_utils.cpp @@ -0,0 +1,70 @@ +// Copyright (C) 2023-2025 University Corporation for Atmospheric Research +// SPDX-License-Identifier: Apache-2.0 + +#include + +#include + +TEST(JoinStrings, EmptyVector) +{ + std::vector names; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, ""); +} + +TEST(JoinStrings, SingleString) +{ + std::vector names = { "species" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "species"); +} + +TEST(JoinStrings, TwoStrings) +{ + std::vector names = { "accumulation", "aqueous" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.aqueous"); +} + +TEST(JoinStrings, ThreeStrings) +{ + std::vector names = { "accumulation", "aqueous", "CO2" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.aqueous.CO2"); +} + +TEST(JoinStrings, EmptyStringAtStart) +{ + std::vector names = { "", "accumulation", "aqueous" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.aqueous"); +} + +TEST(JoinStrings, EmptyStringInMiddle) +{ + std::vector names = { "accumulation", "", "aqueous" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.aqueous"); +} + +TEST(JoinStrings, EmptyStringAtEnd) +{ + std::vector names = { "accumulation", "aqueous", "" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.aqueous"); +} + +TEST(JoinStrings, MultipleEmptyStrings) +{ + std::vector names = { "", "accumulation", "", "aqueous", "" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.aqueous"); +} + +TEST(JoinStrings, StringsWithDots) +{ + std::vector names = { "accumulation.mode", "aqueous.phase" }; + std::string result = micm::JoinStrings(names); + EXPECT_EQ(result, "accumulation.mode.aqueous.phase"); +} +