Skip to content

Commit

Permalink
Merge pull request #29 from open-atmos/parse_condensed_phase_arrhenius
Browse files Browse the repository at this point in the history
Parse condensed phase arrhenius
  • Loading branch information
K20shores authored Jan 19, 2024
2 parents 51a289a + 860d539 commit bf52cb0
Show file tree
Hide file tree
Showing 17 changed files with 870 additions and 13 deletions.
62 changes: 58 additions & 4 deletions examples/full_configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,38 @@
"type": "CONDENSED_PHASE_ARRHENIUS",
"aerosol phase": "aqueous aerosol",
"aerosol-phase water": "H2O_aq",
"A": 123.45,
"Ea": 123.45,
"B": 1.3,
"D": 300.0,
"E": 0.6E-5,
"reactants": [
{
"species name": "H2O2_aq",
"coefficient": 1
},
{
"species name": "H2O_aq",
"coefficient": 1
}
],
"products": [
{
"species name": "ethanol_aq",
"coefficient": 1
}
],
"name": "my condensed arrhenius"
},
{
"type": "CONDENSED_PHASE_ARRHENIUS",
"aerosol phase": "aqueous aerosol",
"aerosol-phase water": "H2O_aq",
"A": 123.45,
"C": 123.45,
"B": 1.3,
"D": 300.0,
"E": 0.6E-5,
"reactants": [
{
"species name": "H2O2_aq",
Expand Down Expand Up @@ -311,10 +343,10 @@
},
{
"gas phase": "gas",
"type" : "TUNNELING",
"A" : 123.45,
"B" : 1200.0,
"C" : 1.0e8,
"type": "TUNNELING",
"A": 123.45,
"B": 1200.0,
"C": 1.0e8,
"reactants": [
{
"species name": "B",
Expand Down Expand Up @@ -356,6 +388,28 @@
"D": 63.4,
"E": -1.3,
"name": "my arrhenius"
},
{
"type": "ARRHENIUS",
"gas phase": "gas",
"reactants": [
{
"species name": "B",
"coefficient": 1
}
],
"products": [
{
"species name": "C",
"coefficient": 1
}
],
"A": 32.1,
"B": -2.3,
"Ea": 102.3,
"D": 63.4,
"E": -1.3,
"name": "my arrhenius"
}
]
}
1 change: 1 addition & 0 deletions include/open_atmos/mechanism_configuration/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace open_atmos
PhaseRequiresUnknownSpecies,
ReactionRequiresUnknownSpecies,
UnknownPhase,
RequestedAerosolSpeciesNotIncludedInAerosolPhase,
TooManyReactionComponents
};
std::string configParseStatusToString(const ConfigParseStatus &status);
Expand Down
18 changes: 18 additions & 0 deletions include/open_atmos/mechanism_configuration/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,18 @@ namespace open_atmos
const std::string E = "E";
const std::string Ea = "Ea";

// Condensed Phase Arrhenius
const std::string CondensedPhaseArrhenius_key = "CONDENSED_PHASE_ARRHENIUS";
const std::string aerosol_phase_water = "aerosol-phase water";
// also these
// aerosol phase
// A
// B
// C
// D
// E
// Ea

// Troe
const std::string Troe_key = "TROE";
const std::string k0_A = "k0_A";
Expand Down Expand Up @@ -135,6 +147,12 @@ namespace open_atmos
const std::vector<std::string> optional_keys{ keys.A, keys.B, keys.C, keys.D, keys.E, keys.Ea, keys.name };
} arrhenius;

struct CondensedPhaseArrhenius
{
const std::vector<std::string> required_keys{ keys.products, keys.reactants, keys.type, keys.aerosol_phase, keys.aerosol_phase_water };
const std::vector<std::string> optional_keys{ keys.A, keys.B, keys.C, keys.D, keys.E, keys.Ea, keys.name };
} condensed_phase_arrhenius;

struct Troe
{
const std::vector<std::string> required_keys{ keys.products, keys.reactants, keys.type, keys.gas_phase };
Expand Down
28 changes: 28 additions & 0 deletions include/open_atmos/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,33 @@ namespace open_atmos
std::unordered_map<std::string, std::string> unknown_properties;
};

struct CondensedPhaseArrhenius
{
/// @brief Pre-exponential factor [(mol m−3)^(−(𝑛−1)) s−1]
double A{ 1 };
/// @brief Unitless exponential factor
double B{ 0 };
/// @brief Activation threshold, expected to be the negative activation energy divided by the boltzman constant
/// [-E_a / k_b), K]
double C{ 0 };
/// @brief A factor that determines temperature dependence [K]
double D{ 300 };
/// @brief A factor that determines pressure dependence [Pa-1]
double E{ 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 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 Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

struct Troe
{
/// @brief low-pressure pre-exponential factor
Expand Down Expand Up @@ -199,6 +226,7 @@ namespace open_atmos
struct Reactions
{
std::vector<types::Arrhenius> arrhenius;
std::vector<types::CondensedPhaseArrhenius> condensed_phase_arrhenius;
std::vector<types::Troe> troe;
std::vector<types::Branched> branched;
std::vector<types::Tunneling> tunneling;
Expand Down
156 changes: 154 additions & 2 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ namespace open_atmos
case ConfigParseStatus::PhaseRequiresUnknownSpecies: return "PhaseRequiresUnknownSpecies";
case ConfigParseStatus::ReactionRequiresUnknownSpecies: return "ReactionRequiresUnknownSpecies";
case ConfigParseStatus::UnknownPhase: return "UnknownPhase";
case ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase: return "RequestedAerosolSpeciesNotIncludedInAerosolPhase";
case ConfigParseStatus::TooManyReactionComponents: return "TooManyReactionComponents";
default: return "Unknown";
}
Expand Down Expand Up @@ -177,6 +178,21 @@ namespace open_atmos
return false;
}

bool RequiresUnknownSpecies(const std::vector<std::string> requested_species, const std::vector<std::string>& existing_species)
{
for (const auto& spec : requested_species)
{
auto it =
std::find_if(existing_species.begin(), existing_species.end(), [&spec](const std::string& existing) { return existing == spec; });

if (it == existing_species.end())
{
return true;
}
}
return false;
}

std::pair<ConfigParseStatus, std::vector<types::Species>> ParseSpecies(const json& objects)
{
ConfigParseStatus status = ConfigParseStatus::Success;
Expand Down Expand Up @@ -416,8 +432,129 @@ namespace open_atmos
return { status, arrhenius };
}

std::pair<ConfigParseStatus, types::Troe>
ParseTroe(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase> existing_phases)
std::pair<ConfigParseStatus, types::CondensedPhaseArrhenius> ParseCondensedPhaseArrhenius(const json& object, const std::vector<types::Species>& existing_species, const std::vector<types::Phase>& existing_phases)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::CondensedPhaseArrhenius condensed_phase_arrhenius;

status = ValidateSchema(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.optional_keys);
if (status == ConfigParseStatus::Success)
{
std::vector<types::ReactionComponent> products{};
for (const auto& product : object[validation::keys.products])
{
auto product_parse = ParseReactionComponent(product);
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.A))
{
condensed_phase_arrhenius.A = object[validation::keys.A].get<double>();
}
if (object.contains(validation::keys.B))
{
condensed_phase_arrhenius.B = object[validation::keys.B].get<double>();
}
if (object.contains(validation::keys.C))
{
condensed_phase_arrhenius.C = object[validation::keys.C].get<double>();
}
if (object.contains(validation::keys.D))
{
condensed_phase_arrhenius.D = object[validation::keys.D].get<double>();
}
if (object.contains(validation::keys.E))
{
condensed_phase_arrhenius.E = object[validation::keys.E].get<double>();
}
if (object.contains(validation::keys.Ea))
{
if (condensed_phase_arrhenius.C != 0)
{
std::cerr << "Ea is specified when C is also specified for an CondensedPhasecondensed_phase_arrhenius reaction. Pick one." << std::endl;
status = ConfigParseStatus::MutuallyExclusiveOption;
}
// Calculate 'C' using 'Ea'
condensed_phase_arrhenius.C = -1 * object[validation::keys.Ea].get<double>() / constants::boltzmann;
}

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

auto comments = GetComments(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.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::string aerosol_phase = object[validation::keys.aerosol_phase].get<std::string>();
std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get<std::string>();

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);
}
requested_species.push_back(aerosol_phase_water);

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

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_species, aerosol_phase_species))
{
status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase;
}
}
else {
status = ConfigParseStatus::UnknownPhase;
}

condensed_phase_arrhenius.aerosol_phase = aerosol_phase;
condensed_phase_arrhenius.aerosol_phase_water = aerosol_phase_water;
condensed_phase_arrhenius.products = products;
condensed_phase_arrhenius.reactants = reactants;
condensed_phase_arrhenius.unknown_properties = unknown_properties;
}

return { status, condensed_phase_arrhenius };
}

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 @@ -869,6 +1006,11 @@ namespace open_atmos
status = ConfigParseStatus::UnknownPhase;
}

if (status == ConfigParseStatus::Success && reactants.size() > 1)
{
status = ConfigParseStatus::TooManyReactionComponents;
}

photolysis.gas_phase = gas_phase;
photolysis.products = products;
photolysis.reactants = reactants;
Expand Down Expand Up @@ -1034,6 +1176,16 @@ namespace open_atmos
}
reactions.arrhenius.push_back(arrhenius_parse.second);
}
else if (type == validation::keys.CondensedPhaseArrhenius_key)
{
auto condensed_phase_arrhenius_parse = ParseCondensedPhaseArrhenius(object, existing_species, existing_phases);
status = condensed_phase_arrhenius_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactions.condensed_phase_arrhenius.push_back(condensed_phase_arrhenius_parse.second);
}
else if (type == validation::keys.Troe_key)
{
auto troe_parse = ParseTroe(object, existing_species, existing_phases);
Expand Down
3 changes: 2 additions & 1 deletion test/integration/test_json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ TEST(JsonParser, ParsesFullConfiguration)
EXPECT_EQ(mechanism.name, "Full Configuration");
EXPECT_EQ(mechanism.species.size(), 11);
EXPECT_EQ(mechanism.phases.size(), 4);
EXPECT_EQ(mechanism.reactions.arrhenius.size(), 1);
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.tunneling.size(), 1);
Expand Down
13 changes: 7 additions & 6 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@ include(test_util)
################################################################################
# Tests

create_standard_test(NAME parse_species SOURCES test_parse_species.cpp)
create_standard_test(NAME parse_phases SOURCES test_parse_phases.cpp)
create_standard_test(NAME parse_arrhenius SOURCES test_parse_arrhenius.cpp)
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)
create_standard_test(NAME parse_condensed_phase_arrhenius SOURCES test_parse_condensed_phase_arrhenius.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_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)
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)

################################################################################
# Copy test data
Expand Down
Loading

0 comments on commit bf52cb0

Please sign in to comment.