Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 99 additions & 15 deletions include/micm/process/chemical_reaction_builder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,84 @@ namespace micm

class ChemicalReactionBuilder
{
private:
std::vector<Species> reactants_;
std::vector<Yield> products_;
std::unique_ptr<RateConstant> rate_constant_;
Phase phase_;

public:
/// @brief Sets the list of reactant species involved in the chemical reaction
/// @param reactants A vector of Species objects representing the reactants
/// @brief Enables aerosol scoping for reactant and product species
/// This function must be called before setting reactants or products
/// in order for scoping to be applied.
/// Cannot be used together with SetPhase. They are mutually exclusive.
/// @param scope Aerosol scope prefix to apply to species names
/// @param phase Phase object representing the reaction phase
/// @return Reference to the builder
ChemicalReactionBuilder& SetReactants(std::vector<Species> reactants)
/// @throws std::system_error if SetPhase, SetReactants, or SetProducts has already been called
ChemicalReactionBuilder& SetAerosolScope(const std::string& scope, const Phase& phase)
{
reactants_ = std::move(reactants);
if (has_phase_)
throw std::system_error(
make_error_code(MicmProcessErrc::InvalidConfiguration),
"SetPhase and SetAerosolScope are mutually exclusive and should not be used together.");
if (has_reactants_)
throw std::system_error(
make_error_code(MicmProcessErrc::InvalidConfiguration),
"SetAerosolScope must be called before SetReactants.");
if (has_products_)
throw std::system_error(
make_error_code(MicmProcessErrc::InvalidConfiguration),
"SetAerosolScope must be called before SetProducts.");

scope_ = scope;
phase_ = phase;
has_scope_ = true;

return *this;
}

/// @brief Sets the list of product species and their yields for the chemical reaction
/// @param products A vector of Yield objects representing the products
/// @brief Sets the list of reactant species involved in the chemical reaction.
/// When scoping is enabled, each reactant name is prefixed with the preset scope.
/// @param reactants A list of Species objects representing the reactants
/// @return Reference to the builder
ChemicalReactionBuilder& SetProducts(std::vector<Yield> products)
ChemicalReactionBuilder& SetReactants(const std::vector<Species>& reactants)
{
products_ = std::move(products);
if (has_scope_)
{
reactants_.reserve(reactants.size());
for (const auto& species : reactants)
{
reactants_.push_back(species);
Scope(reactants_.back(), phase_);
}
}
else
{
reactants_ = reactants;
}

has_reactants_ = true;

return *this;
}

/// @brief Sets the list of product species and their yields for the chemical reaction.
/// When scoping is enabled, each product name is prefixed with the preset scope.
/// @param products A list of Yield objects representing the products
/// @return Reference to the builder
ChemicalReactionBuilder& SetProducts(const std::vector<Yield>& products)
{
if (has_scope_)
{
products_.reserve(products.size());
for (const auto& [species, coefficient] : products)
{
products_.emplace_back(species, coefficient);
Scope(products_.back().species_, phase_);
}
}
else
{
products_ = products;
}

has_products_ = true;

return *this;
}

Expand All @@ -57,11 +113,19 @@ namespace micm
}

/// @brief Sets the phase in which the chemical reaction occurs (e.g., gas, aqueous)
/// Cannot be used together with SetAerosolScope. They are mutually exclusive.
/// @param phase Phase object representing the reaction phase
/// @return Reference to the builder
/// @throws std::system_error if SetAerosolScope has already been called
ChemicalReactionBuilder& SetPhase(const Phase& phase)
{
if (has_scope_)
throw std::system_error(
make_error_code(MicmProcessErrc::InvalidConfiguration),
"SetPhase and SetAerosolScope are mutually exclusive and should not be used togethe.");

phase_ = phase;
has_phase_ = true;
return *this;
}

Expand All @@ -73,11 +137,31 @@ namespace micm
{
if (!rate_constant_)
throw std::system_error(
make_error_code(MicmProcessErrc::RateConstantIsNotSet), "Rate Constant pointer cannot be null");
make_error_code(MicmProcessErrc::RateConstantIsNotSet), "Rate Constant pointer cannot be null.");

ChemicalReaction reaction(std::move(reactants_), std::move(products_), std::move(rate_constant_), phase_);
return Process(std::move(reaction));
}

private:
std::vector<Species> reactants_;
std::vector<Yield> products_;
std::unique_ptr<RateConstant> rate_constant_;
Phase phase_;
std::string scope_;

bool has_scope_ = false;
bool has_phase_ = false;
bool has_reactants_ = false;
bool has_products_ = false;

/// @brief Applies an aerosol phase-specific scope to a species by prefixing its name
/// @param species Species object whose name will be modified
/// @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_;
}
};

} // namespace micm
6 changes: 4 additions & 2 deletions include/micm/process/process_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ enum class MicmProcessErrc
ProductDoesNotExist = MICM_PROCESS_ERROR_CODE_PRODUCT_DOES_NOT_EXIST,
RateConstantIsNotSet = MICM_PROCESS_ERROR_CODE_RATE_CONSTANT_IS_NOT_SET,
TransferCoefficientIsNotSet = MICM_PROCESS_ERROR_CODE_TRANSFER_COEFFICIENT_IS_NOT_SET,
InvalidConfiguration = MICM_PROCESS_ERROR_CODE_INVALID_CONFIGURATION,
};

namespace std
Expand All @@ -35,10 +36,11 @@ class MicmProcessErrorCategory : public std::error_category
{
switch (static_cast<MicmProcessErrc>(ev))
{
case MicmProcessErrc::RateConstantIsNotSet: return "Rate constant is not set";
case MicmProcessErrc::TransferCoefficientIsNotSet: return "Transfer coefficient is not set";
case MicmProcessErrc::ReactantDoesNotExist: return "Reactant does not exist";
case MicmProcessErrc::ProductDoesNotExist: return "Product does not exist";
case MicmProcessErrc::RateConstantIsNotSet: return "Rate constant is not set";
case MicmProcessErrc::TransferCoefficientIsNotSet: return "Transfer coefficient is not set";
case MicmProcessErrc::InvalidConfiguration: return "Configuration is not valid";
default: return "Unknown error";
}
}
Expand Down
1 change: 1 addition & 0 deletions include/micm/util/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#define MICM_PROCESS_ERROR_CODE_PRODUCT_DOES_NOT_EXIST 2
#define MICM_PROCESS_ERROR_CODE_RATE_CONSTANT_IS_NOT_SET 3
#define MICM_PROCESS_ERROR_CODE_TRANSFER_COEFFICIENT_IS_NOT_SET 4
#define MICM_PROCESS_ERROR_CODE_INVALID_CONFIGURATION 5

#define MICM_ERROR_CATEGORY_RATE_CONSTANT "MICM Rate Constant"
#define MICM_RATE_CONSTANT_ERROR_CODE_MISSING_ARGUMENTS_FOR_SURFACE_RATE_CONSTANT 1
Expand Down
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 SOURCES test_process.cpp)
create_standard_test(NAME process_set SOURCES test_process_set.cpp)

Expand Down
Loading
Loading