diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 104ebbb..704362e 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -358,7 +358,6 @@ }, { "type": "WET_DEPOSITION", - "gas phase": "gas", "aerosol phase": "cloud", "name": "rxn cloud", "scaling factor": 12.3 diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index 4894674..17be6e9 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -116,6 +116,12 @@ namespace open_atmos const std::string FirstOrderLoss_key = "FIRST_ORDER_LOSS"; // also scaling factor + // Wet Deposition + const std::string WetDeposition_key = "WET_DEPOSITION"; + // also + // scaling factor + // aerosol phase + // Henry's Law Phase Transfer const std::string HenrysLaw_key = "HL_PHASE_TRANSFER"; const std::string gas_phase_species = "gas-phase species"; @@ -219,6 +225,12 @@ namespace open_atmos const std::vector optional_keys{ keys.name, keys.scaling_factor }; } first_order_loss; + struct WetDeposition + { + const std::vector required_keys{ keys.aerosol_phase, keys.type }; + const std::vector optional_keys{ keys.name, keys.scaling_factor }; + } wet_deposition; + struct HenrysLaw { const std::vector required_keys{ keys.type, keys.gas_phase, keys.gas_phase_species, keys.aerosol_phase, keys.aerosol_phase_species, keys.aerosol_phase_water }; diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index 0dddff4..63bd640 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -182,7 +182,7 @@ namespace open_atmos struct Photolysis { /// @brief Scaling factor to apply to user-provided rate constants - double scaling_factor_{ 1.0 }; + double scaling_factor{ 1.0 }; /// @brief A list of reactants std::vector reactants; /// @brief A list of products @@ -216,7 +216,7 @@ namespace open_atmos struct Emission { /// @brief Scaling factor to apply to user-provided rate constants - double scaling_factor_{ 1.0 }; + double scaling_factor{ 1.0 }; /// @brief A list of products std::vector products; /// @brief An identifier, optional, uniqueness not enforced @@ -230,7 +230,7 @@ namespace open_atmos struct FirstOrderLoss { /// @brief Scaling factor to apply to user-provided rate constants - double scaling_factor_{ 1.0 }; + double scaling_factor{ 1.0 }; /// @brief A list of reactants std::vector reactants; /// @brief An identifier, optional, uniqueness not enforced @@ -241,6 +241,18 @@ namespace open_atmos std::unordered_map unknown_properties; }; + struct WetDeposition + { + /// @brief Scaling factor to apply to user-provided rate constants + double scaling_factor{ 1.0 }; + /// @brief An identifier, optional, uniqueness not enforced + std::string name; + /// @brief An identifier indicating which aerosol phase this reaction takes place in + std::string aerosol_phase; + /// @brief Unknown properties, prefixed with two underscores (__) + std::unordered_map unknown_properties; + }; + struct HenrysLaw { /// @brief An identifier, optional, uniqueness not enforced @@ -267,6 +279,7 @@ namespace open_atmos std::vector condensed_phase_photolysis; std::vector emission; std::vector first_order_loss; + std::vector wet_deposition; std::vector henrys_law; std::vector photolysis; std::vector surface; diff --git a/src/parser.cpp b/src/parser.cpp index 7eeec5e..d84a839 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1024,7 +1024,7 @@ namespace open_atmos if (object.contains(validation::keys.scaling_factor)) { - photolysis.scaling_factor_ = object[validation::keys.scaling_factor].get(); + photolysis.scaling_factor = object[validation::keys.scaling_factor].get(); } if (object.contains(validation::keys.name)) @@ -1213,7 +1213,7 @@ namespace open_atmos if (object.contains(validation::keys.scaling_factor)) { - emission.scaling_factor_ = object[validation::keys.scaling_factor].get(); + emission.scaling_factor = object[validation::keys.scaling_factor].get(); } if (object.contains(validation::keys.name)) @@ -1284,7 +1284,7 @@ namespace open_atmos if (object.contains(validation::keys.scaling_factor)) { - first_order_loss.scaling_factor_ = object[validation::keys.scaling_factor].get(); + first_order_loss.scaling_factor = object[validation::keys.scaling_factor].get(); } if (object.contains(validation::keys.name)) @@ -1332,6 +1332,55 @@ namespace open_atmos return { status, first_order_loss }; } + /// @brief Parses a wet deposition reaction + /// @param object A json object that should have information containing arrhenius parameters + /// @param existing_species A list of species configured in a mechanism + /// @param existing_phases A list of phases configured in a mechanism + /// @return A pair indicating parsing success and a struct of First Order Loss parameters + std::pair + ParseWetDeposition(const json& object, const std::vector existing_species, const std::vector existing_phases) + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::WetDeposition wet_deposition; + + status = ValidateSchema(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); + if (status == ConfigParseStatus::Success) + { + if (object.contains(validation::keys.scaling_factor)) + { + wet_deposition.scaling_factor = object[validation::keys.scaling_factor].get(); + } + + if (object.contains(validation::keys.name)) + { + wet_deposition.name = object[validation::keys.name].get(); + } + + auto comments = GetComments(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); + + std::unordered_map unknown_properties; + for (const auto& key : comments) + { + std::string val = object[key].dump(); + unknown_properties[key] = val; + } + + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + + // check if aerosol phase exists + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const auto& phase) { return phase.name == aerosol_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } + + wet_deposition.aerosol_phase = aerosol_phase; + wet_deposition.unknown_properties = unknown_properties; + } + + return { status, wet_deposition }; + } + /// @brief Parses a first order loss reaction /// @param object A json object that should have information containing arrhenius parameters /// @param existing_species A list of species configured in a mechanism @@ -1528,6 +1577,16 @@ namespace open_atmos } reactions.first_order_loss.push_back(first_order_loss_parse.second); } + else if (type == validation::keys.WetDeposition_key) + { + auto wet_deposition_parse = ParseWetDeposition(object, existing_species, existing_phases); + status = wet_deposition_parse.first; + if (status != ConfigParseStatus::Success) + { + break; + } + reactions.wet_deposition.push_back(wet_deposition_parse.second); + } else if (type == validation::keys.HenrysLaw_key) { auto henrys_law_parse = ParseHenrysLaw(object, existing_species, existing_phases); diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index e0984a7..f0177e6 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -19,6 +19,7 @@ create_standard_test(NAME parse_species SOURCES test_parse_species.cpp) create_standard_test(NAME parse_surface SOURCES test_parse_surface.cpp) create_standard_test(NAME parse_troe SOURCES test_parse_troe.cpp) create_standard_test(NAME parse_tunneling SOURCES test_parse_tunneling.cpp) +create_standard_test(NAME parse_wet_deposition SOURCES test_parse_wet_deposition.cpp) ################################################################################ # Copy test data diff --git a/test/unit/test_parse_emission.cpp b/test/unit/test_parse_emission.cpp index fbe6424..0ce87ab 100644 --- a/test/unit/test_parse_emission.cpp +++ b/test/unit/test_parse_emission.cpp @@ -14,7 +14,7 @@ TEST(JsonParser, CanParseValidEmissionReaction) EXPECT_EQ(mechanism.reactions.emission[0].gas_phase, "gas"); EXPECT_EQ(mechanism.reactions.emission[0].name, "my emission"); - EXPECT_EQ(mechanism.reactions.emission[0].scaling_factor_, 12.3); + EXPECT_EQ(mechanism.reactions.emission[0].scaling_factor, 12.3); EXPECT_EQ(mechanism.reactions.emission[0].products.size(), 1); EXPECT_EQ(mechanism.reactions.emission[0].products[0].species_name, "B"); EXPECT_EQ(mechanism.reactions.emission[0].products[0].coefficient, 1); @@ -22,7 +22,7 @@ TEST(JsonParser, CanParseValidEmissionReaction) EXPECT_EQ(mechanism.reactions.emission[0].unknown_properties["__comment"], "\"Dr. Pepper outranks any other soda\""); EXPECT_EQ(mechanism.reactions.emission[1].gas_phase, "gas"); - EXPECT_EQ(mechanism.reactions.emission[1].scaling_factor_, 1); + EXPECT_EQ(mechanism.reactions.emission[1].scaling_factor, 1); EXPECT_EQ(mechanism.reactions.emission[1].products.size(), 1); EXPECT_EQ(mechanism.reactions.emission[1].products[0].species_name, "B"); EXPECT_EQ(mechanism.reactions.emission[1].products[0].coefficient, 1); diff --git a/test/unit/test_parse_first_order_loss.cpp b/test/unit/test_parse_first_order_loss.cpp index 30de586..f6226f6 100644 --- a/test/unit/test_parse_first_order_loss.cpp +++ b/test/unit/test_parse_first_order_loss.cpp @@ -14,7 +14,7 @@ TEST(JsonParser, CanParseValidFirstOrderLossReaction) EXPECT_EQ(mechanism.reactions.first_order_loss[0].gas_phase, "gas"); EXPECT_EQ(mechanism.reactions.first_order_loss[0].name, "my first order loss"); - EXPECT_EQ(mechanism.reactions.first_order_loss[0].scaling_factor_, 12.3); + EXPECT_EQ(mechanism.reactions.first_order_loss[0].scaling_factor, 12.3); EXPECT_EQ(mechanism.reactions.first_order_loss[0].reactants.size(), 1); EXPECT_EQ(mechanism.reactions.first_order_loss[0].reactants[0].species_name, "C"); EXPECT_EQ(mechanism.reactions.first_order_loss[0].reactants[0].coefficient, 1); @@ -22,7 +22,7 @@ TEST(JsonParser, CanParseValidFirstOrderLossReaction) EXPECT_EQ(mechanism.reactions.first_order_loss[0].unknown_properties["__comment"], "\"Strawberries are the superior fruit\""); EXPECT_EQ(mechanism.reactions.first_order_loss[1].gas_phase, "gas"); - EXPECT_EQ(mechanism.reactions.first_order_loss[1].scaling_factor_, 1); + EXPECT_EQ(mechanism.reactions.first_order_loss[1].scaling_factor, 1); EXPECT_EQ(mechanism.reactions.first_order_loss[1].reactants.size(), 1); EXPECT_EQ(mechanism.reactions.first_order_loss[1].reactants[0].species_name, "C"); EXPECT_EQ(mechanism.reactions.first_order_loss[1].reactants[0].coefficient, 1); diff --git a/test/unit/test_parse_photolysis.cpp b/test/unit/test_parse_photolysis.cpp index eff3e9d..1089126 100644 --- a/test/unit/test_parse_photolysis.cpp +++ b/test/unit/test_parse_photolysis.cpp @@ -14,7 +14,7 @@ TEST(JsonParser, CanParseValidPhotolysisReaction) EXPECT_EQ(mechanism.reactions.photolysis[0].gas_phase, "gas"); EXPECT_EQ(mechanism.reactions.photolysis[0].name, "my photolysis"); - EXPECT_EQ(mechanism.reactions.photolysis[0].scaling_factor_, 12.3); + EXPECT_EQ(mechanism.reactions.photolysis[0].scaling_factor, 12.3); EXPECT_EQ(mechanism.reactions.photolysis[0].reactants.size(), 1); EXPECT_EQ(mechanism.reactions.photolysis[0].reactants[0].species_name, "B"); EXPECT_EQ(mechanism.reactions.photolysis[0].reactants[0].coefficient, 1); @@ -25,7 +25,7 @@ TEST(JsonParser, CanParseValidPhotolysisReaction) EXPECT_EQ(mechanism.reactions.photolysis[0].unknown_properties["__comment"], "\"hi\""); EXPECT_EQ(mechanism.reactions.photolysis[1].gas_phase, "gas"); - EXPECT_EQ(mechanism.reactions.photolysis[1].scaling_factor_, 1); + EXPECT_EQ(mechanism.reactions.photolysis[1].scaling_factor, 1); EXPECT_EQ(mechanism.reactions.photolysis[1].reactants.size(), 1); EXPECT_EQ(mechanism.reactions.photolysis[1].reactants[0].species_name, "B"); EXPECT_EQ(mechanism.reactions.photolysis[1].reactants[0].coefficient, 1.2); diff --git a/test/unit/test_parse_wet_deposition.cpp b/test/unit/test_parse_wet_deposition.cpp new file mode 100644 index 0000000..3b1bd1e --- /dev/null +++ b/test/unit/test_parse_wet_deposition.cpp @@ -0,0 +1,31 @@ +#include + +#include + +using namespace open_atmos::mechanism_configuration; + +TEST(JsonParser, CanParseValidWetDepositionReaction) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/wet_deposition/valid.json")); + EXPECT_EQ(status, ConfigParseStatus::Success); + + EXPECT_EQ(mechanism.reactions.wet_deposition.size(), 2); + + EXPECT_EQ(mechanism.reactions.wet_deposition[0].name, "rxn cloud"); + EXPECT_EQ(mechanism.reactions.wet_deposition[0].aerosol_phase, "cloud"); + EXPECT_EQ(mechanism.reactions.wet_deposition[0].scaling_factor, 12.3); + EXPECT_EQ(mechanism.reactions.wet_deposition[0].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.reactions.wet_deposition[0].unknown_properties["__comment"], "\"Tuxedo cats are the best\""); + + EXPECT_EQ(mechanism.reactions.wet_deposition[1].name, "rxn cloud2"); + EXPECT_EQ(mechanism.reactions.wet_deposition[1].aerosol_phase, "cloud"); + EXPECT_EQ(mechanism.reactions.wet_deposition[1].scaling_factor, 1); +} + +TEST(JsonParser, WetDepositionDetectsUnknownPhase) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/wet_deposition/missing_phase.json")); + EXPECT_EQ(status, ConfigParseStatus::UnknownPhase); +} \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/wet_deposition/missing_phase.json b/test/unit/unit_configs/reactions/wet_deposition/missing_phase.json new file mode 100644 index 0000000..bc25732 --- /dev/null +++ b/test/unit/unit_configs/reactions/wet_deposition/missing_phase.json @@ -0,0 +1,24 @@ +{ + "version": "1.0.0", + "name": "Missing phase", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + ], + "reactions": [ + { + "type": "WET_DEPOSITION", + "aerosol phase": "cloud", + "name": "rxn cloud" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/wet_deposition/valid.json b/test/unit/unit_configs/reactions/wet_deposition/valid.json new file mode 100644 index 0000000..0ad861c --- /dev/null +++ b/test/unit/unit_configs/reactions/wet_deposition/valid.json @@ -0,0 +1,39 @@ +{ + "version": "1.0.0", + "name": "Valid wet deposition", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + { + "name": "cloud", + "species": [ + "A", + "B", + "C" + ] + } + ], + "reactions": [ + { + "type": "WET_DEPOSITION", + "aerosol phase": "cloud", + "name": "rxn cloud", + "scaling factor": 12.3, + "__comment": "Tuxedo cats are the best" + }, + { + "type": "WET_DEPOSITION", + "aerosol phase": "cloud", + "name": "rxn cloud2" + } + ] +} \ No newline at end of file