Skip to content

Commit

Permalink
adding a schema validator
Browse files Browse the repository at this point in the history
  • Loading branch information
K20shores committed Oct 16, 2023
1 parent 88d6b5b commit 0164cea
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 132 deletions.
59 changes: 54 additions & 5 deletions include/micm/configure/solver_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ namespace micm
CAMPDataSectionNotFound,
InvalidMechanism,
ObjectTypeNotFound,
RequiredKeyNotFound
RequiredKeyNotFound,
ContainsNonStandardKey
};

inline std::string configParseStatusToString(const ConfigParseStatus& status)
Expand All @@ -59,6 +60,7 @@ namespace micm
case ConfigParseStatus::InvalidMechanism: return "InvalidMechanism";
case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound";
case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound";
case ConfigParseStatus::ContainsNonStandardKey: return "ContainsNonStandardKey";
default: return "Unknown";
}
}
Expand Down Expand Up @@ -744,10 +746,13 @@ namespace micm
const std::string PRODUCTS = "gas-phase products";
const std::string MUSICA_NAME = "MUSICA name";
const std::string PROBABILITY = "reaction probability";
for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME })
{
if (!ValidateJsonWithKey(object, key))
return ConfigParseStatus::RequiredKeyNotFound;

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});
if (status != ConfigParseStatus::Success) {
return status;
}

std::string species_name = object[REACTANTS].get<std::string>();
Expand Down Expand Up @@ -780,6 +785,50 @@ 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) {
// 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;
auto sorted_required_keys = required_keys;
auto sorted_optional_keys = optional_keys;
std::sort(sorted_object_keys.begin(), sorted_object_keys.end());
std::sort(sorted_required_keys.begin(), sorted_required_keys.end());
std::sort(sorted_optional_keys.begin(), sorted_optional_keys.end());


// get the difference between the object keys and those required
// what's left should be the optional keys and valid comments
std::vector<std::string> difference;
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())) {
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));

// now, anything left must be standard comment starting with __
for(auto& key : remaining)
{
if (!key.starts_with("__")) {
return ConfigParseStatus::ContainsNonStandardKey;
}
}
return ConfigParseStatus::Success;
}
};

/// @brief Public interface to read and parse config
Expand Down
9 changes: 9 additions & 0 deletions test/unit/configure/process/test_surface_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,12 @@ TEST(SurfaceConfig, ParseConfig)
EXPECT_EQ(surface_rate_constant->mean_free_speed_factor_, 8.0 * GAS_CONSTANT / (M_PI * 0.321));
}
}


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

micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/contains_nonstandard_key");
EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status);
}
87 changes: 0 additions & 87 deletions test/unit/unit_configs/MZ326/reactions.json

This file was deleted.

40 changes: 0 additions & 40 deletions test/unit/unit_configs/MZ326/species.json

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"camp-data": [
{
"name": "Valid surface reactions",
"type": "MECHANISM",
"reactions": [
{
"type": "SURFACE",
"gas-phase reactant": "foo",
"gas-phase products": {
"bar": { "yield": 1.0 },
"baz": { "yield": 3.2 }
},
"MUSICA name": "kfoo",
"mUSICA name": "kfoo"
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"camp-data": [
{
"name": "foo",
"type": "CHEM_SPEC",
"molecular weight [kg mol-1]" : 0.123,
"diffusion coefficient [m2 s-1]" : 2.3e-4
},
{
"name": "bar",
"type": "CHEM_SPEC",
"molecular weight [kg mol-1]" : 0.321,
"diffusion coefficient [m2 s-1]" : 0.4e-5
},
{
"name": "baz",
"type": "CHEM_SPEC"
},
{
"name": "quz",
"type": "CHEM_SPEC"
}
]
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"type": "MECHANISM",
"reactions": [
{
"__my comment": "asdf",
"type": "SURFACE",
"gas-phase reactant": "foo",
"gas-phase products": {
Expand Down

0 comments on commit 0164cea

Please sign in to comment.