Skip to content
Merged
5 changes: 3 additions & 2 deletions include/micm/process/chemical_reaction_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <micm/system/phase.hpp>
#include <micm/system/species.hpp>
#include <micm/system/yield.hpp>
#include <micm/util/utils.hpp>

#include <memory>
#include <utility>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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_});
}
};

Expand Down
53 changes: 30 additions & 23 deletions include/micm/system/system.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <micm/system/phase.hpp>
#include <micm/system/species.hpp>
#include <micm/util/utils.hpp>

#include <functional>
#include <string>
Expand All @@ -16,7 +17,7 @@ namespace micm
{
Phase gas_phase_{};
std::unordered_map<std::string, Phase> phases_{};
std::unordered_map<std::string, std::string> others_{};
std::vector<std::string> others_{};
};

/// @brief Represents the complete chemical state of a grid cell
Expand All @@ -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<std::string, Phase> 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<std::string, std::string> others_;
/// @brief Tracks non-phase elements (e.g., number concentrations) associated with a model
std::vector<std::string> others_;

/// @brief Default constructor
System() = default;
Expand All @@ -39,7 +39,7 @@ namespace micm
System(
const Phase& gas_phase,
const std::unordered_map<std::string, Phase>& phases,
const std::unordered_map<std::string, std::string>& others)
const std::vector<std::string>& others)
: gas_phase_(gas_phase),
phases_(phases),
others_(others)
Expand All @@ -58,7 +58,7 @@ namespace micm
System(
Phase&& gas_phase,
std::unordered_map<std::string, Phase>&& phases,
const std::unordered_map<std::string, std::string>& others)
std::vector<std::string>&& others)
: gas_phase_(std::move(gas_phase)),
phases_(std::move(phases)),
others_(std::move(others))
Expand Down Expand Up @@ -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;
}
Expand All @@ -127,22 +124,32 @@ namespace micm
inline std::vector<std::string> System::UniqueNames(
const std::function<std::string(const std::vector<std::string>& variables, const std::size_t i)> f) const
{
std::vector<std::string> 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<std::string> 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<std::string> 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;
}

Expand Down
25 changes: 25 additions & 0 deletions include/micm/util/utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2023-2025 University Corporation for Atmospheric Research
// SPDX-License-Identifier: Apache-2.0
#pragma once

#include <string>
#include <vector>

namespace micm
{
inline std::string JoinStrings(const std::vector<std::string>& 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
2 changes: 1 addition & 1 deletion test/unit/process/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
14 changes: 7 additions & 7 deletions test/unit/solver/test_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand All @@ -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 },
Expand Down
11 changes: 3 additions & 8 deletions test/unit/system/test_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
1 change: 1 addition & 0 deletions test/unit/util/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
70 changes: 70 additions & 0 deletions test/unit/util/test_utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (C) 2023-2025 University Corporation for Atmospheric Research
// SPDX-License-Identifier: Apache-2.0

#include <micm/util/utils.hpp>

#include <gtest/gtest.h>

TEST(JoinStrings, EmptyVector)
{
std::vector<std::string> names;
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "");
}

TEST(JoinStrings, SingleString)
{
std::vector<std::string> names = { "species" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "species");
}

TEST(JoinStrings, TwoStrings)
{
std::vector<std::string> names = { "accumulation", "aqueous" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.aqueous");
}

TEST(JoinStrings, ThreeStrings)
{
std::vector<std::string> names = { "accumulation", "aqueous", "CO2" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.aqueous.CO2");
}

TEST(JoinStrings, EmptyStringAtStart)
{
std::vector<std::string> names = { "", "accumulation", "aqueous" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.aqueous");
}

TEST(JoinStrings, EmptyStringInMiddle)
{
std::vector<std::string> names = { "accumulation", "", "aqueous" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.aqueous");
}

TEST(JoinStrings, EmptyStringAtEnd)
{
std::vector<std::string> names = { "accumulation", "aqueous", "" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.aqueous");
}

TEST(JoinStrings, MultipleEmptyStrings)
{
std::vector<std::string> names = { "", "accumulation", "", "aqueous", "" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.aqueous");
}

TEST(JoinStrings, StringsWithDots)
{
std::vector<std::string> names = { "accumulation.mode", "aqueous.phase" };
std::string result = micm::JoinStrings(names);
EXPECT_EQ(result, "accumulation.mode.aqueous.phase");
}

Loading