Skip to content

Commit

Permalink
parsing surface
Browse files Browse the repository at this point in the history
  • Loading branch information
K20shores committed Jan 18, 2024
1 parent 6bc85a3 commit 935efc0
Show file tree
Hide file tree
Showing 12 changed files with 406 additions and 8 deletions.
19 changes: 13 additions & 6 deletions examples/full_configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,21 @@
{
"type": "SURFACE",
"gas phase": "gas",
"gas-phase reactant": "foo",
"gas-phase reactant": {
"species name": "A",
"coefficient": 1
},
"reaction probability": 2.0e-2,
"gas-phase products": {
"bar": {},
"baz": {
"yield": 0.4
"gas-phase products": [
{
"species name": "B",
"coefficient": 1
},
{
"species name": "C",
"coefficient": 1
}
},
],
"aerosol phase": "surface reacting phase",
"name": "my surface"
},
Expand Down
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 @@ -33,6 +33,7 @@ namespace open_atmos
DuplicatePhasesDetected,
PhaseRequiresUnknownSpecies,
ReactionRequiresUnknownSpecies,
UnknownPhase
};
std::string configParseStatusToString(const ConfigParseStatus &status);

Expand Down
13 changes: 13 additions & 0 deletions include/open_atmos/mechanism_configuration/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ namespace open_atmos
// B
// C

// Surface
const std::string Surface_key = "SURFACE";
const std::string gas_phase_reactant = "gas-phase reactant";
const std::string reaction_probability = "reaction probability";
const std::string gas_phase_products = "gas-phase products";
const std::string aerosol_phase = "aerosol phase";

} keys;

struct Configuration
Expand Down Expand Up @@ -134,6 +141,12 @@ namespace open_atmos
const std::vector<std::string> optional_keys{ keys.name, keys.A, keys.B, keys.C };
} tunneling;

struct Surface
{
const std::vector<std::string> required_keys{ keys.gas_phase_products, keys.gas_phase_reactant, keys.type, keys.gas_phase, keys.aerosol_phase };
const std::vector<std::string> optional_keys{ keys.name, keys.reaction_probability };
} surface;

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 @@ -134,12 +134,31 @@ namespace open_atmos
std::unordered_map<std::string, std::string> unknown_properties;
};

struct Surface
{
/// @brief Reaction probability (0-1) [unitless]
double reaction_probability{ 1.0 };
/// @brief A list of reactants
ReactionComponent gas_phase_reactant;
/// @brief A list of products
std::vector<ReactionComponent> gas_phase_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 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<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;
};

struct Mechanism
Expand Down
92 changes: 90 additions & 2 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ namespace open_atmos
case ConfigParseStatus::DuplicatePhasesDetected: return "DuplicatePhasesDetected";
case ConfigParseStatus::PhaseRequiresUnknownSpecies: return "PhaseRequiresUnknownSpecies";
case ConfigParseStatus::ReactionRequiresUnknownSpecies: return "ReactionRequiresUnknownSpecies";
case ConfigParseStatus::UnknownPhase: return "UnknownPhase";
default: return "Unknown";
}
}
Expand Down Expand Up @@ -684,7 +685,84 @@ namespace open_atmos
return { status, tunneling };
}

std::pair<ConfigParseStatus, types::Reactions> ParseReactions(const json& objects, const std::vector<types::Species> existing_species)
std::pair<ConfigParseStatus, types::Surface> 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;

status = ValidateSchema(object, validation::surface.required_keys, validation::surface.optional_keys);
if (status == ConfigParseStatus::Success)
{
auto reactant_parse = ParseReactionComponent(object[validation::keys.gas_phase_reactant]);
status = reactant_parse.first;
if (status != ConfigParseStatus::Success)
{
return { status, surface };
}

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

if (object.contains(validation::keys.reaction_probability))
{
surface.reaction_probability = object[validation::keys.reaction_probability].get<double>();
}

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

auto comments = GetComments(object, validation::surface.required_keys, validation::surface.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);
}
requested_species.push_back(reactant_parse.second.species_name);

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

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

surface.gas_phase = object[validation::keys.gas_phase].get<std::string>();
surface.aerosol_phase = aerosol_phase;
surface.gas_phase_products = products;
surface.gas_phase_reactant = reactant_parse.second;
surface.unknown_properties = unknown_properties;
}

return { status, surface };
}

std::pair<ConfigParseStatus, types::Reactions> 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 Down Expand Up @@ -732,6 +810,16 @@ namespace open_atmos
}
reactions.tunneling.push_back(tunneling_parse.second);
}
else if (type == validation::keys.Surface_key)
{
auto surface_parse = ParseSurface(object, existing_species, existing_phases);
status = surface_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactions.surface.push_back(surface_parse.second);
}
}

return { status, reactions };
Expand Down Expand Up @@ -807,7 +895,7 @@ namespace open_atmos
}

// parse all of the reactions at once
auto reactions_parsing = ParseReactions(object[validation::keys.reactions], species_parsing.second);
auto reactions_parsing = ParseReactions(object[validation::keys.reactions], species_parsing.second, phases_parsing.second);

if (reactions_parsing.first != ConfigParseStatus::Success)
{
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 @@ -15,6 +15,7 @@ TEST(JsonParser, ParsesFullConfiguration)
EXPECT_EQ(mechanism.reactions.troe.size(), 1);
EXPECT_EQ(mechanism.reactions.branched.size(), 1);
EXPECT_EQ(mechanism.reactions.tunneling.size(), 1);
EXPECT_EQ(mechanism.reactions.surface.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_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)

################################################################################
# Copy test data
Expand Down
60 changes: 60 additions & 0 deletions test/unit/test_parse_surface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <gtest/gtest.h>

#include <open_atmos/mechanism_configuration/parser.hpp>

using namespace open_atmos::mechanism_configuration;

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

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

EXPECT_EQ(mechanism.reactions.surface[0].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.surface[0].name, "my surface");
EXPECT_EQ(mechanism.reactions.surface[0].aerosol_phase, "surface reacting phase");
EXPECT_EQ(mechanism.reactions.surface[0].reaction_probability, 2.0e-2);
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_reactant.species_name, "A");
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_reactant.coefficient, 1);
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_products.size(), 2);
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_products[0].species_name, "B");
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_products[0].coefficient, 1);
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_products[1].species_name, "C");
EXPECT_EQ(mechanism.reactions.surface[0].gas_phase_products[1].coefficient, 1);

EXPECT_EQ(mechanism.reactions.surface[1].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.surface[1].aerosol_phase, "surface reacting phase");
EXPECT_EQ(mechanism.reactions.surface[1].reaction_probability, 1.0);
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_reactant.species_name, "A");
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_reactant.coefficient, 1);
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products.size(), 2);
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products[0].species_name, "B");
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products[0].coefficient, 1);
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products[0].unknown_properties.size(), 1);
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products[0].unknown_properties["__optional thing"], "\"hello\"");
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products[1].species_name, "C");
EXPECT_EQ(mechanism.reactions.surface[1].gas_phase_products[1].coefficient, 1);
}

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

TEST(JsonParser, SurfaceDetectsBadReactionComponent)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/surface/bad_reaction_component.json"));
EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound);
}

TEST(JsonParser, SurfaceDetectsUnknownPhase)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/surface/missing_aerosol_phase.json"));
EXPECT_EQ(status, ConfigParseStatus::UnknownPhase);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"version": "1.0.0",
"name": "Bad reaction component",
"species": [
{
"name": "A"
},
{
"name": "B"
}
],
"phases": [
{
"name": "gas",
"species": [
"A",
"B"
]
}
],
"reactions": [
{
"type": "SURFACE",
"gas phase": "gas",
"gas-phase reactant": {
"Species name": "A",
"coefficient": 1
},
"reaction probability": 2.0e-2,
"gas-phase products": [
{
"species name": "B",
"coefficient": 1
},
{
"species name": "C",
"coefficient": 1
}
],
"aerosol phase": "surface reacting phase",
"name": "my surface",
"__coment" : "key lime pie is superior to all other pies"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"version": "1.0.0",
"name": "Missing aerosol phase",
"species": [
{
"name": "A"
},
{
"name": "B"
},
{
"name": "C"
}
],
"phases": [
{
"name": "gas",
"species": [
"A",
"B",
"C"
]
}
],
"reactions": [
{
"type": "SURFACE",
"gas phase": "gas",
"gas-phase reactant": {
"species name": "A",
"coefficient": 1
},
"gas-phase products": [
{
"species name": "B",
"__optional thing": "hello"
},
{
"species name": "C"
}
],
"aerosol phase": "surface reacting phase"
}
]
}
Loading

0 comments on commit 935efc0

Please sign in to comment.