Skip to content

Commit

Permalink
preventing nonstandard keys and adding tests for troe and arrhenius
Browse files Browse the repository at this point in the history
  • Loading branch information
K20shores committed Oct 16, 2023
1 parent 0164cea commit 59416ae
Show file tree
Hide file tree
Showing 62 changed files with 1,118 additions and 171 deletions.
86 changes: 44 additions & 42 deletions include/micm/configure/solver_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ namespace micm
InvalidMechanism,
ObjectTypeNotFound,
RequiredKeyNotFound,
ContainsNonStandardKey
ContainsNonStandardKey,
MutuallyExclusiveOption
};

inline std::string configParseStatusToString(const ConfigParseStatus& status)
Expand All @@ -61,6 +62,7 @@ namespace micm
case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound";
case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound";
case ConfigParseStatus::ContainsNonStandardKey: return "ContainsNonStandardKey";
case ConfigParseStatus::MutuallyExclusiveOption: return "MutuallyExclusiveOption";
default: return "Unknown";
}
}
Expand Down Expand Up @@ -416,10 +418,9 @@ namespace micm
const std::string PRODUCTS = "products";
const std::string MUSICA_NAME = "MUSICA name";

for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;
auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {});
if (status != ConfigParseStatus::Success) {
return status;
}

auto reactants = ParseReactants(object[REACTANTS]);
Expand All @@ -441,11 +442,9 @@ namespace micm
const std::string REACTANTS = "reactants";
const std::string PRODUCTS = "products";

// Check required json objects exist
for (const auto& key : { REACTANTS, PRODUCTS })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;
auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea" });
if (status != ConfigParseStatus::Success) {
return status;
}

auto reactants = ParseReactants(object[REACTANTS]);
Expand Down Expand Up @@ -474,6 +473,10 @@ namespace micm
}
if (object.contains("Ea"))
{
if (parameters.C_ != 0) {
std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl;
return ConfigParseStatus::MutuallyExclusiveOption;
}
// Calculate 'C' using 'Ea'
parameters.C_ = -1 * object["Ea"].get<double>() / BOLTZMANN_CONSTANT;
}
Expand All @@ -497,11 +500,10 @@ namespace micm
const std::string A0 = "a0";
const std::string N = "n";

// Check required json objects exist
for (const auto& key : { REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;

auto status = ValidateSchema(object, {"type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {});
if (status != ConfigParseStatus::Success) {
return status;
}

auto reactants = ParseReactants(object[REACTANTS]);
Expand Down Expand Up @@ -534,11 +536,9 @@ namespace micm
const std::string REACTANTS = "reactants";
const std::string PRODUCTS = "products";

// Check required json objects exist
for (const auto& key : { REACTANTS, PRODUCTS })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;
auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, {"k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N"});
if (status != ConfigParseStatus::Success) {
return status;
}

auto reactants = ParseReactants(object[REACTANTS]);
Expand Down Expand Up @@ -592,6 +592,11 @@ namespace micm
const std::string REACTANTS = "reactants";
const std::string PRODUCTS = "products";

auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, {"k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N"});
if (status != ConfigParseStatus::Success) {
return status;
}

// Check required json objects exist
for (const auto& key : { REACTANTS, PRODUCTS })
{
Expand Down Expand Up @@ -651,11 +656,9 @@ namespace micm
const std::string REACTANTS = "reactants";
const std::string PRODUCTS = "products";

// Check required json objects exist
for (const auto& key : { REACTANTS, PRODUCTS })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;
auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, {"A", "B", "C"});
if (status != ConfigParseStatus::Success) {
return status;
}

auto reactants = ParseReactants(object[REACTANTS]);
Expand Down Expand Up @@ -688,10 +691,10 @@ namespace micm
{
const std::string SPECIES = "species";
const std::string MUSICA_NAME = "MUSICA name";
for (const auto& key : { SPECIES, MUSICA_NAME })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;

auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {});
if (status != ConfigParseStatus::Success) {
return status;
}

std::string species = object["species"].get<std::string>();
Expand All @@ -716,10 +719,10 @@ namespace micm
{
const std::string SPECIES = "species";
const std::string MUSICA_NAME = "MUSICA name";
for (const auto& key : { SPECIES, MUSICA_NAME })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;

auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {});
if (status != ConfigParseStatus::Success) {
return status;
}

std::string species = object["species"].get<std::string>();
Expand Down Expand Up @@ -747,10 +750,7 @@ namespace micm
const std::string MUSICA_NAME = "MUSICA name";
const std::string PROBABILITY = "reaction probability";

std::vector<std::string> object_keys;
for (auto& [key, value] : object.items())
object_keys.push_back(key);
auto status = ValidateSchema(object_keys, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {PROBABILITY});
auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {PROBABILITY});
if (status != ConfigParseStatus::Success) {
return status;
}
Expand Down Expand Up @@ -786,20 +786,22 @@ namespace micm
return ConfigParseStatus::Success;
}


/// @brief Search for nonstandard keys. Only nonstandard keys starting with __ are allowed. Others are considered typos
/// @param object_keys the keys of the object
/// @param required_keys The required keys
/// @param optional_keys The optional keys
/// @return true if only standard keys are found
ConfigParseStatus ValidateSchema(const std::vector<std::string>& object_keys, const std::vector<std::string>& required_keys, const std::vector<std::string>& optional_keys) {
ConfigParseStatus ValidateSchema(const json& object, const std::vector<std::string>& required_keys, const std::vector<std::string>& optional_keys) {
// standard keys are:
// those in required keys
// those in optional keys
// starting with __
// anything else is reported as an error so that typos are caught, specifically for optional keys

auto sorted_object_keys = object_keys;
std::vector<std::string> sorted_object_keys;
for (auto& [key, value] : object.items())
sorted_object_keys.push_back(key);

auto sorted_required_keys = required_keys;
auto sorted_optional_keys = optional_keys;
std::sort(sorted_object_keys.begin(), sorted_object_keys.end());
Expand All @@ -813,12 +815,12 @@ namespace micm
std::set_difference(sorted_object_keys.begin(), sorted_object_keys.end(), sorted_required_keys.begin(), sorted_required_keys.end(), std::back_inserter(difference));

// check that the number of keys remaining is exactly equal to the expected number of required keys
if (difference.size() != (object_keys.size() - required_keys.size())) {
if (difference.size() != (sorted_object_keys.size() - required_keys.size())) {
return ConfigParseStatus::RequiredKeyNotFound;
}

std::vector<std::string> remaining;
std::set_difference(difference.begin(), difference.end(), optional_keys.begin(), optional_keys.end(), std::back_inserter(remaining));
std::set_difference(difference.begin(), difference.end(), sorted_optional_keys.begin(), sorted_optional_keys.end(), std::back_inserter(remaining));

// now, anything left must be standard comment starting with __
for(auto& key : remaining)
Expand Down
2 changes: 2 additions & 0 deletions test/unit/configure/process/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ include(test_util)
################################################################################
# Tests

create_standard_test(NAME arrhenius_config SOURCES test_arrhenius_config.cpp)
create_standard_test(NAME branched_config SOURCES test_branched_config.cpp)
create_standard_test(NAME emission_config SOURCES test_emission_config.cpp)
create_standard_test(NAME first_order_loss_config SOURCES test_first_order_loss_config.cpp)
create_standard_test(NAME photolysis_config SOURCES test_photolysis_config.cpp)
create_standard_test(NAME surface_config SOURCES test_surface_config.cpp)
create_standard_test(NAME ternary_chemical_activation_config SOURCES test_ternary_chemical_activation_config.cpp)
create_standard_test(NAME troe_config SOURCES test_troe_config.cpp)
create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp)
98 changes: 98 additions & 0 deletions test/unit/configure/process/test_arrhenius_config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#include <gtest/gtest.h>

#include <micm/configure/solver_config.hpp>

TEST(Arrhenius, DetectsInvalidConfig)
{
micm::SolverConfig solver_config;

// Read and parse the configure files
micm::ConfigParseStatus status =
solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_reactants");
EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status);
status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_products");
EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status);
status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/mutually_exclusive");
EXPECT_EQ(micm::ConfigParseStatus::MutuallyExclusiveOption, status);
}

TEST(Arrhenius, ParseConfig)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/valid");
EXPECT_EQ(micm::ConfigParseStatus::Success, status);

micm::SolverParameters solver_params = solver_config.GetSolverParams();

auto& process_vector = solver_params.processes_;

// first reaction
{
EXPECT_EQ(process_vector[0].reactants_.size(), 3);
EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo");
EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz");
EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz");
EXPECT_EQ(process_vector[0].products_.size(), 2);
EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar");
EXPECT_EQ(process_vector[0].products_[0].second, 1.0);
EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz");
EXPECT_EQ(process_vector[0].products_[1].second, 3.2);
micm::ArrheniusRateConstant* ternary_rate_constant =
dynamic_cast<micm::ArrheniusRateConstant*>(process_vector[0].rate_constant_.get());
auto& params = ternary_rate_constant->parameters_;
EXPECT_EQ(params.A_, 1.0);
EXPECT_EQ(params.B_, 0.0);
EXPECT_EQ(params.C_, 0.0);
EXPECT_EQ(params.D_, 300);
EXPECT_EQ(params.E_, 0.0);
}

// second reaction
{
EXPECT_EQ(process_vector[1].reactants_.size(), 2);
EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar");
EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz");
EXPECT_EQ(process_vector[1].products_.size(), 2);
EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar");
EXPECT_EQ(process_vector[1].products_[0].second, 0.5);
EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo");
EXPECT_EQ(process_vector[1].products_[1].second, 1.0);
micm::ArrheniusRateConstant* ternary_rate_constant =
dynamic_cast<micm::ArrheniusRateConstant*>(process_vector[1].rate_constant_.get());
auto& params = ternary_rate_constant->parameters_;
EXPECT_EQ(params.A_, 32.1);
EXPECT_EQ(params.B_, -2.3);
EXPECT_EQ(params.C_, 102.3);
EXPECT_EQ(params.D_, 63.4);
EXPECT_EQ(params.E_, -1.3);
}

// third reaction
{
EXPECT_EQ(process_vector[2].reactants_.size(), 2);
EXPECT_EQ(process_vector[2].reactants_[0].name_, "bar");
EXPECT_EQ(process_vector[2].reactants_[1].name_, "baz");
EXPECT_EQ(process_vector[2].products_.size(), 2);
EXPECT_EQ(process_vector[2].products_[0].first.name_, "bar");
EXPECT_EQ(process_vector[2].products_[0].second, 0.5);
EXPECT_EQ(process_vector[2].products_[1].first.name_, "foo");
EXPECT_EQ(process_vector[2].products_[1].second, 1.0);
micm::ArrheniusRateConstant* ternary_rate_constant =
dynamic_cast<micm::ArrheniusRateConstant*>(process_vector[2].rate_constant_.get());
auto& params = ternary_rate_constant->parameters_;
EXPECT_EQ(params.A_, 32.1);
EXPECT_EQ(params.B_, -2.3);
EXPECT_EQ(params.C_, -1 * 102.3 / BOLTZMANN_CONSTANT);
EXPECT_EQ(params.D_, 63.4);
EXPECT_EQ(params.E_, -1.3);
}
}

TEST(Arrhenius, DetectsNonstandardKeys)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
8 changes: 8 additions & 0 deletions test/unit/configure/process/test_branched_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,11 @@ TEST(BranchedConfig, ParseConfig)
EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Nitrate);
}
}

TEST(BranchedConfig, DetectsNonstandardKeys)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
8 changes: 8 additions & 0 deletions test/unit/configure/process/test_emission_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,12 @@ TEST(EmissionConfig, ParseConfig)
EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1);
EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.bar");
}
}

TEST(EmissionConfig, DetectsNonstandardKeys)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
10 changes: 9 additions & 1 deletion test/unit/configure/process/test_first_order_loss_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@ TEST(FirstOrderLossConfig, ParseConfig)
EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1);
EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.bar");
}
}
}

TEST(FirstOrderLossConfig, DetectsNonstandardKeys)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
8 changes: 8 additions & 0 deletions test/unit/configure/process/test_photolysis_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,12 @@ TEST(PhotolysisConfig, ParseConfig)
EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1);
EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jbar");
}
}

TEST(PhotolysisConfig, DetectsNonstandardKeys)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,11 @@ TEST(TernaryChemicalActivationConfig, ParseConfig)
EXPECT_EQ(params.N_, 32.1);
}
}

TEST(TernaryChemicalActivationConfig, DetectsNonstandardKeys)
{
micm::SolverConfig solver_config;

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
Loading

0 comments on commit 59416ae

Please sign in to comment.