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

Parse troe #17

Merged
merged 2 commits into from
Jan 18, 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
3 changes: 3 additions & 0 deletions examples/full_configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
{
"name": "C"
},
{
"name": "M"
},
{
"name": "H2O2",
"HLC(298K) [mol m-3 Pa-1]": 1.011596348,
Expand Down
17 changes: 17 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,17 @@ namespace open_atmos
const std::string E = "E";
const std::string Ea = "Ea";

// Troe
const std::string Troe_key = "TROE";
const std::string k0_A = "k0_A";
const std::string k0_B = "k0_B";
const std::string k0_C = "k0_C";
const std::string kinf_A = "kinf_A";
const std::string kinf_B = "kinf_B";
const std::string kinf_C = "kinf_C";
const std::string Fc = "Fc";
const std::string N = "N";

} keys;

struct Configuration
Expand Down Expand Up @@ -89,6 +100,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 Troe
{
const std::vector<std::string> required_keys{ keys.products, keys.reactants, keys.type, keys.gas_phase };
const std::vector<std::string> optional_keys{ keys.name, keys.k0_A, keys.k0_B, keys.k0_C, keys.kinf_A, keys.kinf_B, keys.kinf_C, keys.Fc, keys.N };
} troe;

struct Mechanism
{
const std::vector<std::string> required_keys{};
Expand Down
37 changes: 34 additions & 3 deletions include/open_atmos/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,24 @@ namespace open_atmos
struct Species
{
std::string name;

std::map<std::string, double> optional_numerical_properties;

/// @brief Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

struct Phase
{
std::string name;
std::vector<std::string> species;
/// @brief Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

struct ReactionComponent
{
std::string species_name;
double coefficient;
/// @brief Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

Expand All @@ -47,7 +48,6 @@ namespace open_atmos
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
Expand All @@ -56,13 +56,44 @@ namespace open_atmos
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 Troe
{
/// @brief low-pressure pre-exponential factor
double k0_A = 1.0;
/// @brief low-pressure temperature-scaling parameter
double k0_B = 0.0;
/// @brief low-pressure exponential factor
double k0_C = 0.0;
/// @brief high-pressure pre-exponential factor
double kinf_A = 1.0;
/// @brief high-pressure temperature-scaling parameter
double kinf_B = 0.0;
/// @brief high-pressure exponential factor
double kinf_C = 0.0;
/// @brief Troe F_c parameter
double Fc = 0.6;
/// @brief Troe N parameter
double N = 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;
};

struct Mechanism
Expand Down
130 changes: 124 additions & 6 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ namespace open_atmos
double coefficient = 1;
if (object.contains(validation::keys.coefficient))
{
coefficient = object[validation::keys.coefficient].get<double>();
coefficient = object[validation::keys.coefficient].get<double>();
}

auto comments = GetComments(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys);
Expand Down Expand Up @@ -318,7 +318,8 @@ namespace open_atmos
{
auto product_parse = ParseReactionComponent(product);
status = product_parse.first;
if (status != ConfigParseStatus::Success) {
if (status != ConfigParseStatus::Success)
{
break;
}
products.push_back(product_parse.second);
Expand All @@ -329,7 +330,8 @@ namespace open_atmos
{
auto reactant_parse = ParseReactionComponent(reactant);
status = reactant_parse.first;
if (status != ConfigParseStatus::Success) {
if (status != ConfigParseStatus::Success)
{
break;
}
reactants.push_back(reactant_parse.second);
Expand Down Expand Up @@ -381,14 +383,17 @@ namespace open_atmos
}

std::vector<std::string> requested_species;
for(const auto& spec : products) {
for (const auto& spec : products)
{
requested_species.push_back(spec.species_name);
}
for(const auto& spec : reactants) {
for (const auto& spec : reactants)
{
requested_species.push_back(spec.species_name);
}

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

Expand All @@ -401,6 +406,109 @@ namespace open_atmos
return { status, arrhenius };
}

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

status = ValidateSchema(object, validation::troe.required_keys, validation::troe.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.k0_A))
{
troe.k0_A = object[validation::keys.k0_A].get<double>();
}
if (object.contains(validation::keys.k0_B))
{
troe.k0_B = object[validation::keys.k0_B].get<double>();
}
if (object.contains(validation::keys.k0_C))
{
troe.k0_C = object[validation::keys.k0_C].get<double>();
}
if (object.contains(validation::keys.kinf_A))
{
troe.kinf_A = object[validation::keys.kinf_A].get<double>();
}
if (object.contains(validation::keys.kinf_B))
{
troe.kinf_B = object[validation::keys.kinf_B].get<double>();
}
if (object.contains(validation::keys.kinf_C))
{
troe.kinf_C = object[validation::keys.kinf_C].get<double>();
}
if (object.contains(validation::keys.Fc))
{
troe.Fc = object[validation::keys.Fc].get<double>();
}
if (object.contains(validation::keys.N))
{
troe.N = object[validation::keys.N].get<double>();
}

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

auto comments = GetComments(object, validation::troe.required_keys, validation::troe.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;
}

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

return { status, troe };
}

std::pair<ConfigParseStatus, types::Reactions> ParseReactions(const json& objects, const std::vector<types::Species> existing_species)
{
ConfigParseStatus status = ConfigParseStatus::Success;
Expand All @@ -419,6 +527,16 @@ namespace open_atmos
}
reactions.arrhenius.push_back(arrhenius_parse.second);
}
else if (type == validation::keys.Troe_key)
{
auto troe_parse = ParseTroe(object, existing_species);
status = troe_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactions.troe.push_back(troe_parse.second);
}
}

return { status, reactions };
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 @@ -10,8 +10,9 @@ TEST(JsonParser, ParsesFullConfiguration)
auto [status, mechanism] = parser.Parse(std::string("examples/full_configuration.json"));
EXPECT_EQ(status, ConfigParseStatus::Success);
EXPECT_EQ(mechanism.name, "Full Configuration");
EXPECT_EQ(mechanism.species.size(), 10);
EXPECT_EQ(mechanism.species.size(), 11);
EXPECT_EQ(mechanism.reactions.arrhenius.size(), 1);
EXPECT_EQ(mechanism.reactions.troe.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 @@ -9,6 +9,7 @@ include(test_util)
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)

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

#include <open_atmos/mechanism_configuration/parser.hpp>

using namespace open_atmos::mechanism_configuration;

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

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

EXPECT_EQ(mechanism.reactions.troe[0].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.troe[0].k0_A, 1.0);
EXPECT_EQ(mechanism.reactions.troe[0].k0_B, 0.0);
EXPECT_EQ(mechanism.reactions.troe[0].k0_C, 0.0);
EXPECT_EQ(mechanism.reactions.troe[0].kinf_A, 1.0);
EXPECT_EQ(mechanism.reactions.troe[0].kinf_B, 0.0);
EXPECT_EQ(mechanism.reactions.troe[0].kinf_C, 0.0);
EXPECT_EQ(mechanism.reactions.troe[0].Fc, 0.6);
EXPECT_EQ(mechanism.reactions.troe[0].N, 1.0);
EXPECT_EQ(mechanism.reactions.troe[0].reactants.size(), 1);
EXPECT_EQ(mechanism.reactions.troe[0].reactants[0].species_name, "A");
EXPECT_EQ(mechanism.reactions.troe[0].reactants[0].coefficient, 1);
EXPECT_EQ(mechanism.reactions.troe[0].products.size(), 1);
EXPECT_EQ(mechanism.reactions.troe[0].products[0].species_name, "C");
EXPECT_EQ(mechanism.reactions.troe[0].products[0].coefficient, 1);
EXPECT_EQ(mechanism.reactions.troe[0].unknown_properties.size(), 1);
EXPECT_EQ(mechanism.reactions.troe[0].unknown_properties["__my object"], "{\"a\":1.0}");

EXPECT_EQ(mechanism.reactions.troe[1].name, "my troe");
EXPECT_EQ(mechanism.reactions.troe[1].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.troe[1].k0_A, 32.1);
EXPECT_EQ(mechanism.reactions.troe[1].k0_B, -2.3);
EXPECT_EQ(mechanism.reactions.troe[1].k0_C, 102.3);
EXPECT_EQ(mechanism.reactions.troe[1].kinf_A, 63.4);
EXPECT_EQ(mechanism.reactions.troe[1].kinf_B, -1.3);
EXPECT_EQ(mechanism.reactions.troe[1].kinf_C, 908.5);
EXPECT_EQ(mechanism.reactions.troe[1].Fc, 1.3);
EXPECT_EQ(mechanism.reactions.troe[1].N, 32.1);
EXPECT_EQ(mechanism.reactions.troe[1].reactants.size(), 1);
EXPECT_EQ(mechanism.reactions.troe[1].reactants[0].species_name, "C");
EXPECT_EQ(mechanism.reactions.troe[1].reactants[0].coefficient, 1);
EXPECT_EQ(mechanism.reactions.troe[1].products.size(), 2);
EXPECT_EQ(mechanism.reactions.troe[1].products[0].species_name, "A");
EXPECT_EQ(mechanism.reactions.troe[1].products[0].coefficient, 0.2);
EXPECT_EQ(mechanism.reactions.troe[1].products[0].unknown_properties.size(), 1);
EXPECT_EQ(mechanism.reactions.troe[1].products[0].unknown_properties["__optional thing"], "\"hello\"");
EXPECT_EQ(mechanism.reactions.troe[1].products[1].species_name, "B");
EXPECT_EQ(mechanism.reactions.troe[1].products[1].coefficient, 1.2);
EXPECT_EQ(mechanism.reactions.troe[1].products[1].unknown_properties.size(), 0);
}

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

TEST(JsonParser, TroeDetectsBadReactionComponent)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/troe/bad_reaction_component.json"));
EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": "1.0.0",
"name": "Mutually Exclusive",
"name": "Bad reaction component",
"species": [
{
"name": "A"
Expand Down
Loading