diff --git a/examples/v1/cam_cloud_chemistry.json b/examples/v1/cam_cloud_chemistry.json index 0755db00..0281bfd9 100644 --- a/examples/v1/cam_cloud_chemistry.json +++ b/examples/v1/cam_cloud_chemistry.json @@ -48,6 +48,7 @@ "aerosol processes": [ { "__comment": "Henry's Law Equilibrium: SO2(g) <-> SO2(aq)", + "__note": "HLC = HLC_ref * exp(C*(1/T - 1/T0))", "type": "HENRY_LAW_EQUILIBRIUM", "gas phase": "gas", "gas-phase species": "SO2", @@ -62,6 +63,7 @@ }, { "__comment": "Henry's Law Equilibrium: H2O2(g) <-> H2O2(aq)", + "__note": "HLC = HLC_ref * exp(C*(1/T - 1/T0))", "type": "HENRY_LAW_EQUILIBRIUM", "gas phase": "gas", "gas-phase species": "H2O2", @@ -76,6 +78,7 @@ }, { "__comment": "Henry's Law Equilibrium: O3(g) <-> O3(aq)", + "__note": "HLC = HLC_ref * exp(C*(1/T - 1/T0))", "type": "HENRY_LAW_EQUILIBRIUM", "gas phase": "gas", "gas-phase species": "O3", @@ -103,10 +106,10 @@ { "name": "H2O", "coefficient": 1 } ], "forward rate constants": { - "CLOUD": { "type": "ARRHENIUS", "A": 3.184e8, "C": 4430.0 } + "type": "ARRHENIUS", "A": 3.184e8, "C": 4430.0 }, "equilibrium constant": { - "type": "ARRHENIUS_REFERENCE_TEMPERATURE", "A": 1725.0, "C [K]": 0.0, "T0 [K]": 298.15 + "type": "EQUILIBRIUM", "A": 1725.0, "C [K]": 0.0, "T0 [K]": 298.15 } }, { @@ -123,7 +126,7 @@ { "name": "SO4mm", "coefficient": 1 } ], "rate constants": { - "CLOUD": { "type": "ARRHENIUS", "A": 1.333e8, "C": 4430.0} + "type": "ARRHENIUS", "A": 1.333e8, "C": 4430.0 } }, { @@ -140,7 +143,7 @@ ], "algebraic species": "OHm", "equilibrium constant": { - "type": "ARRHENIUS_REFERENCE_TEMPERATURE", "A": 3.24e-18, "C [K]": 0.0, "T0 [K]": 298.15 + "type": "EQUILIBRIUM", "A": 3.24e-18, "C [K]": 0.0, "T0 [K]": 298.15 } }, { @@ -157,7 +160,7 @@ ], "algebraic species": "HSO3m", "equilibrium constant": { - "type": "ARRHENIUS_REFERENCE_TEMPERATURE", "A": 3.06e-4, "C [K]": 2090.0, "T0 [K]": 298.15 + "type": "EQUILIBRIUM", "A": 3.06e-4, "C [K]": 2090.0, "T0 [K]": 298.15 } }, { @@ -174,7 +177,7 @@ { "name": "SO3mm", "coefficient": 1 } ], "equilibrium constant": { - "type": "ARRHENIUS_REFERENCE_TEMPERATURE", "A": 1.08e-9, "C [K]": 1120.0, "T0 [K]": 298.15 + "type": "EQUILIBRIUM", "A": 1.08e-9, "C [K]": 1120.0, "T0 [K]": 298.15 } }, { diff --git a/include/mechanism_configuration/errors.hpp b/include/mechanism_configuration/errors.hpp index 23a91177..33d50759 100644 --- a/include/mechanism_configuration/errors.hpp +++ b/include/mechanism_configuration/errors.hpp @@ -41,7 +41,6 @@ namespace mechanism_configuration ReactionRequiresUnknownSpecies, UnknownSpecies, UnknownPhase, - UnknownAerosolRepresentation, RequestedSpeciesNotRegisteredInPhase, TooManyReactionComponents, InvalidVersion, diff --git a/include/mechanism_configuration/types/aerosol.hpp b/include/mechanism_configuration/types/aerosol.hpp index ee47f9e4..ea706623 100644 --- a/include/mechanism_configuration/types/aerosol.hpp +++ b/include/mechanism_configuration/types/aerosol.hpp @@ -7,7 +7,6 @@ #include #include -#include #include #include #include @@ -19,9 +18,9 @@ namespace mechanism_configuration::types // Rate constants // ---------------------------------------- - /// @brief Reference-temperature Arrhenius + /// @brief Equilibrium constant with reference-temperature Arrhenius form /// f(T) = A * exp( C * (1/T0 - 1/T) ) (C = +Ea/R, positive) - struct ArrheniusReferenceTemperature + struct Equilibrium { double A; ///< Value at the reference temperature T0 [units vary by use] double C = 0.0; ///< Temperature-dependence parameter [K] (C = +Ea/R) @@ -29,8 +28,7 @@ namespace mechanism_configuration::types }; /// @brief Henry's law constant: HLC(T) = HLC_ref * exp( C * (1/T - 1/T0) ) - /// Same as ArrheniusReferenceTemperature but with the - /// opposite temperature trend (solubility rises as T falls) + /// Same as Equilibrium but with the opposite temperature trend struct HenryLawConstant { double HLC_ref; ///< Reference HLC at T0 [mol m-3 Pa-1] @@ -39,7 +37,7 @@ namespace mechanism_configuration::types }; /// @brief A reaction rate constant parsed from config. - using RateConstant = std::variant>; + using RateConstant = std::variant>; // ---------------------------------------- // Representations @@ -80,8 +78,7 @@ namespace mechanism_configuration::types std::string solvent; std::vector reactants; std::vector products; - /// @brief Rate constant per aerosol representation; keys are representation names. - std::map rate_constants; + RateConstant rate_constants; std::optional solvent_floor_; std::optional min_halflife_; }; @@ -92,12 +89,11 @@ namespace mechanism_configuration::types std::string solvent; std::vector reactants; std::vector products; - /// @brief Per-representation forward / reverse rate constants; keys are representation names. - /// Supply exactly two of {forward, reverse, equilibrium} per representation; the third is derived. - std::map forward_rate_constants; - std::map reverse_rate_constants; + /// @brief Supply exactly two of {forward, reverse, equilibrium}; the third is derived. + std::optional forward_rate_constants; + std::optional reverse_rate_constants; /// @brief Shared, intrinsic equilibrium constant (NOT per representation). - std::optional equilibrium_constant; + std::optional equilibrium_constant; std::optional solvent_floor_; }; @@ -138,7 +134,7 @@ namespace mechanism_configuration::types std::string solvent; std::vector reactants; std::vector products; - ArrheniusReferenceTemperature equilibrium_constant; + Equilibrium equilibrium_constant; std::optional solvent_floor_; }; diff --git a/src/detail/v1/aerosol/keys.hpp b/src/detail/v1/aerosol/keys.hpp index 77c3d759..a298cda0 100644 --- a/src/detail/v1/aerosol/keys.hpp +++ b/src/detail/v1/aerosol/keys.hpp @@ -39,7 +39,7 @@ namespace mechanism_configuration::v1::keys inline constexpr std::string_view equilibrium_constant = "equilibrium constant"; inline constexpr std::string_view reference_temperature = "T0 [K]"; - inline constexpr std::string_view ArrheniusReferenceTemperature_key = "ARRHENIUS_REFERENCE_TEMPERATURE"; + inline constexpr std::string_view Equilibrium_key = "EQUILIBRIUM"; inline constexpr std::string_view henry_law_constant = "Henry's law constant"; inline constexpr std::string_view HLC_ref = "HLC_ref [mol m-3 Pa-1]"; diff --git a/src/detail/v1/aerosol/parsers.hpp b/src/detail/v1/aerosol/parsers.hpp index 98049e1d..dfa02e03 100644 --- a/src/detail/v1/aerosol/parsers.hpp +++ b/src/detail/v1/aerosol/parsers.hpp @@ -10,7 +10,6 @@ #include -#include #include #include @@ -21,19 +20,15 @@ namespace mechanism_configuration::v1 // ---------------------------------------- types::HenryLawConstant ParseHenryLawConstant(const YAML::Node& object); - types::ArrheniusReferenceTemperature ParseArrheniusReferenceTemperature(const YAML::Node& object); + types::Equilibrium ParseEquilibrium(const YAML::Node& object); /// @brief Parses a bare Arrhenius rate-constant block (A, B, C, D, E). types::Arrhenius ParseArrhenius(const YAML::Node& object); /// @brief Parses one rate-constant block into the RateConstant variant, dispatching on its - /// inner `type` (ARRHENIUS -> Arrhenius, ARRHENIUS_REFERENCE_TEMPERATURE -> Arrhenius reference temperature). + /// inner `type` (ARRHENIUS -> Arrhenius, EQUILIBRIUM -> Equilibrium constant). types::RateConstant ParseRateConstant(const YAML::Node& object); - /// @brief Parses a per-representation rate-constant map, e.g. { "CLOUD": { ... } }. - /// @return Map keyed by representation name to its rate constant. - std::map ParseRateConstantMap(const YAML::Node& object); - // ---------------------------------------- // Representation parsers // ---------------------------------------- diff --git a/src/errors.cpp b/src/errors.cpp index 98240cb5..f3788f29 100644 --- a/src/errors.cpp +++ b/src/errors.cpp @@ -25,7 +25,6 @@ namespace mechanism_configuration case ErrorCode::ReactionRequiresUnknownSpecies: return "ReactionRequiresUnknownSpecies"; case ErrorCode::UnknownSpecies: return "UnknownSpecies"; case ErrorCode::UnknownPhase: return "UnknownPhase"; - case ErrorCode::UnknownAerosolRepresentation: return "UnknownAerosolRepresentation"; case ErrorCode::RequestedSpeciesNotRegisteredInPhase: return "RequestedSpeciesNotRegisteredInPhase"; case ErrorCode::TooManyReactionComponents: return "TooManyReactionComponents"; case ErrorCode::InvalidVersion: return "InvalidVersion"; diff --git a/src/v1/aerosol/parsers.cpp b/src/v1/aerosol/parsers.cpp index acd0fb76..f00248fb 100644 --- a/src/v1/aerosol/parsers.cpp +++ b/src/v1/aerosol/parsers.cpp @@ -15,9 +15,9 @@ namespace mechanism_configuration::v1 // Rate constants parser // ---------------------------------------- - types::ArrheniusReferenceTemperature ParseArrheniusReferenceTemperature(const YAML::Node& object) + types::Equilibrium ParseEquilibrium(const YAML::Node& object) { - types::ArrheniusReferenceTemperature rate_constant; + types::Equilibrium rate_constant; rate_constant.A = object[keys::A].as(); if (object[keys::henry_law_C]) @@ -47,8 +47,8 @@ namespace mechanism_configuration::v1 types::RateConstant ParseRateConstant(const YAML::Node& object) { - if (object[keys::type] && object[keys::type].as() == keys::ArrheniusReferenceTemperature_key) - return ParseArrheniusReferenceTemperature(object); + if (object[keys::type] && object[keys::type].as() == keys::Equilibrium_key) + return ParseEquilibrium(object); // ARRHENIUS is the default when no (recognized) type is given. return ParseArrhenius(object); @@ -67,20 +67,6 @@ namespace mechanism_configuration::v1 return henry_law_constant; } - std::map ParseRateConstantMap(const YAML::Node& object) - { - std::map rate_constants; - - // Each key is an aerosol representation name (e.g. "CLOUD"); each value is a rate-constant - // block whose own `type` selects the RateConstant variant alternative. - for (const auto& entry : object) - { - rate_constants.emplace(entry.first.as(), ParseRateConstant(entry.second)); - } - - return rate_constants; - } - // ---------------------------------------- // Representation parsers // ---------------------------------------- @@ -162,7 +148,7 @@ namespace mechanism_configuration::v1 reaction.solvent = object[keys::solvent].as(); reaction.reactants = ParseReactionComponents(object, keys::reactants); reaction.products = ParseReactionComponents(object, keys::products); - reaction.rate_constants = ParseRateConstantMap(object[keys::rate_constants]); + reaction.rate_constants = ParseRateConstant(object[keys::rate_constants]); return reaction; } @@ -179,11 +165,11 @@ namespace mechanism_configuration::v1 // Any two of {forward, reverse, equilibrium} may be supplied; the third is derived // downstream, so each is parsed only when present. if (object[keys::forward_rate_constants]) - reaction.forward_rate_constants = ParseRateConstantMap(object[keys::forward_rate_constants]); + reaction.forward_rate_constants = ParseRateConstant(object[keys::forward_rate_constants]); if (object[keys::reverse_rate_constants]) - reaction.reverse_rate_constants = ParseRateConstantMap(object[keys::reverse_rate_constants]); + reaction.reverse_rate_constants = ParseRateConstant(object[keys::reverse_rate_constants]); if (object[keys::equilibrium_constant]) - reaction.equilibrium_constant = ParseArrheniusReferenceTemperature(object[keys::equilibrium_constant]); + reaction.equilibrium_constant = ParseEquilibrium(object[keys::equilibrium_constant]); return reaction; } @@ -225,7 +211,7 @@ namespace mechanism_configuration::v1 equilibrium.solvent = object[keys::solvent].as(); equilibrium.reactants = ParseReactionComponents(object, keys::reactants); equilibrium.products = ParseReactionComponents(object, keys::products); - equilibrium.equilibrium_constant = ParseArrheniusReferenceTemperature(object[keys::equilibrium_constant]); + equilibrium.equilibrium_constant = ParseEquilibrium(object[keys::equilibrium_constant]); return equilibrium; } diff --git a/src/v1/aerosol/schema.cpp b/src/v1/aerosol/schema.cpp index 82eff72e..30837cb5 100644 --- a/src/v1/aerosol/schema.cpp +++ b/src/v1/aerosol/schema.cpp @@ -21,7 +21,7 @@ namespace mechanism_configuration::v1 { namespace { - Errors CheckArrheniusReferenceTemperatureSchema(const YAML::Node& object) + Errors CheckEquilibriumSchema(const YAML::Node& object) { const std::vector required_keys = { keys::A, keys::henry_law_C }; const std::vector optional_keys = { keys::type, keys::reference_temperature }; @@ -37,8 +37,8 @@ namespace mechanism_configuration::v1 Errors CheckRateConstantSchema(const YAML::Node& object) { - if (object[keys::type] && object[keys::type].as() == keys::ArrheniusReferenceTemperature_key) - return CheckArrheniusReferenceTemperatureSchema(object); + if (object[keys::type] && object[keys::type].as() == keys::Equilibrium_key) + return CheckEquilibriumSchema(object); return CheckArrheniusSchema(object); } @@ -49,18 +49,6 @@ namespace mechanism_configuration::v1 return CheckSchema(object, required_keys, optional_keys); } - // A per-representation rate-constant map. Each value is a typed rate-constant block. - Errors CheckRateConstantMapSchema(const YAML::Node& object) - { - Errors errors; - for (const auto& entry : object) - { - auto entry_errors = CheckRateConstantSchema(entry.second); - errors.insert(errors.end(), entry_errors.begin(), entry_errors.end()); - } - return errors; - } - // Each linear-constraint term references a species in a phase with a coefficient. Errors CheckLinearConstraintTermsSchema(const YAML::Node& object) { @@ -178,7 +166,7 @@ namespace mechanism_configuration::v1 } if (object[keys::rate_constants]) { - auto e = CheckRateConstantMapSchema(object[keys::rate_constants]); + auto e = CheckRateConstantSchema(object[keys::rate_constants]); nested_errors.insert(nested_errors.end(), e.begin(), e.end()); } } @@ -198,17 +186,17 @@ namespace mechanism_configuration::v1 } if (object[keys::forward_rate_constants]) { - auto e = CheckRateConstantMapSchema(object[keys::forward_rate_constants]); + auto e = CheckRateConstantSchema(object[keys::forward_rate_constants]); nested_errors.insert(nested_errors.end(), e.begin(), e.end()); } if (object[keys::reverse_rate_constants]) { - auto e = CheckRateConstantMapSchema(object[keys::reverse_rate_constants]); + auto e = CheckRateConstantSchema(object[keys::reverse_rate_constants]); nested_errors.insert(nested_errors.end(), e.begin(), e.end()); } if (object[keys::equilibrium_constant]) { - auto e = CheckArrheniusReferenceTemperatureSchema(object[keys::equilibrium_constant]); + auto e = CheckEquilibriumSchema(object[keys::equilibrium_constant]); nested_errors.insert(nested_errors.end(), e.begin(), e.end()); } } @@ -247,7 +235,7 @@ namespace mechanism_configuration::v1 } if (object[keys::equilibrium_constant]) { - auto e = CheckArrheniusReferenceTemperatureSchema(object[keys::equilibrium_constant]); + auto e = CheckEquilibriumSchema(object[keys::equilibrium_constant]); nested_errors.insert(nested_errors.end(), e.begin(), e.end()); } } diff --git a/src/validate.cpp b/src/validate.cpp index a44bb51f..18793324 100644 --- a/src/validate.cpp +++ b/src/validate.cpp @@ -302,11 +302,6 @@ namespace mechanism_configuration registered.emplace(ps.name, &ps); } - // Rate-constants are keyed by its aerosol representation - std::unordered_set representation_names; - for (const auto& representation : aerosol.representations) - std::visit([&](const auto& rep) { representation_names.insert(rep.name); }, representation); - // Verifies that species is registered in phase. Returns the entry (or nullptr) and reports. auto require_registered_species = [&](const std::string& phase, const std::string& species, const std::string& context) -> const types::PhaseSpecies* @@ -379,17 +374,6 @@ namespace mechanism_configuration Message(std::nullopt, mc_fmt::format("Unknown phase '{}' referenced by {}.", phase, context)) }); }; - // Validates that each rate-constant map entry is keyed by a declared aerosol representation. - auto require_representation = [&](const std::string& representation, const std::string& context) - { - if (!representation_names.contains(representation)) - errors.push_back( - { ErrorCode::UnknownAerosolRepresentation, - Message( - std::nullopt, - mc_fmt::format("Unknown aerosol representation '{}' referenced by {}.", representation, context)) }); - }; - for (const auto& representation : aerosol.representations) { std::visit( @@ -410,8 +394,6 @@ namespace mechanism_configuration for (const auto& c : p->products) require_registered_species(p->phase, c.name, "DISSOLVED_REACTION product"); require_registered_species(p->phase, p->solvent, "DISSOLVED_REACTION solvent"); - for (const auto& entry : p->rate_constants) - require_representation(entry.first, "DISSOLVED_REACTION rate constant"); } else if (const auto* p = std::get_if(&process)) { @@ -420,10 +402,6 @@ namespace mechanism_configuration for (const auto& c : p->products) require_registered_species(p->phase, c.name, "DISSOLVED_REVERSIBLE_REACTION product"); require_registered_species(p->phase, p->solvent, "DISSOLVED_REVERSIBLE_REACTION solvent"); - for (const auto& entry : p->forward_rate_constants) - require_representation(entry.first, "DISSOLVED_REVERSIBLE_REACTION forward rate constant"); - for (const auto& entry : p->reverse_rate_constants) - require_representation(entry.first, "DISSOLVED_REVERSIBLE_REACTION reverse rate constant"); } else if (const auto* p = std::get_if(&process)) { diff --git a/test/unit/test_validate.cpp b/test/unit/test_validate.cpp index 5cea810a..aae042d9 100644 --- a/test/unit/test_validate.cpp +++ b/test/unit/test_validate.cpp @@ -246,7 +246,7 @@ TEST(ValidateAerosol, AcceptsValidProcessesAndConstraints) reaction.solvent = "H2O"; reaction.reactants = { component("A") }; reaction.products = { component("A") }; - reaction.rate_constants = { { "cloud", types::ArrheniusReferenceTemperature{} } }; // keyed by a declared representation + reaction.rate_constants = types::Equilibrium{}; m.aerosol->processes.push_back(reaction); m.aerosol->constraints = { ValidEquilibrium() }; @@ -279,20 +279,6 @@ TEST(ValidateAerosol, DetectsSpeciesNotRegisteredInCondensedPhase) EXPECT_TRUE(HasCode(ValidateAerosolModel(m), ErrorCode::RequestedSpeciesNotRegisteredInPhase)); } -TEST(ValidateAerosol, DetectsRateConstantKeyedByUnknownRepresentation) -{ - Mechanism m = AerosolBaseMechanism(); - types::DissolvedReaction reaction; - reaction.phase = "aqueous"; - reaction.solvent = "H2O"; - reaction.reactants = { component("A") }; - reaction.products = { component("A") }; - reaction.rate_constants = { { "not_a_representation", types::ArrheniusReferenceTemperature{} } }; - m.aerosol->processes = { reaction }; - - EXPECT_TRUE(HasCode(ValidateAerosolModel(m), ErrorCode::UnknownAerosolRepresentation)); -} - TEST(ValidateAerosol, DetectsMissingGasDiffusionCoefficient) { Mechanism m = AerosolBaseMechanism(); diff --git a/test/unit/v1/v1_unit_configs/aerosol/valid_aerosol.json b/test/unit/v1/v1_unit_configs/aerosol/valid_aerosol.json index a1a40566..abd8cd03 100644 --- a/test/unit/v1/v1_unit_configs/aerosol/valid_aerosol.json +++ b/test/unit/v1/v1_unit_configs/aerosol/valid_aerosol.json @@ -55,9 +55,7 @@ "solvent": "H2O", "reactants": [ { "name": "A", "coefficient": 1 } ], "products": [ { "name": "B", "coefficient": 1 } ], - "rate constants": { - "aitken": { "type": "ARRHENIUS", "A": 1.0e3, "C": 100.0 }, - "accumulation": { "type": "ARRHENIUS", "A": 1.5e3, "C": 120.0 } } + "rate constants": { "type": "ARRHENIUS", "A": 1.0e3, "C": 100.0 } }, { "type": "HENRY_LAW_EQUILIBRIUM",