Skip to content

Commit

Permalink
Merge pull request #35 from open-atmos/parse_henrys_law
Browse files Browse the repository at this point in the history
parsing henrys law
  • Loading branch information
K20shores authored Jan 23, 2024
2 parents 7aa9b49 + b44e27e commit fe81f91
Show file tree
Hide file tree
Showing 12 changed files with 457 additions and 3 deletions.
15 changes: 15 additions & 0 deletions include/open_atmos/mechanism_configuration/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ namespace open_atmos
const std::string FirstOrderLoss_key = "FIRST_ORDER_LOSS";
// also scaling factor

// Henry's Law Phase Transfer
const std::string HenrysLaw_key = "HL_PHASE_TRANSFER";
const std::string gas_phase_species = "gas-phase species";
const std::string aerosol_phase_species = "aerosol-phase species";
// also
// gas phase
// aerosol phase
// aerosol-phase water

} keys;

struct Configuration
Expand Down Expand Up @@ -210,6 +219,12 @@ namespace open_atmos
const std::vector<std::string> optional_keys{ keys.name, keys.scaling_factor };
} first_order_loss;

struct HenrysLaw
{
const std::vector<std::string> required_keys{ keys.type, keys.gas_phase, keys.gas_phase_species, keys.aerosol_phase, keys.aerosol_phase_species, keys.aerosol_phase_water };
const std::vector<std::string> optional_keys{ keys.name };
} henrys_law;

struct Mechanism
{
const std::vector<std::string> required_keys{};
Expand Down
19 changes: 19 additions & 0 deletions include/open_atmos/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,24 @@ namespace open_atmos
std::unordered_map<std::string, std::string> unknown_properties;
};

struct HenrysLaw
{
/// @brief An identifier, optional, uniqueness not enforced
std::string name;
/// @brief An identifier indicating which gas phase this reaction takes place in
std::string gas_phase;
/// @brief An identifier indicating which gas phase species this reaction involves
std::string gas_phase_species;
/// @brief An identifier indicating which aerosol phase this reaction takes place in
std::string aerosol_phase;
/// @brief An identifier indicating the species label of aqueous phase water
std::string aerosol_phase_water;
/// @brief An identifier indicating which aerosol phase species this reaction involves
std::string aerosol_phase_species;
/// @brief Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

struct Reactions
{
std::vector<types::Arrhenius> arrhenius;
Expand All @@ -249,6 +267,7 @@ namespace open_atmos
std::vector<types::CondensedPhasePhotolysis> condensed_phase_photolysis;
std::vector<types::Emission> emission;
std::vector<types::FirstOrderLoss> first_order_loss;
std::vector<types::HenrysLaw> henrys_law;
std::vector<types::Photolysis> photolysis;
std::vector<types::Surface> surface;
std::vector<types::Troe> troe;
Expand Down
92 changes: 92 additions & 0 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1332,6 +1332,88 @@ namespace open_atmos
return { status, first_order_loss };
}

/// @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
/// @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<ConfigParseStatus, types::HenrysLaw>
ParseHenrysLaw(const json& object, const std::vector<types::Species> existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::HenrysLaw henrys_law;

status = ValidateSchema(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys);
if (status == ConfigParseStatus::Success)
{
std::string gas_phase = object[validation::keys.gas_phase].get<std::string>();
std::string gas_phase_species = object[validation::keys.gas_phase_species].get<std::string>();
std::string aerosol_phase = object[validation::keys.aerosol_phase].get<std::string>();
std::string aerosol_phase_species = object[validation::keys.aerosol_phase_species].get<std::string>();
std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get<std::string>();

if (object.contains(validation::keys.name))
{
henrys_law.name = object[validation::keys.name].get<std::string>();
}

auto comments = GetComments(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys);

std::unordered_map<std::string, std::string> unknown_properties;
for (const auto& key : comments)
{
std::string val = object[key].dump();
unknown_properties[key] = val;
}

std::vector<std::string> requested_species;
requested_species.push_back(gas_phase_species);
requested_species.push_back(aerosol_phase_species);
requested_species.push_back(aerosol_phase_water);

std::vector<std::string> requested_aerosol_species;
requested_aerosol_species.push_back(aerosol_phase_species);
requested_aerosol_species.push_back(aerosol_phase_water);

if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species))
{
status = ConfigParseStatus::ReactionRequiresUnknownSpecies;
}

auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; });
if (status == ConfigParseStatus::Success && it == existing_phases.end())
{
status = ConfigParseStatus::UnknownPhase;
}

auto phase_it = std::find_if(
existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; });

if (phase_it != existing_phases.end())
{
// check if all of the species for this reaction are actually in the aerosol phase
std::vector<std::string> aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() };
if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_aerosol_species, aerosol_phase_species))
{
status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase;
}
}
else
{
status = ConfigParseStatus::UnknownPhase;
}

henrys_law.gas_phase = gas_phase;
henrys_law.gas_phase_species = gas_phase_species;
henrys_law.aerosol_phase = aerosol_phase;
henrys_law.aerosol_phase_species = aerosol_phase_species;
henrys_law.aerosol_phase_water = aerosol_phase_water;
henrys_law.unknown_properties = unknown_properties;
}

return { status, henrys_law };
}

/// @brief Parses all reactions
/// @param objects A json object that should contain only valid reactions
/// @param existing_species A list of spcecies configured for a mechanism
Expand Down Expand Up @@ -1446,6 +1528,16 @@ namespace open_atmos
}
reactions.first_order_loss.push_back(first_order_loss_parse.second);
}
else if (type == validation::keys.HenrysLaw_key)
{
auto henrys_law_parse = ParseHenrysLaw(object, existing_species, existing_phases);
status = henrys_law_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactions.henrys_law.push_back(henrys_law_parse.second);
}
}

return { status, reactions };
Expand Down
8 changes: 6 additions & 2 deletions test/integration/test_json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ TEST(JsonParser, ParsesFullConfiguration)
EXPECT_EQ(mechanism.species.size(), 11);
EXPECT_EQ(mechanism.phases.size(), 4);
EXPECT_EQ(mechanism.reactions.arrhenius.size(), 2);
EXPECT_EQ(mechanism.reactions.condensed_phase_arrhenius.size(), 2);
EXPECT_EQ(mechanism.reactions.troe.size(), 1);
EXPECT_EQ(mechanism.reactions.branched.size(), 1);
EXPECT_EQ(mechanism.reactions.condensed_phase_arrhenius.size(), 2);
EXPECT_EQ(mechanism.reactions.tunneling.size(), 1);
EXPECT_EQ(mechanism.reactions.surface.size(), 1);
EXPECT_EQ(mechanism.reactions.photolysis.size(), 1);
EXPECT_EQ(mechanism.reactions.condensed_phase_photolysis.size(), 1);
EXPECT_EQ(mechanism.reactions.emission.size(), 1);
EXPECT_EQ(mechanism.reactions.first_order_loss.size(), 1);
EXPECT_EQ(mechanism.reactions.henrys_law.size(), 1);
EXPECT_EQ(mechanism.reactions.photolysis.size(), 1);
EXPECT_EQ(mechanism.reactions.surface.size(), 1);
EXPECT_EQ(mechanism.reactions.troe.size(), 1);
EXPECT_EQ(mechanism.reactions.tunneling.size(), 1);
}

TEST(JsonParser, ParserReportsBadFiles)
Expand Down
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ create_standard_test(NAME parse_condensed_phase_arrhenius SOURCES test_parse_con
create_standard_test(NAME parse_condensed_phase_photolysis SOURCES test_parse_condensed_phase_photolysis.cpp)
create_standard_test(NAME parse_emission SOURCES test_parse_emission.cpp)
create_standard_test(NAME parse_first_order_loss SOURCES test_parse_first_order_loss.cpp)
create_standard_test(NAME parse_henrys_law SOURCES test_parse_henrys_law.cpp)
create_standard_test(NAME parse_phases SOURCES test_parse_phases.cpp)
create_standard_test(NAME parse_photolysis SOURCES test_parse_photolysis.cpp)
create_standard_test(NAME parse_species SOURCES test_parse_species.cpp)
Expand Down
59 changes: 59 additions & 0 deletions test/unit/test_parse_henrys_law.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <gtest/gtest.h>

#include <open_atmos/mechanism_configuration/parser.hpp>

using namespace open_atmos::mechanism_configuration;

TEST(JsonParser, CanParseValidHenrysLawReaction)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/henrys_law/valid.json"));
EXPECT_EQ(status, ConfigParseStatus::Success);

EXPECT_EQ(mechanism.reactions.henrys_law.size(), 2);

EXPECT_EQ(mechanism.reactions.henrys_law[0].name, "my henry's law");
EXPECT_EQ(mechanism.reactions.henrys_law[0].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.henrys_law[0].gas_phase_species, "A");
EXPECT_EQ(mechanism.reactions.henrys_law[0].aerosol_phase, "aqueous aerosol");
EXPECT_EQ(mechanism.reactions.henrys_law[0].aerosol_phase_species, "B");
EXPECT_EQ(mechanism.reactions.henrys_law[0].aerosol_phase_water, "H2O_aq");
EXPECT_EQ(mechanism.reactions.henrys_law[0].unknown_properties.size(), 1);
EXPECT_EQ(mechanism.reactions.henrys_law[0].unknown_properties["__comment"], "\"hi\"");

EXPECT_EQ(mechanism.reactions.henrys_law[1].name, "");
EXPECT_EQ(mechanism.reactions.henrys_law[1].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.henrys_law[1].gas_phase_species, "A");
EXPECT_EQ(mechanism.reactions.henrys_law[1].aerosol_phase, "aqueous aerosol");
EXPECT_EQ(mechanism.reactions.henrys_law[1].aerosol_phase_species, "B");
EXPECT_EQ(mechanism.reactions.henrys_law[1].aerosol_phase_water, "H2O_aq");
EXPECT_EQ(mechanism.reactions.henrys_law[1].unknown_properties.size(), 0);
}

TEST(JsonParser, HenrysLawDetectsUnknownSpecies)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/henrys_law/unknown_species.json"));
EXPECT_EQ(status, ConfigParseStatus::ReactionRequiresUnknownSpecies);
}

TEST(JsonParser, HenrysLawDetectsUnknownPhase)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/henrys_law/missing_phase.json"));
EXPECT_EQ(status, ConfigParseStatus::UnknownPhase);
}

TEST(JsonParser, HenrysLawDetectsUnknownAerosolPhaseWater)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/condensed_phase_arrhenius/missing_aerosol_phase_water.json"));
EXPECT_EQ(status, ConfigParseStatus::ReactionRequiresUnknownSpecies);
}

TEST(JsonParser, HenrysLawDetectsWhenRequestedSpeciesAreNotInAerosolPhase)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/condensed_phase_arrhenius/species_not_in_aerosol_phase.json"));
EXPECT_EQ(status, ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"version": "1.0.0",
"name": "Missing condensed phase arrhenius aerosol phase water",
"species": [
{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
},
{
"name": "H2O_aq"
}
],
"phases": [
{
"name": "aqueous aerosol",
"species": [
"A",
"B",
"C",
"H2O_aq"
]
}
],
"reactions": [
{
"type": "CONDENSED_PHASE_ARRHENIUS",
"aerosol phase": "aqueous aerosol",
"aerosol-phase water": "H2O_a",
"reactants": [
{
"species name": "A"
}
],
"products": [
{
"species name": "C"
}
]
}
]
}
27 changes: 27 additions & 0 deletions test/unit/unit_configs/reactions/henrys_law/missing_phase.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"version": "1.0.0",
"name": "Missing phase",
"species": [
{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
}
],
"phases": [ ],
"reactions": [
{
"type": "HL_PHASE_TRANSFER",
"gas phase": "gas",
"gas-phase species": "H2O2",
"aerosol phase": "aqueous aerosol",
"aerosol-phase species": "H2O2_aq",
"aerosol-phase water": "H2O_aq",
"name": "my henry's law"
}
]
}
Loading

0 comments on commit fe81f91

Please sign in to comment.