Skip to content

Commit

Permalink
parsing arrhenius
Browse files Browse the repository at this point in the history
  • Loading branch information
K20shores committed Jan 17, 2024
1 parent fb6d30d commit f8c2b54
Show file tree
Hide file tree
Showing 8 changed files with 369 additions and 4 deletions.
14 changes: 14 additions & 0 deletions include/open_atmos/constants.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (C) 2023-2024 National Center for Atmospheric Research, University of Illinois at Urbana-Champaign
//
// SPDX-License-Identifier: Apache-2.0

#pragma once
namespace open_atmos
{
namespace constants
{
static constexpr double boltzmann = 1.380649e-23; // J K^{-1}
static constexpr double avogadro = 6.02214076e23; // # mol^{-1}
static constexpr double R = boltzmann * avogadro; // J K^{-1} mol^{-1}
} // namespace constants
} // namespace open_atmos
32 changes: 32 additions & 0 deletions include/open_atmos/mechanism_configuration/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,26 @@ namespace open_atmos
const std::string phase = "phase";
const std::string n_star = "N star";
const std::string density = "density [kg m-3]";

// Reactions
const std::string reactants = "reactants";
const std::string products = "products";
const std::string type = "type";
const std::string gas_phase = "gas phase";

// Reactant and product
const std::string species_name = "species name";
const std::string coefficient = "coefficient";

// Arrhenius
const std::string Arrhenius_key = "ARRHENIUS";
const std::string A = "A";
const std::string B = "B";
const std::string C = "C";
const std::string D = "D";
const std::string E = "E";
const std::string Ea = "Ea";

} keys;

struct Configuration
Expand All @@ -57,6 +77,18 @@ namespace open_atmos
const std::vector<std::string> optional_keys{};
} phase;

struct ReactionComponent
{
const std::vector<std::string> required_keys{ keys.species_name };
const std::vector<std::string> optional_keys{ keys.coefficient };
} reaction_component;

struct Arrhenius
{
const std::vector<std::string> required_keys{ keys.products, keys.reactants, keys.type, keys.gas_phase };
const std::vector<std::string> optional_keys{ keys.A, keys.B, keys.C, keys.D, keys.E, keys.name };
} arrhenius;

struct Mechanism
{
const std::vector<std::string> required_keys{};
Expand Down
42 changes: 41 additions & 1 deletion include/open_atmos/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,51 @@ namespace open_atmos
std::unordered_map<std::string, std::string> unknown_properties;
};

struct ReactionComponent
{
std::string species_name;
double coefficient;
std::unordered_map<std::string, std::string> unknown_properties;
};

struct Arrhenius
{
/// @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 gas phase this reaction takes place in
std::string gas_phase;

std::unordered_map<std::string, std::string> unknown_properties;
};

struct Reactions
{
std::vector<types::Arrhenius> arrhenius;
};

struct Mechanism
{
std::string name; // optional
/// @brief An identifier, optional
std::string name;
std::vector<types::Species> species;
std::vector<types::Phase> phases;
Reactions reactions;
};

} // namespace types
Expand Down
148 changes: 146 additions & 2 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//
// SPDX-License-Identifier: Apache-2.0

#include <open_atmos/constants.hpp>
#include <open_atmos/mechanism_configuration/parser.hpp>
#include <open_atmos/mechanism_configuration/validation.hpp>
#include <open_atmos/mechanism_configuration/version.hpp>
Expand Down Expand Up @@ -271,6 +272,138 @@ namespace open_atmos
return { status, all_phases };
}

std::pair<ConfigParseStatus, types::ReactionComponent> ParseReactionComponent(const json& object)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::ReactionComponent component;

status = ValidateSchema(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys);
if (status == ConfigParseStatus::Success)
{
double coefficient = object[validation::keys.coefficient].get<double>();
std::string species_name = object[validation::keys.species_name].get<std::string>();

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

component.species_name = species_name;
component.coefficient = coefficient;
component.unknown_properties = unknown_properties;
}

return { status, component };
}

std::pair<ConfigParseStatus, types::Arrhenius> ParseArrhenius(const json& object, const std::vector<types::Species> existing_species)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Arrhenius arrhenius;

status = ValidateSchema(object, validation::arrhenius.required_keys, validation::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);
if (product_parse.first != 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);
if (reactant_parse.first != ConfigParseStatus::Success) {
break;
}
reactants.push_back(reactant_parse.second);
}

if (object.contains(validation::keys.A))
{
arrhenius.A = object[validation::keys.A].get<double>();
}
if (object.contains(validation::keys.B))
{
arrhenius.B = object[validation::keys.B].get<double>();
}
if (object.contains(validation::keys.C))
{
arrhenius.C = object[validation::keys.C].get<double>();
}
if (object.contains(validation::keys.D))
{
arrhenius.D = object[validation::keys.D].get<double>();
}
if (object.contains(validation::keys.E))
{
arrhenius.E = object[validation::keys.E].get<double>();
}
if (object.contains(validation::keys.Ea))
{
if (arrhenius.C != 0)
{
std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl;
status = ConfigParseStatus::MutuallyExclusiveOption;
}
// Calculate 'C' using 'Ea'
arrhenius.C = -1 * object[validation::keys.Ea].get<double>() / constants::boltzmann;
}

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

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

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

return { status, arrhenius };
}

std::pair<ConfigParseStatus, types::Reactions> ParseReactions(const json& objects, const std::vector<types::Species> existing_species)
{
ConfigParseStatus status = ConfigParseStatus::Success;
types::Reactions reactions;

for (const auto& object : objects)
{
std::string type = object[validation::keys.type].get<std::string>();
if (type == validation::keys.Arrhenius_key)
{
auto arrhenius_parse = ParseArrhenius(object, existing_species);
if (arrhenius_parse.first != ConfigParseStatus::Success)
{
break;
}
reactions.arrhenius.push_back(arrhenius_parse.second);
}
}

return { status, reactions };
}

std::pair<ConfigParseStatus, types::Mechanism> JsonParser::Parse(const std::string& file_path)
{
return JsonParser::Parse(std::filesystem::path(file_path));
Expand Down Expand Up @@ -321,7 +454,7 @@ namespace open_atmos
mechanism.name = name;

// parse all of the species at once
auto species_parsing = ParseSpecies(object["species"]);
auto species_parsing = ParseSpecies(object[validation::keys.species]);

if (species_parsing.first != ConfigParseStatus::Success)
{
Expand All @@ -331,7 +464,7 @@ namespace open_atmos
}

// parse all of the phases at once
auto phases_parsing = ParsePhases(object["phases"], species_parsing.second);
auto phases_parsing = ParsePhases(object[validation::keys.phases], species_parsing.second);

if (phases_parsing.first != ConfigParseStatus::Success)
{
Expand All @@ -340,8 +473,19 @@ namespace open_atmos
std::cerr << "[" << msg << "] Failed to parse the phases." << std::endl;
}

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

if (reactions_parsing.first != ConfigParseStatus::Success)
{
status = reactions_parsing.first;
std::string msg = configParseStatusToString(status);
std::cerr << "[" << msg << "] Failed to parse the reactions." << std::endl;
}

mechanism.species = species_parsing.second;
mechanism.phases = phases_parsing.second;
mechanism.reactions = reactions_parsing.second;

return { status, mechanism };
}
Expand Down
5 changes: 4 additions & 1 deletion test/integration/test_json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

using namespace open_atmos::mechanism_configuration;

TEST(JsonParser, Returns)
TEST(JsonParser, ParsesFullConfiguration)
{
JsonParser parser;
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.reactions.arrhenius.size(), 1);
}
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,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)

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

#include <open_atmos/mechanism_configuration/parser.hpp>

using namespace open_atmos::mechanism_configuration;

TEST(JsonParser, CanParseValidArrheniusReaction)
{
JsonParser parser;
auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/valid.json"));

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

EXPECT_EQ(mechanism.reactions.arrhenius[0].name, "my arrhenius");
EXPECT_EQ(mechanism.reactions.arrhenius[0].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.arrhenius[0].A, 32.1);
EXPECT_EQ(mechanism.reactions.arrhenius[0].B, -2.3);
EXPECT_EQ(mechanism.reactions.arrhenius[0].C, 102.3);
EXPECT_EQ(mechanism.reactions.arrhenius[0].D, 63.4);
EXPECT_EQ(mechanism.reactions.arrhenius[0].E, -1.3);
EXPECT_EQ(mechanism.reactions.arrhenius[0].reactants.size(), 1);
EXPECT_EQ(mechanism.reactions.arrhenius[0].reactants[0].species_name, "A");
EXPECT_EQ(mechanism.reactions.arrhenius[0].reactants[0].coefficient, 1);
EXPECT_EQ(mechanism.reactions.arrhenius[0].products.size(), 2);
EXPECT_EQ(mechanism.reactions.arrhenius[0].products[0].species_name, "B");
EXPECT_EQ(mechanism.reactions.arrhenius[0].products[0].coefficient, 1.2);
EXPECT_EQ(mechanism.reactions.arrhenius[0].products[1].species_name, "C");
EXPECT_EQ(mechanism.reactions.arrhenius[0].products[1].coefficient, 0.3);
EXPECT_EQ(mechanism.reactions.arrhenius[0].unknown_properties.size(), 1);
EXPECT_EQ(mechanism.reactions.arrhenius[0].unknown_properties["__solver_param"], "0.1");

EXPECT_EQ(mechanism.reactions.arrhenius[1].name, "my arrhenius2");
EXPECT_EQ(mechanism.reactions.arrhenius[1].gas_phase, "gas");
EXPECT_EQ(mechanism.reactions.arrhenius[1].A, 3.1);
EXPECT_EQ(mechanism.reactions.arrhenius[1].B, -0.3);
EXPECT_EQ(mechanism.reactions.arrhenius[1].C, 12.3);
EXPECT_EQ(mechanism.reactions.arrhenius[1].D, 6.4);
EXPECT_EQ(mechanism.reactions.arrhenius[1].E, -0.3);
EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants.size(), 2);
EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[0].species_name, "A");
EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[0].coefficient, 2);
EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[1].species_name, "B");
EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[1].coefficient, 0.1);

EXPECT_EQ(mechanism.reactions.arrhenius[1].products.size(), 1);
EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].species_name, "C");
EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].coefficient, 0.5);
EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].unknown_properties.size(), 1);
EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].unknown_properties["__optional thing"], "\"hello\"");
}
Loading

0 comments on commit f8c2b54

Please sign in to comment.