Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

parsing henrys law #35

Merged
merged 2 commits into from
Jan 23, 2024
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
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
Loading