Skip to content

Commit

Permalink
Merge pull request #26 from open-atmos/parse_photolysis
Browse files Browse the repository at this point in the history
Parse photolysis
  • Loading branch information
K20shores authored Jan 19, 2024
2 parents 9c6ac54 + c693ab2 commit 65732fb
Show file tree
Hide file tree
Showing 20 changed files with 630 additions and 15 deletions.
10 changes: 10 additions & 0 deletions include/open_atmos/mechanism_configuration/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ namespace open_atmos
const std::string gas_phase_products = "gas-phase products";
const std::string aerosol_phase = "aerosol phase";

// Photolysis
const std::string Photolysis_key = "PHOTOLYSIS";
const std::string scaling_factor = "scaling factor";

} keys;

struct Configuration
Expand Down Expand Up @@ -149,6 +153,12 @@ namespace open_atmos
const std::vector<std::string> optional_keys{ keys.name, keys.reaction_probability };
} surface;

struct Photolysis
{
const std::vector<std::string> required_keys{ keys.reactants, keys.products, keys.type, keys.gas_phase };
const std::vector<std::string> optional_keys{ keys.name, keys.scaling_factor };
} photolysis;

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

struct Photolysis
{
/// @brief Scaling factor to apply to user-provided rate constants
double scaling_factor_{ 1.0 };
/// @brief A list of reactants
std::vector<ReactionComponent> reactants;
/// @brief A list of products
std::vector<ReactionComponent> products;
/// @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 Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

struct Reactions
{
std::vector<types::Arrhenius> arrhenius;
std::vector<types::Troe> troe;
std::vector<types::Branched> branched;
std::vector<types::Tunneling> tunneling;
std::vector<types::Surface> surface;
std::vector<types::Photolysis> photolysis;
};

struct Mechanism
Expand Down
154 changes: 140 additions & 14 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ namespace open_atmos
return { status, component };
}

std::pair<ConfigParseStatus, types::Arrhenius> ParseArrhenius(const json& object, const std::vector<types::Species> existing_species)
std::pair<ConfigParseStatus, types::Arrhenius> ParseArrhenius(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Arrhenius arrhenius;
Expand Down Expand Up @@ -398,7 +398,15 @@ namespace open_atmos
status = ConfigParseStatus::ReactionRequiresUnknownSpecies;
}

arrhenius.gas_phase = object[validation::keys.gas_phase].get<std::string>();
std::string gas_phase = object[validation::keys.gas_phase].get<std::string>();
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;
}

arrhenius.gas_phase = gas_phase;
arrhenius.products = products;
arrhenius.reactants = reactants;
arrhenius.unknown_properties = unknown_properties;
Expand All @@ -407,7 +415,7 @@ namespace open_atmos
return { status, arrhenius };
}

std::pair<ConfigParseStatus, types::Troe> ParseTroe(const json& object, const std::vector<types::Species> existing_species)
std::pair<ConfigParseStatus, types::Troe> ParseTroe(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Troe troe;
Expand Down Expand Up @@ -501,7 +509,16 @@ namespace open_atmos
status = ConfigParseStatus::ReactionRequiresUnknownSpecies;
}

troe.gas_phase = object[validation::keys.gas_phase].get<std::string>();

std::string gas_phase = object[validation::keys.gas_phase].get<std::string>();
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;
}

troe.gas_phase = gas_phase;
troe.products = products;
troe.reactants = reactants;
troe.unknown_properties = unknown_properties;
Expand All @@ -510,7 +527,7 @@ namespace open_atmos
return { status, troe };
}

std::pair<ConfigParseStatus, types::Branched> ParseBranched(const json& object, const std::vector<types::Species> existing_species)
std::pair<ConfigParseStatus, types::Branched> ParseBranched(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Branched branched;
Expand Down Expand Up @@ -592,7 +609,15 @@ namespace open_atmos
status = ConfigParseStatus::ReactionRequiresUnknownSpecies;
}

branched.gas_phase = object[validation::keys.gas_phase].get<std::string>();
std::string gas_phase = object[validation::keys.gas_phase].get<std::string>();
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;
}

branched.gas_phase = gas_phase;
branched.nitrate_products = nitrate_products;
branched.alkoxy_products = alkoxy_products;
branched.reactants = reactants;
Expand All @@ -602,7 +627,7 @@ namespace open_atmos
return { status, branched };
}

std::pair<ConfigParseStatus, types::Tunneling> ParseTunneling(const json& object, const std::vector<types::Species> existing_species)
std::pair<ConfigParseStatus, types::Tunneling> ParseTunneling(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Tunneling tunneling;
Expand Down Expand Up @@ -676,7 +701,15 @@ namespace open_atmos
status = ConfigParseStatus::ReactionRequiresUnknownSpecies;
}

tunneling.gas_phase = object[validation::keys.gas_phase].get<std::string>();
std::string gas_phase = object[validation::keys.gas_phase].get<std::string>();
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;
}

tunneling.gas_phase = gas_phase;
tunneling.products = products;
tunneling.reactants = reactants;
tunneling.unknown_properties = unknown_properties;
Expand All @@ -686,7 +719,7 @@ namespace open_atmos
}

std::pair<ConfigParseStatus, types::Surface>
ParseSurface(const json& object, const std::vector<types::Species> existing_species, const std::vector<types::Phase> existing_phases)
ParseSurface(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Surface surface;
Expand Down Expand Up @@ -762,8 +795,91 @@ namespace open_atmos
return { status, surface };
}

std::pair<ConfigParseStatus, types::Photolysis> ParsePhotolysis(const json& object, const std::vector<types::Species> existing_species, const std::vector<types::Phase> existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Photolysis photolysis;

status = ValidateSchema(object, validation::photolysis.required_keys, validation::photolysis.optional_keys);
if (status == ConfigParseStatus::Success)
{
std::vector<types::ReactionComponent> products{};
for (const auto& reactant : object[validation::keys.products])
{
auto product_parse = ParseReactionComponent(reactant);
status = product_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
products.push_back(product_parse.second);
}

std::vector<types::ReactionComponent> reactants{};
for (const auto& reactant : object[validation::keys.reactants])
{
auto reactant_parse = ParseReactionComponent(reactant);
status = reactant_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactants.push_back(reactant_parse.second);
}

if (object.contains(validation::keys.scaling_factor))
{
photolysis.scaling_factor_ = object[validation::keys.scaling_factor].get<double>();
}

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

auto comments = GetComments(object, validation::photolysis.required_keys, validation::photolysis.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;
for (const auto& spec : products)
{
requested_species.push_back(spec.species_name);
}
for (const auto& spec : reactants)
{
requested_species.push_back(spec.species_name);
}

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

std::string gas_phase = object[validation::keys.gas_phase].get<std::string>();
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;
}

photolysis.gas_phase = gas_phase;
photolysis.products = products;
photolysis.reactants = reactants;
photolysis.unknown_properties = unknown_properties;
}

return { status, photolysis };
}

std::pair<ConfigParseStatus, types::Reactions>
ParseReactions(const json& objects, const std::vector<types::Species> existing_species, const std::vector<types::Phase> existing_phases)
ParseReactions(const json& objects, const std::vector<types::Species>& existing_species, const std::vector<types::Phase>& existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Reactions reactions;
Expand All @@ -773,7 +889,7 @@ namespace open_atmos
std::string type = object[validation::keys.type].get<std::string>();
if (type == validation::keys.Arrhenius_key)
{
auto arrhenius_parse = ParseArrhenius(object, existing_species);
auto arrhenius_parse = ParseArrhenius(object, existing_species, existing_phases);
status = arrhenius_parse.first;
if (status != ConfigParseStatus::Success)
{
Expand All @@ -783,7 +899,7 @@ namespace open_atmos
}
else if (type == validation::keys.Troe_key)
{
auto troe_parse = ParseTroe(object, existing_species);
auto troe_parse = ParseTroe(object, existing_species, existing_phases);
status = troe_parse.first;
if (status != ConfigParseStatus::Success)
{
Expand All @@ -793,7 +909,7 @@ namespace open_atmos
}
else if (type == validation::keys.Branched_key)
{
auto branched_parse = ParseBranched(object, existing_species);
auto branched_parse = ParseBranched(object, existing_species, existing_phases);
status = branched_parse.first;
if (status != ConfigParseStatus::Success)
{
Expand All @@ -803,7 +919,7 @@ namespace open_atmos
}
else if (type == validation::keys.Tunneling_key)
{
auto tunneling_parse = ParseTunneling(object, existing_species);
auto tunneling_parse = ParseTunneling(object, existing_species, existing_phases);
status = tunneling_parse.first;
if (status != ConfigParseStatus::Success)
{
Expand All @@ -821,6 +937,16 @@ namespace open_atmos
}
reactions.surface.push_back(surface_parse.second);
}
else if (type == validation::keys.Photolysis_key)
{
auto photolysis_parse = ParsePhotolysis(object, existing_species, existing_phases);
status = photolysis_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactions.photolysis.push_back(photolysis_parse.second);
}
}

return { status, reactions };
Expand Down
1 change: 1 addition & 0 deletions test/integration/test_json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ TEST(JsonParser, ParsesFullConfiguration)
EXPECT_EQ(mechanism.reactions.branched.size(), 1);
EXPECT_EQ(mechanism.reactions.tunneling.size(), 1);
EXPECT_EQ(mechanism.reactions.surface.size(), 1);
EXPECT_EQ(mechanism.reactions.photolysis.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 @@ -13,6 +13,7 @@ create_standard_test(NAME parse_troe SOURCES test_parse_troe.cpp)
create_standard_test(NAME parse_branched SOURCES test_parse_branched.cpp)
create_standard_test(NAME parse_tunneling SOURCES test_parse_tunneling.cpp)
create_standard_test(NAME parse_surface SOURCES test_parse_surface.cpp)
create_standard_test(NAME parse_photolysis SOURCES test_parse_photolysis.cpp)

################################################################################
# Copy test data
Expand Down
7 changes: 7 additions & 0 deletions test/unit/test_parse_arrhenius.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,11 @@ TEST(JsonParser, ArrheniusDetectsBadReactionComponent)
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/bad_reaction_component.json"));
EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound);
}

TEST(JsonParser, ArrheniusDetectsUnknownPhase)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/missing_phase.json"));
EXPECT_EQ(status, ConfigParseStatus::UnknownPhase);
}
7 changes: 7 additions & 0 deletions test/unit/test_parse_branched.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,11 @@ TEST(JsonParser, BranchedDetectsBadReactionComponent)
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/branched/bad_reaction_component.json"));
EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound);
}

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

0 comments on commit 65732fb

Please sign in to comment.