From 914aa1fd7555e4ac6e6086eaa3881fc17ead0e89 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 23 Jan 2024 17:23:39 -0600 Subject: [PATCH 1/4] removing much code duplication --- src/parser.cpp | 2810 +++++++++++-------------- test/integration/test_json_parser.cpp | 10 +- 2 files changed, 1232 insertions(+), 1588 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index bdde5c3..9cf1f00 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2,1826 +2,1470 @@ // // SPDX-License-Identifier: Apache-2.0 +#include #include #include #include #include -namespace open_atmos +using nlohmann::json; +using namespace open_atmos; +using namespace open_atmos::mechanism_configuration; + +class IReactionParser { - namespace mechanism_configuration + public: + /// @brief Parses a reaction + /// @param object A json object + /// @param existing_species A list of species configured in a mechanism + /// @param existing_phases A list of phases configured in a mechanism + /// @return A pair indicating parsing success and a struct of Condensed Phase Arrhenius parameters + virtual ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + types::Reactions& reactions) = 0; + virtual ~IReactionParser() = default; +}; + +/// @brief Finds all keys that start with "__" +/// @param object A json object +/// @param required_keys for a type +/// @param optional_keys for a type +/// @return a dictionary of comments w +std::unordered_map +GetComments(const json& object, const std::vector& required_keys, const std::vector& 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 + + std::vector 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()); + 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 difference; + std::set_difference( + sorted_object_keys.begin(), sorted_object_keys.end(), sorted_required_keys.begin(), sorted_required_keys.end(), std::back_inserter(difference)); + + std::vector remaining; + std::set_difference(difference.begin(), difference.end(), sorted_optional_keys.begin(), sorted_optional_keys.end(), std::back_inserter(remaining)); + + std::unordered_map unknown_properties; + for (const auto& key : remaining) { - using nlohmann::json; + std::string val = object[key].dump(); + unknown_properties[key] = val; + } + return unknown_properties; +} + +/// @brief Search for nonstandard keys. Only nonstandard keys starting with __ are allowed. Others are considered typos +/// @param object the object whose keys need to be validated +/// @param required_keys The required keys +/// @param optional_keys The optional keys +/// @return true if only standard keys are found +ConfigParseStatus ValidateSchema(const json& object, const std::vector& required_keys, const std::vector& 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 - std::string configParseStatusToString(const ConfigParseStatus& status) + // debug statement + // std::cout << "ValidateSchema object " << object.dump(4) << std::endl; + + if (!object.empty() && object.begin().value().is_null()) + { + return ConfigParseStatus::Success; + } + + std::vector 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()); + 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 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() != (sorted_object_keys.size() - required_keys.size())) + { + std::vector missing_keys; + std::set_difference( + sorted_required_keys.begin(), + sorted_required_keys.end(), + sorted_object_keys.begin(), + sorted_object_keys.end(), + std::back_inserter(missing_keys)); + for (auto& key : missing_keys) + std::cerr << "Missing required key '" << key << "' in object: " << object << std::endl; + + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::vector 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) + { + if (key.find("__") == std::string::npos) { - switch (status) + std::cerr << "Non-standard key '" << key << "' found in object" << object << std::endl; + + return ConfigParseStatus::InvalidKey; + } + } + return ConfigParseStatus::Success; +} + +template +bool ContainsUniqueObjectsByName(const std::vector& collection) +{ + for (size_t i = 0; i < collection.size(); ++i) + { + for (size_t j = i + 1; j < collection.size(); ++j) + { + if (collection[i].name == collection[j].name) { - case ConfigParseStatus::Success: return "Success"; - case ConfigParseStatus::None: return "None"; - case ConfigParseStatus::InvalidKey: return "InvalidKey"; - case ConfigParseStatus::UnknownKey: return "UnknownKey"; - case ConfigParseStatus::InvalidFilePath: return "InvalidFilePath"; - case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; - case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound"; - case ConfigParseStatus::MutuallyExclusiveOption: return "MutuallyExclusiveOption"; - case ConfigParseStatus::DuplicateSpeciesDetected: return "DuplicateSpeciesDetected"; - case ConfigParseStatus::DuplicatePhasesDetected: return "DuplicatePhasesDetected"; - case ConfigParseStatus::PhaseRequiresUnknownSpecies: return "PhaseRequiresUnknownSpecies"; - case ConfigParseStatus::ReactionRequiresUnknownSpecies: return "ReactionRequiresUnknownSpecies"; - case ConfigParseStatus::UnknownPhase: return "UnknownPhase"; - case ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase: return "RequestedAerosolSpeciesNotIncludedInAerosolPhase"; - case ConfigParseStatus::TooManyReactionComponents: return "TooManyReactionComponents"; - case ConfigParseStatus::InvalidIonPair: return "InvalidIonPair"; - default: return "Unknown"; + return false; } } + } + return true; +} + +/// @brief Check if any species in required by a reaction is not in the species configured for this mechanism +/// @param requested_species a list of species needed by a reaction +/// @param existing_species all configured species in the mechanism +/// @return a boolean indicating if this reaction needs species that are not configured in the mechanism +template +bool RequiresUnknownSpecies(const std::vector& requested_species, const std::vector& existing_species) +{ + for (const auto& spec : requested_species) + { + auto it = std::find_if( + existing_species.begin(), + existing_species.end(), + [&spec](const auto& existing) + { + if constexpr (std::is_same::value) + { + return existing == spec; + } + else + { + return existing.name == spec; + } + }); - // Returns a vector for the allowed nonstandard keys, those that start with two underscores, like "__absolute tolerance" - std::vector - GetComments(const json& object, const std::vector& required_keys, const std::vector& optional_keys) + if (it == existing_species.end()) { - // 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 - - std::vector 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()); - 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 difference; - std::set_difference( - sorted_object_keys.begin(), - sorted_object_keys.end(), - sorted_required_keys.begin(), - sorted_required_keys.end(), - std::back_inserter(difference)); - - std::vector remaining; - std::set_difference( - difference.begin(), difference.end(), sorted_optional_keys.begin(), sorted_optional_keys.end(), std::back_inserter(remaining)); - - return remaining; + return true; } + } + return false; +} + +/// @brief Parses species in a mechanism +/// @param objects json object of species +/// @return A pair indicating parsing success and a list of species +std::pair> ParseSpecies(const json& objects) +{ + ConfigParseStatus status = ConfigParseStatus::Success; + std::vector all_species; - /// @brief Search for nonstandard keys. Only nonstandard keys starting with __ are allowed. Others are considered typos - /// @param object the object whose keys need to be validated - /// @param required_keys The required keys - /// @param optional_keys The optional keys - /// @return true if only standard keys are found - ConfigParseStatus ValidateSchema(const json& object, const std::vector& required_keys, const std::vector& optional_keys) + for (const auto& object : objects) + { + types::Species species; + status = ValidateSchema(object, validation::species.required_keys, validation::species.optional_keys); + if (status != ConfigParseStatus::Success) { - // 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 + break; + } - // debug statement - // std::cout << "ValidateSchema object " << object.dump(4) << std::endl; + std::string name = object[validation::keys.name].get(); - if (!object.empty() && object.begin().value().is_null()) + std::map numerical_properties{}; + for (const auto& key : validation::species.optional_keys) + { + if (object.contains(key)) { - return ConfigParseStatus::Success; + double val = object[key].get(); + numerical_properties[key] = val; } + } - std::vector sorted_object_keys; - for (auto& [key, value] : object.items()) - sorted_object_keys.push_back(key); + species.name = name; + species.optional_numerical_properties = numerical_properties; + species.unknown_properties = GetComments(object, validation::species.required_keys, validation::species.optional_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()); + all_species.push_back(species); + } - // get the difference between the object keys and those required - // what's left should be the optional keys and valid comments - std::vector difference; - std::set_difference( - sorted_object_keys.begin(), - sorted_object_keys.end(), - sorted_required_keys.begin(), - sorted_required_keys.end(), - std::back_inserter(difference)); + if (!ContainsUniqueObjectsByName(all_species)) + status = ConfigParseStatus::DuplicateSpeciesDetected; - // check that the number of keys remaining is exactly equal to the expected number of required keys - if (difference.size() != (sorted_object_keys.size() - required_keys.size())) - { - std::vector missing_keys; - std::set_difference( - sorted_required_keys.begin(), - sorted_required_keys.end(), - sorted_object_keys.begin(), - sorted_object_keys.end(), - std::back_inserter(missing_keys)); - for (auto& key : missing_keys) - std::cerr << "Missing required key '" << key << "' in object: " << object << std::endl; + return { status, all_species }; +} - return ConfigParseStatus::RequiredKeyNotFound; - } +/// @brief Parses phases in a mechanism +/// @param objects jsoon object of phases +/// @param existing_species a list of species configured in the mechanism +/// @return A pair indicating parsing success and a list of phases +std::pair> ParsePhases(const json& objects, const std::vector existing_species) +{ + ConfigParseStatus status = ConfigParseStatus::Success; + std::vector all_phases; - std::vector remaining; - std::set_difference( - difference.begin(), difference.end(), sorted_optional_keys.begin(), sorted_optional_keys.end(), std::back_inserter(remaining)); + for (const auto& object : objects) + { + types::Phase phase; + status = ValidateSchema(object, validation::phase.required_keys, validation::phase.optional_keys); + if (status != ConfigParseStatus::Success) + { + break; + } - // now, anything left must be standard comment starting with __ - for (auto& key : remaining) - { - if (key.find("__") == std::string::npos) - { - std::cerr << "Non-standard key '" << key << "' found in object" << object << std::endl; + std::string name = object[validation::keys.name].get(); - return ConfigParseStatus::InvalidKey; - } - } - return ConfigParseStatus::Success; + std::vector species{}; + for (const auto& spec : object[validation::keys.species]) + { + species.push_back(spec); + } + + phase.name = name; + phase.species = species; + phase.unknown_properties = GetComments(object, validation::phase.required_keys, validation::phase.optional_keys); + + if (RequiresUnknownSpecies(species, existing_species)) + { + status = ConfigParseStatus::PhaseRequiresUnknownSpecies; + break; + } + + all_phases.push_back(phase); + } + + if (status == ConfigParseStatus::Success && !ContainsUniqueObjectsByName(all_phases)) + status = ConfigParseStatus::DuplicatePhasesDetected; + + return { status, all_phases }; +} + +/// @brief Parses all reactions +/// @param object +/// @return A pair indicating parsing success and a struct of all reactions found in the mechanism +std::pair 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) + { + std::string species_name = object[validation::keys.species_name].get(); + double coefficient = 1; + if (object.contains(validation::keys.coefficient)) + { + coefficient = object[validation::keys.coefficient].get(); + } + + component.species_name = species_name; + component.coefficient = coefficient; + component.unknown_properties = GetComments(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys); + } + + return { status, component }; +} + +std::vector ParseReactantsOrProducts(const std::string& key, const json& object, ConfigParseStatus& status) +{ + std::vector result{}; + for (const auto& product : object[key]) + { + auto component_parse = ParseReactionComponent(product); + status = component_parse.first; + if (status != ConfigParseStatus::Success) + { + break; } + result.push_back(component_parse.second); + } + return result; +} + +class CondensedPhaseArrheniusParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::CondensedPhaseArrhenius condensed_phase_arrhenius; - template - bool ContainsUniqueObjectsByName(const std::vector& collection) + status = ValidateSchema(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.optional_keys); + if (status == ConfigParseStatus::Success) { - for (size_t i = 0; i < collection.size(); ++i) + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); + + if (object.contains(validation::keys.A)) + { + condensed_phase_arrhenius.A = object[validation::keys.A].get(); + } + if (object.contains(validation::keys.B)) + { + condensed_phase_arrhenius.B = object[validation::keys.B].get(); + } + if (object.contains(validation::keys.C)) + { + condensed_phase_arrhenius.C = object[validation::keys.C].get(); + } + if (object.contains(validation::keys.D)) { - for (size_t j = i + 1; j < collection.size(); ++j) + condensed_phase_arrhenius.D = object[validation::keys.D].get(); + } + if (object.contains(validation::keys.E)) + { + condensed_phase_arrhenius.E = object[validation::keys.E].get(); + } + if (object.contains(validation::keys.Ea)) + { + if (condensed_phase_arrhenius.C != 0) { - if (collection[i].name == collection[j].name) - { - return false; - } + std::cerr << "Ea is specified when C is also specified for an CondensedPhasecondensed_phase_arrhenius reaction. Pick one." << std::endl; + status = ConfigParseStatus::MutuallyExclusiveOption; } + // Calculate 'C' using 'Ea' + condensed_phase_arrhenius.C = -1 * object[validation::keys.Ea].get() / constants::boltzmann; } - return true; - } - /// @brief Check if any species in required by a reaction is not in the species configured for this mechanism - /// @param requested_species a list of species needed by a reaction - /// @param existing_species all configured species in the mechanism - /// @return a boolean indicating if this reaction needs species that are not configured in the mechanism - bool RequiresUnknownSpecies(const std::vector requested_species, const std::vector& existing_species) - { - for (const auto& spec : requested_species) + if (object.contains(validation::keys.name)) { - auto it = - std::find_if(existing_species.begin(), existing_species.end(), [&spec](const types::Species& existing) { return existing.name == spec; }); + condensed_phase_arrhenius.name = object[validation::keys.name].get(); + } - if (it == existing_species.end()) - { - return true; - } + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); + + std::vector requested_species; + for (const auto& spec : products) + { + requested_species.push_back(spec.species_name); } - return false; - } + for (const auto& spec : reactants) + { + requested_species.push_back(spec.species_name); + } + requested_species.push_back(aerosol_phase_water); - /// @brief Check if any species in required by a reaction is not in the species configured for this mechanism - /// @param requested_species a list of species needed by a reaction - /// @param existing_species all configured species in the mechanism - /// @return a boolean indicating if this reaction needs species that are not configured in the mechanism - bool RequiresUnknownSpecies(const std::vector requested_species, const std::vector& existing_species) - { - for (const auto& spec : requested_species) + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) { - auto it = std::find_if(existing_species.begin(), existing_species.end(), [&spec](const std::string& existing) { return existing == spec; }); + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } + + auto phase_it = std::find_if( + existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); - if (it == existing_species.end()) + if (phase_it != existing_phases.end()) + { + // check if all of the species for this reaction are actually in the aerosol phase + std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, aerosol_phase_species)) { - return true; + status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; } } - return false; + else + { + status = ConfigParseStatus::UnknownPhase; + } + + condensed_phase_arrhenius.aerosol_phase = aerosol_phase; + condensed_phase_arrhenius.aerosol_phase_water = aerosol_phase_water; + condensed_phase_arrhenius.products = products; + condensed_phase_arrhenius.reactants = reactants; + condensed_phase_arrhenius.unknown_properties = + GetComments(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.optional_keys); } - /// @brief Parses species in a mechanism - /// @param objects json object of species - /// @return A pair indicating parsing success and a list of species - std::pair> ParseSpecies(const json& objects) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - std::vector all_species; - - for (const auto& object : objects) - { - types::Species species; - status = ValidateSchema(object, validation::species.required_keys, validation::species.optional_keys); - if (status != ConfigParseStatus::Success) - { - break; - } + reactions.condensed_phase_arrhenius.push_back(condensed_phase_arrhenius); + } + return status; + } +}; - std::string name = object[validation::keys.name].get(); +class TroeParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Troe troe; - std::map numerical_properties{}; - for (const auto& key : validation::species.optional_keys) - { - if (object.contains(key)) - { - double val = object[key].get(); - numerical_properties[key] = val; - } - } + status = ValidateSchema(object, validation::troe.required_keys, validation::troe.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - auto comments = GetComments(object, validation::species.required_keys, validation::species.optional_keys); + if (object.contains(validation::keys.k0_A)) + { + troe.k0_A = object[validation::keys.k0_A].get(); + } + if (object.contains(validation::keys.k0_B)) + { + troe.k0_B = object[validation::keys.k0_B].get(); + } + if (object.contains(validation::keys.k0_C)) + { + troe.k0_C = object[validation::keys.k0_C].get(); + } + if (object.contains(validation::keys.kinf_A)) + { + troe.kinf_A = object[validation::keys.kinf_A].get(); + } + if (object.contains(validation::keys.kinf_B)) + { + troe.kinf_B = object[validation::keys.kinf_B].get(); + } + if (object.contains(validation::keys.kinf_C)) + { + troe.kinf_C = object[validation::keys.kinf_C].get(); + } + if (object.contains(validation::keys.Fc)) + { + troe.Fc = object[validation::keys.Fc].get(); + } + if (object.contains(validation::keys.N)) + { + troe.N = object[validation::keys.N].get(); + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + if (object.contains(validation::keys.name)) + { + troe.name = object[validation::keys.name].get(); + } - species.name = name; - species.optional_numerical_properties = numerical_properties; - species.unknown_properties = unknown_properties; + std::vector 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); + } - all_species.push_back(species); + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; } - if (!ContainsUniqueObjectsByName(all_species)) - status = ConfigParseStatus::DuplicateSpeciesDetected; + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } - return { status, all_species }; + troe.gas_phase = gas_phase; + troe.products = products; + troe.reactants = reactants; + troe.unknown_properties = GetComments(object, validation::troe.required_keys, validation::troe.optional_keys); } - /// @brief Parses phases in a mechanism - /// @param objects jsoon object of phases - /// @param existing_species a list of species configured in the mechanism - /// @return A pair indicating parsing success and a list of phases - std::pair> ParsePhases(const json& objects, const std::vector existing_species) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - std::vector all_phases; - - for (const auto& object : objects) - { - types::Phase phase; - status = ValidateSchema(object, validation::phase.required_keys, validation::phase.optional_keys); - if (status != ConfigParseStatus::Success) - { - break; - } + reactions.troe.push_back(troe); + } + return status; + } +}; - std::string name = object[validation::keys.name].get(); +class BranchedParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Branched branched; - std::vector species{}; - for (const auto& spec : object[validation::keys.species]) - { - species.push_back(spec); - } + status = ValidateSchema(object, validation::branched.required_keys, validation::branched.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto alkoxy_products = ParseReactantsOrProducts(validation::keys.alkoxy_products, object, status); + auto nitrate_products = ParseReactantsOrProducts(validation::keys.nitrate_products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - auto comments = GetComments(object, validation::phase.required_keys, validation::phase.optional_keys); + branched.X = object[validation::keys.X].get(); + branched.Y = object[validation::keys.Y].get(); + branched.a0 = object[validation::keys.a0].get(); + branched.n = object[validation::keys.n].get(); - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + if (object.contains(validation::keys.name)) + { + branched.name = object[validation::keys.name].get(); + } - phase.name = name; - phase.species = species; - phase.unknown_properties = unknown_properties; + std::vector requested_species; + for (const auto& spec : nitrate_products) + { + requested_species.push_back(spec.species_name); + } + for (const auto& spec : alkoxy_products) + { + requested_species.push_back(spec.species_name); + } + for (const auto& spec : reactants) + { + requested_species.push_back(spec.species_name); + } - if (RequiresUnknownSpecies(species, existing_species)) - { - status = ConfigParseStatus::PhaseRequiresUnknownSpecies; - break; - } + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - all_phases.push_back(phase); + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; } - if (status == ConfigParseStatus::Success && !ContainsUniqueObjectsByName(all_phases)) - status = ConfigParseStatus::DuplicatePhasesDetected; + branched.gas_phase = gas_phase; + branched.nitrate_products = nitrate_products; + branched.alkoxy_products = alkoxy_products; + branched.reactants = reactants; + branched.unknown_properties = GetComments(object, validation::branched.required_keys, validation::branched.optional_keys); + } - return { status, all_phases }; + if (status == ConfigParseStatus::Success) + { + reactions.branched.push_back(branched); } + return status; + } +}; + +class TunnelingParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Tunneling tunneling; - /// @brief Parses all reactions - /// @param object - /// @return A pair indicating parsing success and a struct of all reactions found in the mechanism - std::pair ParseReactionComponent(const json& object) + status = ValidateSchema(object, validation::tunneling.required_keys, validation::tunneling.optional_keys); + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::ReactionComponent component; + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - status = ValidateSchema(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys); - if (status == ConfigParseStatus::Success) + if (object.contains(validation::keys.A)) { - std::string species_name = object[validation::keys.species_name].get(); - double coefficient = 1; - if (object.contains(validation::keys.coefficient)) - { - coefficient = object[validation::keys.coefficient].get(); - } + tunneling.A = object[validation::keys.A].get(); + } + if (object.contains(validation::keys.B)) + { + tunneling.B = object[validation::keys.B].get(); + } + if (object.contains(validation::keys.C)) + { + tunneling.C = object[validation::keys.C].get(); + } - auto comments = GetComments(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys); + if (object.contains(validation::keys.name)) + { + tunneling.name = object[validation::keys.name].get(); + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + std::vector 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); + } - component.species_name = species_name; - component.coefficient = coefficient; - component.unknown_properties = unknown_properties; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } + + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; } - return { status, component }; + tunneling.gas_phase = gas_phase; + tunneling.products = products; + tunneling.reactants = reactants; + tunneling.unknown_properties = GetComments(object, validation::tunneling.required_keys, validation::tunneling.optional_keys); } - /// @brief Parses an arrhenius reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Arrhenius parameters - std::pair - ParseArrhenius(const json& object, const std::vector& existing_species, const std::vector existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Arrhenius arrhenius; - - status = ValidateSchema(object, validation::arrhenius.required_keys, validation::arrhenius.optional_keys); - if (status == ConfigParseStatus::Success) - { - std::vector 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 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); - } + reactions.tunneling.push_back(tunneling); + } + return status; + } +}; - if (object.contains(validation::keys.A)) - { - arrhenius.A = object[validation::keys.A].get(); - } - if (object.contains(validation::keys.B)) - { - arrhenius.B = object[validation::keys.B].get(); - } - if (object.contains(validation::keys.C)) - { - arrhenius.C = object[validation::keys.C].get(); - } - if (object.contains(validation::keys.D)) - { - arrhenius.D = object[validation::keys.D].get(); - } - if (object.contains(validation::keys.E)) - { - arrhenius.E = object[validation::keys.E].get(); - } - 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() / constants::boltzmann; - } +class SurfaceParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Surface surface; - if (object.contains(validation::keys.name)) - { - arrhenius.name = object[validation::keys.name].get(); - } + status = ValidateSchema(object, validation::surface.required_keys, validation::surface.optional_keys); + if (status == ConfigParseStatus::Success) + { + std::string gas_phase_species = object[validation::keys.gas_phase_species].get(); - auto comments = GetComments(object, validation::arrhenius.required_keys, validation::arrhenius.optional_keys); + auto products = ParseReactantsOrProducts(validation::keys.gas_phase_products, object, status); - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + if (object.contains(validation::keys.reaction_probability)) + { + surface.reaction_probability = object[validation::keys.reaction_probability].get(); + } - std::vector 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 (object.contains(validation::keys.name)) + { + surface.name = object[validation::keys.name].get(); + } - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } + std::vector requested_species; + for (const auto& spec : products) + { + requested_species.push_back(spec.species_name); + } + requested_species.push_back(gas_phase_species); - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - arrhenius.gas_phase = gas_phase; - arrhenius.products = products; - arrhenius.reactants = reactants; - arrhenius.unknown_properties = unknown_properties; + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + 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; } - return { status, arrhenius }; + surface.gas_phase = object[validation::keys.gas_phase].get(); + surface.aerosol_phase = aerosol_phase; + surface.gas_phase_products = products; + surface.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; + surface.unknown_properties = GetComments(object, validation::surface.required_keys, validation::surface.optional_keys); } - /// @brief Parses a condensed phase arrhenius reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Condensed Phase Arrhenius parameters - std::pair ParseCondensedPhaseArrhenius( - const json& object, - const std::vector& existing_species, - const std::vector& existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::CondensedPhaseArrhenius condensed_phase_arrhenius; + reactions.surface.push_back(surface); + } + return status; + } +}; - status = ValidateSchema(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.optional_keys); - if (status == ConfigParseStatus::Success) - { - std::vector 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); - } +class PhotolysisParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Photolysis photolysis; - std::vector 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); - } + status = ValidateSchema(object, validation::photolysis.required_keys, validation::photolysis.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - if (object.contains(validation::keys.A)) - { - condensed_phase_arrhenius.A = object[validation::keys.A].get(); - } - if (object.contains(validation::keys.B)) - { - condensed_phase_arrhenius.B = object[validation::keys.B].get(); - } - if (object.contains(validation::keys.C)) - { - condensed_phase_arrhenius.C = object[validation::keys.C].get(); - } - if (object.contains(validation::keys.D)) - { - condensed_phase_arrhenius.D = object[validation::keys.D].get(); - } - if (object.contains(validation::keys.E)) - { - condensed_phase_arrhenius.E = object[validation::keys.E].get(); - } - if (object.contains(validation::keys.Ea)) - { - if (condensed_phase_arrhenius.C != 0) - { - std::cerr << "Ea is specified when C is also specified for an CondensedPhasecondensed_phase_arrhenius reaction. Pick one." << std::endl; - status = ConfigParseStatus::MutuallyExclusiveOption; - } - // Calculate 'C' using 'Ea' - condensed_phase_arrhenius.C = -1 * object[validation::keys.Ea].get() / constants::boltzmann; - } + if (object.contains(validation::keys.scaling_factor)) + { + photolysis.scaling_factor = object[validation::keys.scaling_factor].get(); + } - if (object.contains(validation::keys.name)) - { - condensed_phase_arrhenius.name = object[validation::keys.name].get(); - } + if (object.contains(validation::keys.name)) + { + photolysis.name = object[validation::keys.name].get(); + } + + std::vector 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); + } - auto comments = GetComments(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.optional_keys); + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); + if (status == ConfigParseStatus::Success && reactants.size() > 1) + { + status = ConfigParseStatus::TooManyReactionComponents; + } - std::vector 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); - } - requested_species.push_back(aerosol_phase_water); + photolysis.gas_phase = gas_phase; + photolysis.products = products; + photolysis.reactants = reactants; + photolysis.unknown_properties = GetComments(object, validation::photolysis.required_keys, validation::photolysis.optional_keys); + } - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } + if (status == ConfigParseStatus::Success) + { + reactions.photolysis.push_back(photolysis); + } + return status; + } +}; - auto phase_it = std::find_if( - existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); +class CondensedPhasePhotolysisParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::CondensedPhasePhotolysis condensed_phase_photolysis; - if (phase_it != existing_phases.end()) - { - // check if all of the species for this reaction are actually in the aerosol phase - std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, aerosol_phase_species)) - { - status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; - } - } - else - { - status = ConfigParseStatus::UnknownPhase; - } + status = ValidateSchema(object, validation::condensed_phase_photolysis.required_keys, validation::photolysis.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - condensed_phase_arrhenius.aerosol_phase = aerosol_phase; - condensed_phase_arrhenius.aerosol_phase_water = aerosol_phase_water; - condensed_phase_arrhenius.products = products; - condensed_phase_arrhenius.reactants = reactants; - condensed_phase_arrhenius.unknown_properties = unknown_properties; + if (object.contains(validation::keys.scaling_factor)) + { + condensed_phase_photolysis.scaling_factor_ = object[validation::keys.scaling_factor].get(); } - return { status, condensed_phase_arrhenius }; - } + if (object.contains(validation::keys.name)) + { + condensed_phase_photolysis.name = object[validation::keys.name].get(); + } - /// @brief Parses a troe reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Troe parameters - std::pair - ParseTroe(const json& object, const std::vector& existing_species, const std::vector existing_phases) - { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Troe troe; + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); - status = ValidateSchema(object, validation::troe.required_keys, validation::troe.optional_keys); - if (status == ConfigParseStatus::Success) + std::vector requested_species; + for (const auto& spec : products) { - std::vector 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); - } + requested_species.push_back(spec.species_name); + } + for (const auto& spec : reactants) + { + requested_species.push_back(spec.species_name); + } + requested_species.push_back(aerosol_phase_water); - std::vector 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 (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - if (object.contains(validation::keys.k0_A)) - { - troe.k0_A = object[validation::keys.k0_A].get(); - } - if (object.contains(validation::keys.k0_B)) - { - troe.k0_B = object[validation::keys.k0_B].get(); - } - if (object.contains(validation::keys.k0_C)) - { - troe.k0_C = object[validation::keys.k0_C].get(); - } - if (object.contains(validation::keys.kinf_A)) - { - troe.kinf_A = object[validation::keys.kinf_A].get(); - } - if (object.contains(validation::keys.kinf_B)) - { - troe.kinf_B = object[validation::keys.kinf_B].get(); - } - if (object.contains(validation::keys.kinf_C)) - { - troe.kinf_C = object[validation::keys.kinf_C].get(); - } - if (object.contains(validation::keys.Fc)) - { - troe.Fc = object[validation::keys.Fc].get(); - } - if (object.contains(validation::keys.N)) - { - troe.N = object[validation::keys.N].get(); - } + if (status == ConfigParseStatus::Success && reactants.size() > 1) + { + status = ConfigParseStatus::TooManyReactionComponents; + } - if (object.contains(validation::keys.name)) + auto phase_it = std::find_if( + existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); + + if (phase_it != existing_phases.end()) + { + // check if all of the species for this reaction are actually in the aerosol phase + std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, aerosol_phase_species)) { - troe.name = object[validation::keys.name].get(); + status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; } + } + else + { + status = ConfigParseStatus::UnknownPhase; + } - auto comments = GetComments(object, validation::troe.required_keys, validation::troe.optional_keys); + condensed_phase_photolysis.aerosol_phase = aerosol_phase; + condensed_phase_photolysis.aerosol_phase_water = aerosol_phase_water; + condensed_phase_photolysis.products = products; + condensed_phase_photolysis.reactants = reactants; + condensed_phase_photolysis.unknown_properties = + GetComments(object, validation::condensed_phase_photolysis.required_keys, validation::photolysis.optional_keys); + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + if (status == ConfigParseStatus::Success) + { + reactions.condensed_phase_photolysis.push_back(condensed_phase_photolysis); + } + return status; + } +}; - std::vector 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); - } +class EmissionParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Emission emission; - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } + status = ValidateSchema(object, validation::emission.required_keys, validation::emission.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } + if (object.contains(validation::keys.scaling_factor)) + { + emission.scaling_factor = object[validation::keys.scaling_factor].get(); + } - troe.gas_phase = gas_phase; - troe.products = products; - troe.reactants = reactants; - troe.unknown_properties = unknown_properties; + if (object.contains(validation::keys.name)) + { + emission.name = object[validation::keys.name].get(); } - return { status, troe }; - } + std::vector requested_species; + for (const auto& spec : products) + { + requested_species.push_back(spec.species_name); + } - /// @brief Parses a branched reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Branched parameters - std::pair - ParseBranched(const json& object, const std::vector& existing_species, const std::vector existing_phases) - { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Branched branched; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - status = ValidateSchema(object, validation::branched.required_keys, validation::branched.optional_keys); - if (status == ConfigParseStatus::Success) + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) { - std::vector alkoxy_products{}; - for (const auto& product : object[validation::keys.alkoxy_products]) - { - auto product_parse = ParseReactionComponent(product); - status = product_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - alkoxy_products.push_back(product_parse.second); - } + status = ConfigParseStatus::UnknownPhase; + } - std::vector nitrate_products{}; - for (const auto& product : object[validation::keys.nitrate_products]) - { - auto product_parse = ParseReactionComponent(product); - status = product_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - nitrate_products.push_back(product_parse.second); - } + emission.gas_phase = gas_phase; + emission.products = products; + emission.unknown_properties = GetComments(object, validation::emission.required_keys, validation::emission.optional_keys); + } - std::vector 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 (status == ConfigParseStatus::Success) + { + reactions.emission.push_back(emission); + } + return status; + } +}; - branched.X = object[validation::keys.X].get(); - branched.Y = object[validation::keys.Y].get(); - branched.a0 = object[validation::keys.a0].get(); - branched.n = object[validation::keys.n].get(); +class FirstOrderLossParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::FirstOrderLoss first_order_loss; - if (object.contains(validation::keys.name)) - { - branched.name = object[validation::keys.name].get(); - } + status = ValidateSchema(object, validation::first_order_loss.required_keys, validation::first_order_loss.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - auto comments = GetComments(object, validation::branched.required_keys, validation::branched.optional_keys); + if (object.contains(validation::keys.scaling_factor)) + { + first_order_loss.scaling_factor = object[validation::keys.scaling_factor].get(); + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + if (object.contains(validation::keys.name)) + { + first_order_loss.name = object[validation::keys.name].get(); + } - std::vector requested_species; - for (const auto& spec : nitrate_products) - { - requested_species.push_back(spec.species_name); - } - for (const auto& spec : alkoxy_products) - { - requested_species.push_back(spec.species_name); - } - for (const auto& spec : reactants) - { - requested_species.push_back(spec.species_name); - } + std::vector requested_species; + for (const auto& spec : reactants) + { + requested_species.push_back(spec.species_name); + } - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } - branched.gas_phase = gas_phase; - branched.nitrate_products = nitrate_products; - branched.alkoxy_products = alkoxy_products; - branched.reactants = reactants; - branched.unknown_properties = unknown_properties; + if (status == ConfigParseStatus::Success && reactants.size() > 1) + { + status = ConfigParseStatus::TooManyReactionComponents; } - return { status, branched }; + first_order_loss.gas_phase = gas_phase; + first_order_loss.reactants = reactants; + first_order_loss.unknown_properties = + GetComments(object, validation::first_order_loss.required_keys, validation::first_order_loss.optional_keys); } - /// @brief Parses a tunneling reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Tunneling parameters - std::pair - ParseTunneling(const json& object, const std::vector& existing_species, const std::vector existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Tunneling tunneling; - - status = ValidateSchema(object, validation::tunneling.required_keys, validation::tunneling.optional_keys); - if (status == ConfigParseStatus::Success) - { - std::vector 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 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); - } + reactions.first_order_loss.push_back(first_order_loss); + } + return status; + } +}; - if (object.contains(validation::keys.A)) - { - tunneling.A = object[validation::keys.A].get(); - } - if (object.contains(validation::keys.B)) - { - tunneling.B = object[validation::keys.B].get(); - } - if (object.contains(validation::keys.C)) - { - tunneling.C = object[validation::keys.C].get(); - } +class SimpolPhaseTransferParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::SimpolPhaseTransfer simpol_phase_transfer; - if (object.contains(validation::keys.name)) - { - tunneling.name = object[validation::keys.name].get(); - } + status = ValidateSchema(object, validation::simpol_phase_transfer.required_keys, validation::simpol_phase_transfer.optional_keys); + if (status == ConfigParseStatus::Success) + { + std::string gas_phase_species = object[validation::keys.gas_phase_species].get(); + std::string aerosol_phase_species = object[validation::keys.aerosol_phase_species].get(); - auto comments = GetComments(object, validation::tunneling.required_keys, validation::tunneling.optional_keys); + if (object.contains(validation::keys.name)) + { + simpol_phase_transfer.name = object[validation::keys.name].get(); + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + std::vector requested_species{ gas_phase_species, aerosol_phase_species }; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - std::vector requested_species; - for (const auto& spec : products) - { - requested_species.push_back(spec.species_name); - } - for (const auto& spec : reactants) + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + auto aerosol_it = + std::find_if(existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const auto& phase) { return phase.name == aerosol_phase; }); + if (status == ConfigParseStatus::Success && aerosol_it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } + else + { + auto phase = *aerosol_it; + auto spec_it = std::find_if( + phase.species.begin(), + phase.species.end(), + [&aerosol_phase_species](const std::string& species) { return species == aerosol_phase_species; }); + if (spec_it == phase.species.end()) { - requested_species.push_back(spec.species_name); + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; } + } - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto gas_it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && gas_it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } + else + { + auto phase = *gas_it; + auto spec_it = std::find_if( + phase.species.begin(), phase.species.end(), [&gas_phase_species](const std::string& species) { return species == gas_phase_species; }); + if (spec_it == phase.species.end()) { status = ConfigParseStatus::ReactionRequiresUnknownSpecies; } + } - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) + if (object.contains(validation::keys.B) && object[validation::keys.B].is_array() && object[validation::keys.B].size() == 4) + { + for (size_t i = 0; i < 4; ++i) { - status = ConfigParseStatus::UnknownPhase; + simpol_phase_transfer.B[i] = object[validation::keys.B][i]; } - - tunneling.gas_phase = gas_phase; - tunneling.products = products; - tunneling.reactants = reactants; - tunneling.unknown_properties = unknown_properties; } - return { status, tunneling }; + simpol_phase_transfer.gas_phase = gas_phase; + simpol_phase_transfer.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; + simpol_phase_transfer.aerosol_phase = aerosol_phase; + simpol_phase_transfer.aerosol_phase_species = { .species_name = aerosol_phase_species, .coefficient = 1 }; + simpol_phase_transfer.unknown_properties = + GetComments(object, validation::simpol_phase_transfer.required_keys, validation::simpol_phase_transfer.optional_keys); } - /// @brief Parses a surface reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Surface parameters - std::pair - ParseSurface(const json& object, const std::vector& existing_species, const std::vector existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Surface surface; + reactions.simpol_phase_transfer.push_back(simpol_phase_transfer); + } + return status; + } +}; - status = ValidateSchema(object, validation::surface.required_keys, validation::surface.optional_keys); - if (status == ConfigParseStatus::Success) - { - std::string gas_phase_species = object[validation::keys.gas_phase_species].get(); +class AqueousEquilibriumParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::AqueousEquilibrium aqueous_equilibrium; - std::vector 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); - } + status = ValidateSchema(object, validation::aqueous_equilibrium.required_keys, validation::aqueous_equilibrium.optional_keys); + if (status == ConfigParseStatus::Success) + { + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - if (object.contains(validation::keys.reaction_probability)) - { - surface.reaction_probability = object[validation::keys.reaction_probability].get(); - } + if (object.contains(validation::keys.A)) + { + aqueous_equilibrium.A = object[validation::keys.A].get(); + } + if (object.contains(validation::keys.C)) + { + aqueous_equilibrium.C = object[validation::keys.C].get(); + } - if (object.contains(validation::keys.name)) - { - surface.name = object[validation::keys.name].get(); - } + aqueous_equilibrium.k_reverse = object[validation::keys.k_reverse].get(); - auto comments = GetComments(object, validation::surface.required_keys, validation::surface.optional_keys); + if (object.contains(validation::keys.name)) + { + aqueous_equilibrium.name = object[validation::keys.name].get(); + } - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); - std::vector requested_species; - for (const auto& spec : products) - { - requested_species.push_back(spec.species_name); - } - requested_species.push_back(gas_phase_species); + std::vector 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); + } + requested_species.push_back(aerosol_phase_water); - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - 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()) + auto phase_it = std::find_if( + existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); + + if (phase_it != existing_phases.end()) + { + // check if all of the species for this reaction are actually in the aerosol phase + std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, aerosol_phase_species)) { - status = ConfigParseStatus::UnknownPhase; + status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; } - - surface.gas_phase = object[validation::keys.gas_phase].get(); - surface.aerosol_phase = aerosol_phase; - surface.gas_phase_products = products; - surface.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; - surface.unknown_properties = unknown_properties; + } + else + { + status = ConfigParseStatus::UnknownPhase; } - return { status, surface }; + aqueous_equilibrium.aerosol_phase = aerosol_phase; + aqueous_equilibrium.aerosol_phase_water = aerosol_phase_water; + aqueous_equilibrium.products = products; + aqueous_equilibrium.reactants = reactants; + aqueous_equilibrium.unknown_properties = + GetComments(object, validation::aqueous_equilibrium.required_keys, validation::aqueous_equilibrium.optional_keys); } - /// @brief Parses a photolysis reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Photolysis parameters - std::pair - ParsePhotolysis(const json& object, const std::vector existing_species, const std::vector existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Photolysis photolysis; + reactions.aqueous_equilibrium.push_back(aqueous_equilibrium); + } + return status; + } +}; - status = ValidateSchema(object, validation::photolysis.required_keys, validation::photolysis.optional_keys); - if (status == ConfigParseStatus::Success) +class WetDepositionParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::WetDeposition wet_deposition; + + status = ValidateSchema(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); + if (status == ConfigParseStatus::Success) + { + if (object.contains(validation::keys.scaling_factor)) { - std::vector products{}; - for (const auto& reactant : object[validation::keys.products]) - { - auto product_parse = ParseReactionComponent(reactant); - status = product_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - products.push_back(product_parse.second); - } - - std::vector 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.scaling_factor)) - { - photolysis.scaling_factor = object[validation::keys.scaling_factor].get(); - } - - if (object.contains(validation::keys.name)) - { - photolysis.name = object[validation::keys.name].get(); - } - - auto comments = GetComments(object, validation::photolysis.required_keys, validation::photolysis.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } - - std::vector 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; - } - - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } - - if (status == ConfigParseStatus::Success && reactants.size() > 1) - { - status = ConfigParseStatus::TooManyReactionComponents; - } - - photolysis.gas_phase = gas_phase; - photolysis.products = products; - photolysis.reactants = reactants; - photolysis.unknown_properties = unknown_properties; + wet_deposition.scaling_factor = object[validation::keys.scaling_factor].get(); } - return { status, photolysis }; - } - - /// @brief Parses a photolysis reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Photolysis parameters - std::pair ParseCondensedPhasePhotolysis( - const json& object, - const std::vector existing_species, - const std::vector existing_phases) - { - ConfigParseStatus status = ConfigParseStatus::Success; - types::CondensedPhasePhotolysis condensed_phase_photolysis; - - status = ValidateSchema(object, validation::condensed_phase_photolysis.required_keys, validation::photolysis.optional_keys); - if (status == ConfigParseStatus::Success) + if (object.contains(validation::keys.name)) { - std::vector products{}; - for (const auto& reactant : object[validation::keys.products]) - { - auto product_parse = ParseReactionComponent(reactant); - status = product_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - products.push_back(product_parse.second); - } - - std::vector 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.scaling_factor)) - { - condensed_phase_photolysis.scaling_factor_ = object[validation::keys.scaling_factor].get(); - } - - if (object.contains(validation::keys.name)) - { - condensed_phase_photolysis.name = object[validation::keys.name].get(); - } - - auto comments = GetComments(object, validation::condensed_phase_photolysis.required_keys, validation::photolysis.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } - - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); - - std::vector 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); - } - requested_species.push_back(aerosol_phase_water); - - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - - if (status == ConfigParseStatus::Success && reactants.size() > 1) - { - status = ConfigParseStatus::TooManyReactionComponents; - } - - auto phase_it = std::find_if( - existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); - - if (phase_it != existing_phases.end()) - { - // check if all of the species for this reaction are actually in the aerosol phase - std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, aerosol_phase_species)) - { - status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; - } - } - else - { - status = ConfigParseStatus::UnknownPhase; - } - - condensed_phase_photolysis.aerosol_phase = aerosol_phase; - condensed_phase_photolysis.aerosol_phase_water = aerosol_phase_water; - condensed_phase_photolysis.products = products; - condensed_phase_photolysis.reactants = reactants; - condensed_phase_photolysis.unknown_properties = unknown_properties; + wet_deposition.name = object[validation::keys.name].get(); } - return { status, condensed_phase_photolysis }; - } + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - /// @brief Parses a emission reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Emission parameters - std::pair - ParseEmission(const json& object, const std::vector existing_species, const std::vector existing_phases) - { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Emission emission; - - status = ValidateSchema(object, validation::emission.required_keys, validation::emission.optional_keys); - if (status == ConfigParseStatus::Success) + // check if aerosol phase exists + 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()) { - std::vector 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); - } - - if (object.contains(validation::keys.scaling_factor)) - { - emission.scaling_factor = object[validation::keys.scaling_factor].get(); - } - - if (object.contains(validation::keys.name)) - { - emission.name = object[validation::keys.name].get(); - } - - auto comments = GetComments(object, validation::emission.required_keys, validation::emission.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } - - std::vector requested_species; - for (const auto& spec : products) - { - requested_species.push_back(spec.species_name); - } - - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } - - emission.gas_phase = gas_phase; - emission.products = products; - emission.unknown_properties = unknown_properties; + status = ConfigParseStatus::UnknownPhase; } - return { status, emission }; + wet_deposition.aerosol_phase = aerosol_phase; + wet_deposition.unknown_properties = GetComments(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); } - /// @brief Parses a first order loss reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of First Order Loss parameters - std::pair - ParseFirstOrderLoss(const json& object, const std::vector existing_species, const std::vector existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::FirstOrderLoss first_order_loss; - - status = ValidateSchema(object, validation::first_order_loss.required_keys, validation::first_order_loss.optional_keys); - if (status == ConfigParseStatus::Success) - { - std::vector 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.scaling_factor)) - { - first_order_loss.scaling_factor = object[validation::keys.scaling_factor].get(); - } - - if (object.contains(validation::keys.name)) - { - first_order_loss.name = object[validation::keys.name].get(); - } - - auto comments = GetComments(object, validation::first_order_loss.required_keys, validation::first_order_loss.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } - - std::vector requested_species; - for (const auto& spec : reactants) - { - requested_species.push_back(spec.species_name); - } - - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } - - if (status == ConfigParseStatus::Success && reactants.size() > 1) - { - status = ConfigParseStatus::TooManyReactionComponents; - } - - first_order_loss.gas_phase = gas_phase; - first_order_loss.reactants = reactants; - first_order_loss.unknown_properties = unknown_properties; - } - - return { status, first_order_loss }; + reactions.wet_deposition.push_back(wet_deposition); } + return status; + } +}; + +class HenrysLawParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::HenrysLaw henrys_law; - /// @brief Parses a SIMPOL phase transfer reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Surface parameters - std::pair - ParseSimpolPhaseTransfer(const json& object, const std::vector& existing_species, const std::vector existing_phases) + status = ValidateSchema(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys); + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::SimpolPhaseTransfer simpol_phase_transfer; + std::string gas_phase = object[validation::keys.gas_phase].get(); + std::string gas_phase_species = object[validation::keys.gas_phase_species].get(); + std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + std::string aerosol_phase_species = object[validation::keys.aerosol_phase_species].get(); + std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); - status = ValidateSchema(object, validation::simpol_phase_transfer.required_keys, validation::simpol_phase_transfer.optional_keys); - if (status == ConfigParseStatus::Success) + if (object.contains(validation::keys.name)) { - std::string gas_phase_species = object[validation::keys.gas_phase_species].get(); - std::string aerosol_phase_species = object[validation::keys.aerosol_phase_species].get(); - - if (object.contains(validation::keys.name)) - { - simpol_phase_transfer.name = object[validation::keys.name].get(); - } + henrys_law.name = object[validation::keys.name].get(); + } - auto comments = GetComments(object, validation::simpol_phase_transfer.required_keys, validation::simpol_phase_transfer.optional_keys); + std::vector requested_species; + requested_species.push_back(gas_phase_species); + requested_species.push_back(aerosol_phase_species); + requested_species.push_back(aerosol_phase_water); - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + std::vector requested_aerosol_species; + requested_aerosol_species.push_back(aerosol_phase_species); + requested_aerosol_species.push_back(aerosol_phase_water); - std::vector requested_species{ gas_phase_species, aerosol_phase_species }; - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - auto aerosol_it = - std::find_if(existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const auto& phase) { return phase.name == aerosol_phase; }); - if (status == ConfigParseStatus::Success && aerosol_it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } - else - { - auto phase = *aerosol_it; - auto spec_it = std::find_if( - phase.species.begin(), - phase.species.end(), - [&aerosol_phase_species](const std::string& species) { return species == aerosol_phase_species; }); - if (spec_it == phase.species.end()) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - } + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; + } - std::string gas_phase = object[validation::keys.gas_phase].get(); - auto gas_it = - std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && gas_it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } - else - { - auto phase = *gas_it; - auto spec_it = std::find_if( - phase.species.begin(), phase.species.end(), [&gas_phase_species](const std::string& species) { return species == gas_phase_species; }); - if (spec_it == phase.species.end()) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - } + auto phase_it = std::find_if( + existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); - if (object.contains(validation::keys.B) && object[validation::keys.B].is_array() && object[validation::keys.B].size() == 4) + if (phase_it != existing_phases.end()) + { + // check if all of the species for this reaction are actually in the aerosol phase + std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_aerosol_species, aerosol_phase_species)) { - for (size_t i = 0; i < 4; ++i) - { - simpol_phase_transfer.B[i] = object[validation::keys.B][i]; - } + status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; } - - simpol_phase_transfer.gas_phase = gas_phase; - simpol_phase_transfer.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; - simpol_phase_transfer.aerosol_phase = aerosol_phase; - simpol_phase_transfer.aerosol_phase_species = { .species_name = aerosol_phase_species, .coefficient = 1 }; - simpol_phase_transfer.unknown_properties = unknown_properties; + } + else + { + status = ConfigParseStatus::UnknownPhase; } - return { status, simpol_phase_transfer }; + henrys_law.gas_phase = gas_phase; + henrys_law.gas_phase_species = gas_phase_species; + henrys_law.aerosol_phase = aerosol_phase; + henrys_law.aerosol_phase_species = aerosol_phase_species; + henrys_law.aerosol_phase_water = aerosol_phase_water; + henrys_law.unknown_properties = GetComments(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys); } - /// @brief Parses an aqueous equilibrium reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of Condensed Phase Arrhenius parameters - std::pair - ParseAqueousEquilibrium(const json& object, const std::vector& existing_species, const std::vector& existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::AqueousEquilibrium aqueous_equilibrium; - - status = ValidateSchema(object, validation::aqueous_equilibrium.required_keys, validation::aqueous_equilibrium.optional_keys); - if (status == ConfigParseStatus::Success) - { - std::vector 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 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.A)) - { - aqueous_equilibrium.A = object[validation::keys.A].get(); - } - if (object.contains(validation::keys.C)) - { - aqueous_equilibrium.C = object[validation::keys.C].get(); - } - - aqueous_equilibrium.k_reverse = object[validation::keys.k_reverse].get(); - - if (object.contains(validation::keys.name)) - { - aqueous_equilibrium.name = object[validation::keys.name].get(); - } - - auto comments = GetComments(object, validation::aqueous_equilibrium.required_keys, validation::aqueous_equilibrium.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } - - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); - - std::vector 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); - } - requested_species.push_back(aerosol_phase_water); - - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - - auto phase_it = std::find_if( - existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); - - if (phase_it != existing_phases.end()) - { - // check if all of the species for this reaction are actually in the aerosol phase - std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, aerosol_phase_species)) - { - status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; - } - } - else - { - status = ConfigParseStatus::UnknownPhase; - } + reactions.henrys_law.push_back(henrys_law); + } - aqueous_equilibrium.aerosol_phase = aerosol_phase; - aqueous_equilibrium.aerosol_phase_water = aerosol_phase_water; - aqueous_equilibrium.products = products; - aqueous_equilibrium.reactants = reactants; - aqueous_equilibrium.unknown_properties = unknown_properties; - } + return status; + } +}; - return { status, aqueous_equilibrium }; - } +class ArrheniusParser : public IReactionParser +{ + public: + ConfigParseStatus parse( + const json& object, + const std::vector& existing_species, + const std::vector& existing_phases, + open_atmos::types::Reactions& reactions) override + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Arrhenius arrhenius; - /// @brief Parses a wet deposition reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of First Order Loss parameters - std::pair - ParseWetDeposition(const json& object, const std::vector existing_species, const std::vector existing_phases) + status = ValidateSchema(object, validation::arrhenius.required_keys, validation::arrhenius.optional_keys); + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::WetDeposition wet_deposition; + auto products = ParseReactantsOrProducts(validation::keys.products, object, status); + auto reactants = ParseReactantsOrProducts(validation::keys.reactants, object, status); - status = ValidateSchema(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); - if (status == ConfigParseStatus::Success) + if (object.contains(validation::keys.A)) { - if (object.contains(validation::keys.scaling_factor)) - { - wet_deposition.scaling_factor = object[validation::keys.scaling_factor].get(); - } - - if (object.contains(validation::keys.name)) + arrhenius.A = object[validation::keys.A].get(); + } + if (object.contains(validation::keys.B)) + { + arrhenius.B = object[validation::keys.B].get(); + } + if (object.contains(validation::keys.C)) + { + arrhenius.C = object[validation::keys.C].get(); + } + if (object.contains(validation::keys.D)) + { + arrhenius.D = object[validation::keys.D].get(); + } + if (object.contains(validation::keys.E)) + { + arrhenius.E = object[validation::keys.E].get(); + } + if (object.contains(validation::keys.Ea)) + { + if (arrhenius.C != 0) { - wet_deposition.name = object[validation::keys.name].get(); + 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() / constants::boltzmann; + } - auto comments = GetComments(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } + if (object.contains(validation::keys.name)) + { + arrhenius.name = object[validation::keys.name].get(); + } - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); + std::vector 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); + } - // check if aerosol phase exists - 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; - } + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) + { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } - wet_deposition.aerosol_phase = aerosol_phase; - wet_deposition.unknown_properties = unknown_properties; + std::string gas_phase = object[validation::keys.gas_phase].get(); + auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); + if (status == ConfigParseStatus::Success && it == existing_phases.end()) + { + status = ConfigParseStatus::UnknownPhase; } - return { status, wet_deposition }; + arrhenius.gas_phase = gas_phase; + arrhenius.products = products; + arrhenius.reactants = reactants; + arrhenius.unknown_properties = GetComments(object, validation::arrhenius.required_keys, validation::arrhenius.optional_keys); } - /// @brief Parses a first order loss reaction - /// @param object A json object that should have information containing arrhenius parameters - /// @param existing_species A list of species configured in a mechanism - /// @param existing_phases A list of phases configured in a mechanism - /// @return A pair indicating parsing success and a struct of First Order Loss parameters - std::pair - ParseHenrysLaw(const json& object, const std::vector existing_species, const std::vector existing_phases) + if (status == ConfigParseStatus::Success) { - ConfigParseStatus status = ConfigParseStatus::Success; - types::HenrysLaw henrys_law; + reactions.arrhenius.push_back(arrhenius); + } - status = ValidateSchema(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys); - if (status == ConfigParseStatus::Success) + return status; + } +}; + +/// @brief Parses all reactions +/// @param objects A json object that should contain only valid reactions +/// @param existing_species A list of spcecies configured for a mechanism +/// @param existing_phases A list of phases configured for a mechanism +/// @return A pair indicating parsing status and a Reactions struct filled with reactions configured for a mechansim +std::pair +ParseReactions(const json& objects, const std::vector& existing_species, const std::vector& existing_phases) +{ + ConfigParseStatus status = ConfigParseStatus::Success; + types::Reactions reactions; + + std::map> parsers; + parsers[validation::keys.Arrhenius_key] = std::make_unique(); + parsers[validation::keys.HenrysLaw_key] = std::make_unique(); + parsers[validation::keys.WetDeposition_key] = std::make_unique(); + parsers[validation::keys.AqueousPhaseEquilibrium_key] = std::make_unique(); + parsers[validation::keys.SimpolPhaseTransfer_key] = std::make_unique(); + parsers[validation::keys.FirstOrderLoss_key] = std::make_unique(); + parsers[validation::keys.Emission_key] = std::make_unique(); + parsers[validation::keys.CondensedPhasePhotolysis_key] = std::make_unique(); + parsers[validation::keys.Photolysis_key] = std::make_unique(); + parsers[validation::keys.Surface_key] = std::make_unique(); + parsers[validation::keys.Tunneling_key] = std::make_unique(); + parsers[validation::keys.Branched_key] = std::make_unique(); + parsers[validation::keys.Troe_key] = std::make_unique(); + parsers[validation::keys.CondensedPhaseArrhenius_key] = std::make_unique(); + + for (const auto& object : objects) + { + std::string type = object[validation::keys.type].get(); + auto it = parsers.find(type); + if (it != parsers.end()) + { + auto parse_status = it->second->parse(object, existing_species, existing_phases, reactions); + status = parse_status; + if (status != ConfigParseStatus::Success) { - std::string gas_phase = object[validation::keys.gas_phase].get(); - std::string gas_phase_species = object[validation::keys.gas_phase_species].get(); - std::string aerosol_phase = object[validation::keys.aerosol_phase].get(); - std::string aerosol_phase_species = object[validation::keys.aerosol_phase_species].get(); - std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get(); - - if (object.contains(validation::keys.name)) - { - henrys_law.name = object[validation::keys.name].get(); - } - - auto comments = GetComments(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys); - - std::unordered_map unknown_properties; - for (const auto& key : comments) - { - std::string val = object[key].dump(); - unknown_properties[key] = val; - } - - std::vector requested_species; - requested_species.push_back(gas_phase_species); - requested_species.push_back(aerosol_phase_species); - requested_species.push_back(aerosol_phase_water); - - std::vector requested_aerosol_species; - requested_aerosol_species.push_back(aerosol_phase_species); - requested_aerosol_species.push_back(aerosol_phase_water); - - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) - { - status = ConfigParseStatus::ReactionRequiresUnknownSpecies; - } - - auto it = std::find_if(existing_phases.begin(), existing_phases.end(), [&gas_phase](const auto& phase) { return phase.name == gas_phase; }); - if (status == ConfigParseStatus::Success && it == existing_phases.end()) - { - status = ConfigParseStatus::UnknownPhase; - } - - auto phase_it = std::find_if( - existing_phases.begin(), existing_phases.end(), [&aerosol_phase](const types::Phase& phase) { return phase.name == aerosol_phase; }); - - if (phase_it != existing_phases.end()) - { - // check if all of the species for this reaction are actually in the aerosol phase - std::vector aerosol_phase_species = { (*phase_it).species.begin(), (*phase_it).species.end() }; - if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_aerosol_species, aerosol_phase_species)) - { - status = ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase; - } - } - else - { - status = ConfigParseStatus::UnknownPhase; - } - - henrys_law.gas_phase = gas_phase; - henrys_law.gas_phase_species = gas_phase_species; - henrys_law.aerosol_phase = aerosol_phase; - henrys_law.aerosol_phase_species = aerosol_phase_species; - henrys_law.aerosol_phase_water = aerosol_phase_water; - henrys_law.unknown_properties = unknown_properties; + break; } - - return { status, henrys_law }; } - - /// @brief Parses all reactions - /// @param objects A json object that should contain only valid reactions - /// @param existing_species A list of spcecies configured for a mechanism - /// @param existing_phases A list of phases configured for a mechanism - /// @return A pair indicating parsing status and a Reactions struct filled with reactions configured for a mechansim - std::pair - ParseReactions(const json& objects, const std::vector& existing_species, const std::vector& existing_phases) + else { - ConfigParseStatus status = ConfigParseStatus::Success; - types::Reactions reactions; + const std::string& msg = "Unknown type: " + type; + throw std::runtime_error(msg); + } + } - for (const auto& object : objects) - { - std::string type = object[validation::keys.type].get(); - if (type == validation::keys.Arrhenius_key) - { - auto arrhenius_parse = ParseArrhenius(object, existing_species, existing_phases); - status = arrhenius_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.arrhenius.push_back(arrhenius_parse.second); - } - else if (type == validation::keys.CondensedPhaseArrhenius_key) - { - auto condensed_phase_arrhenius_parse = ParseCondensedPhaseArrhenius(object, existing_species, existing_phases); - status = condensed_phase_arrhenius_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.condensed_phase_arrhenius.push_back(condensed_phase_arrhenius_parse.second); - } - else if (type == validation::keys.Troe_key) - { - auto troe_parse = ParseTroe(object, existing_species, existing_phases); - status = troe_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.troe.push_back(troe_parse.second); - } - else if (type == validation::keys.Branched_key) - { - auto branched_parse = ParseBranched(object, existing_species, existing_phases); - status = branched_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.branched.push_back(branched_parse.second); - } - else if (type == validation::keys.Tunneling_key) - { - auto tunneling_parse = ParseTunneling(object, existing_species, existing_phases); - status = tunneling_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - 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); - } - else if (type == validation::keys.Photolysis_key) - { - auto photolysis_parse = ParsePhotolysis(object, existing_species, existing_phases); - status = photolysis_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.photolysis.push_back(photolysis_parse.second); - } - else if (type == validation::keys.CondensedPhasePhotolysis_key) - { - auto condensed_phase_photolysis_parse = ParseCondensedPhasePhotolysis(object, existing_species, existing_phases); - status = condensed_phase_photolysis_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.condensed_phase_photolysis.push_back(condensed_phase_photolysis_parse.second); - } - else if (type == validation::keys.Emission_key) - { - auto emission_parse = ParseEmission(object, existing_species, existing_phases); - status = emission_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.emission.push_back(emission_parse.second); - } - else if (type == validation::keys.FirstOrderLoss_key) - { - auto first_order_loss_parse = ParseFirstOrderLoss(object, existing_species, existing_phases); - status = first_order_loss_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.first_order_loss.push_back(first_order_loss_parse.second); - } + return { status, reactions }; +} - else if (type == validation::keys.SimpolPhaseTransfer_key) - { - auto simpol_phase_transfer_parse = ParseSimpolPhaseTransfer(object, existing_species, existing_phases); - status = simpol_phase_transfer_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.simpol_phase_transfer.push_back(simpol_phase_transfer_parse.second); - } - else if (type == validation::keys.AqueousPhaseEquilibrium_key) - { - auto aqueous_equilibrium_parse = ParseAqueousEquilibrium(object, existing_species, existing_phases); - status = aqueous_equilibrium_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.aqueous_equilibrium.push_back(aqueous_equilibrium_parse.second); - } - else if (type == validation::keys.WetDeposition_key) - { - auto wet_deposition_parse = ParseWetDeposition(object, existing_species, existing_phases); - status = wet_deposition_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.wet_deposition.push_back(wet_deposition_parse.second); - } - else if (type == validation::keys.HenrysLaw_key) - { - auto henrys_law_parse = ParseHenrysLaw(object, existing_species, existing_phases); - status = henrys_law_parse.first; - if (status != ConfigParseStatus::Success) - { - break; - } - reactions.henrys_law.push_back(henrys_law_parse.second); - } +namespace open_atmos +{ + namespace mechanism_configuration + { + std::string configParseStatusToString(const ConfigParseStatus& status) + { + switch (status) + { + case ConfigParseStatus::Success: return "Success"; + case ConfigParseStatus::None: return "None"; + case ConfigParseStatus::InvalidKey: return "InvalidKey"; + case ConfigParseStatus::UnknownKey: return "UnknownKey"; + case ConfigParseStatus::InvalidFilePath: return "InvalidFilePath"; + case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; + case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound"; + case ConfigParseStatus::MutuallyExclusiveOption: return "MutuallyExclusiveOption"; + case ConfigParseStatus::DuplicateSpeciesDetected: return "DuplicateSpeciesDetected"; + case ConfigParseStatus::DuplicatePhasesDetected: return "DuplicatePhasesDetected"; + case ConfigParseStatus::PhaseRequiresUnknownSpecies: return "PhaseRequiresUnknownSpecies"; + case ConfigParseStatus::ReactionRequiresUnknownSpecies: return "ReactionRequiresUnknownSpecies"; + case ConfigParseStatus::UnknownPhase: return "UnknownPhase"; + case ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase: return "RequestedAerosolSpeciesNotIncludedInAerosolPhase"; + case ConfigParseStatus::TooManyReactionComponents: return "TooManyReactionComponents"; + case ConfigParseStatus::InvalidIonPair: return "InvalidIonPair"; + default: return "Unknown"; } - - return { status, reactions }; } /// @brief Parse a mechanism diff --git a/test/integration/test_json_parser.cpp b/test/integration/test_json_parser.cpp index 623438a..fe883cf 100644 --- a/test/integration/test_json_parser.cpp +++ b/test/integration/test_json_parser.cpp @@ -12,22 +12,22 @@ TEST(JsonParser, ParsesFullConfiguration) EXPECT_EQ(mechanism.name, "Full Configuration"); EXPECT_EQ(mechanism.species.size(), 11); EXPECT_EQ(mechanism.phases.size(), 4); + EXPECT_EQ(mechanism.reactions.aqueous_equilibrium.size(), 1); EXPECT_EQ(mechanism.reactions.arrhenius.size(), 2); EXPECT_EQ(mechanism.reactions.branched.size(), 1); EXPECT_EQ(mechanism.reactions.condensed_phase_arrhenius.size(), 2); - EXPECT_EQ(mechanism.reactions.tunneling.size(), 1); - EXPECT_EQ(mechanism.reactions.surface.size(), 1); - EXPECT_EQ(mechanism.reactions.photolysis.size(), 1); EXPECT_EQ(mechanism.reactions.condensed_phase_photolysis.size(), 1); EXPECT_EQ(mechanism.reactions.emission.size(), 1); EXPECT_EQ(mechanism.reactions.first_order_loss.size(), 1); - EXPECT_EQ(mechanism.reactions.simpol_phase_transfer.size(), 1); - EXPECT_EQ(mechanism.reactions.aqueous_equilibrium.size(), 1); EXPECT_EQ(mechanism.reactions.henrys_law.size(), 1); EXPECT_EQ(mechanism.reactions.photolysis.size(), 1); + EXPECT_EQ(mechanism.reactions.photolysis.size(), 1); + EXPECT_EQ(mechanism.reactions.simpol_phase_transfer.size(), 1); + EXPECT_EQ(mechanism.reactions.surface.size(), 1); EXPECT_EQ(mechanism.reactions.surface.size(), 1); EXPECT_EQ(mechanism.reactions.troe.size(), 1); EXPECT_EQ(mechanism.reactions.tunneling.size(), 1); + EXPECT_EQ(mechanism.reactions.tunneling.size(), 1); } TEST(JsonParser, ParserReportsBadFiles) From 5a4faa757a98e889240a43937b0a234af705646a Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 23 Jan 2024 17:28:13 -0600 Subject: [PATCH 2/4] removing more lines of code --- src/parser.cpp | 68 +++++++++----------------------------------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 9cf1f00..b65724b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -412,12 +412,9 @@ class CondensedPhaseArrheniusParser : public IReactionParser condensed_phase_arrhenius.reactants = reactants; condensed_phase_arrhenius.unknown_properties = GetComments(object, validation::condensed_phase_arrhenius.required_keys, validation::condensed_phase_arrhenius.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.condensed_phase_arrhenius.push_back(condensed_phase_arrhenius); } + return status; } }; @@ -504,12 +501,9 @@ class TroeParser : public IReactionParser troe.products = products; troe.reactants = reactants; troe.unknown_properties = GetComments(object, validation::troe.required_keys, validation::troe.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.troe.push_back(troe); } + return status; } }; @@ -574,12 +568,9 @@ class BranchedParser : public IReactionParser branched.alkoxy_products = alkoxy_products; branched.reactants = reactants; branched.unknown_properties = GetComments(object, validation::branched.required_keys, validation::branched.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.branched.push_back(branched); } + return status; } }; @@ -646,12 +637,9 @@ class TunnelingParser : public IReactionParser tunneling.products = products; tunneling.reactants = reactants; tunneling.unknown_properties = GetComments(object, validation::tunneling.required_keys, validation::tunneling.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.tunneling.push_back(tunneling); } + return status; } }; @@ -710,12 +698,9 @@ class SurfaceParser : public IReactionParser surface.gas_phase_products = products; surface.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; surface.unknown_properties = GetComments(object, validation::surface.required_keys, validation::surface.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.surface.push_back(surface); } + return status; } }; @@ -779,12 +764,9 @@ class PhotolysisParser : public IReactionParser photolysis.products = products; photolysis.reactants = reactants; photolysis.unknown_properties = GetComments(object, validation::photolysis.required_keys, validation::photolysis.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.photolysis.push_back(photolysis); } + return status; } }; @@ -864,12 +846,9 @@ class CondensedPhasePhotolysisParser : public IReactionParser condensed_phase_photolysis.reactants = reactants; condensed_phase_photolysis.unknown_properties = GetComments(object, validation::condensed_phase_photolysis.required_keys, validation::photolysis.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.condensed_phase_photolysis.push_back(condensed_phase_photolysis); } + return status; } }; @@ -922,12 +901,9 @@ class EmissionParser : public IReactionParser emission.gas_phase = gas_phase; emission.products = products; emission.unknown_properties = GetComments(object, validation::emission.required_keys, validation::emission.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.emission.push_back(emission); } + return status; } }; @@ -986,12 +962,9 @@ class FirstOrderLossParser : public IReactionParser first_order_loss.reactants = reactants; first_order_loss.unknown_properties = GetComments(object, validation::first_order_loss.required_keys, validation::first_order_loss.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.first_order_loss.push_back(first_order_loss); } + return status; } }; @@ -1076,12 +1049,9 @@ class SimpolPhaseTransferParser : public IReactionParser simpol_phase_transfer.aerosol_phase_species = { .species_name = aerosol_phase_species, .coefficient = 1 }; simpol_phase_transfer.unknown_properties = GetComments(object, validation::simpol_phase_transfer.required_keys, validation::simpol_phase_transfer.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.simpol_phase_transfer.push_back(simpol_phase_transfer); } + return status; } }; @@ -1162,12 +1132,9 @@ class AqueousEquilibriumParser : public IReactionParser aqueous_equilibrium.reactants = reactants; aqueous_equilibrium.unknown_properties = GetComments(object, validation::aqueous_equilibrium.required_keys, validation::aqueous_equilibrium.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.aqueous_equilibrium.push_back(aqueous_equilibrium); } + return status; } }; @@ -1209,12 +1176,9 @@ class WetDepositionParser : public IReactionParser wet_deposition.aerosol_phase = aerosol_phase; wet_deposition.unknown_properties = GetComments(object, validation::wet_deposition.required_keys, validation::wet_deposition.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.wet_deposition.push_back(wet_deposition); } + return status; } }; @@ -1288,10 +1252,6 @@ class HenrysLawParser : public IReactionParser henrys_law.aerosol_phase_species = aerosol_phase_species; henrys_law.aerosol_phase_water = aerosol_phase_water; henrys_law.unknown_properties = GetComments(object, validation::henrys_law.required_keys, validation::henrys_law.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.henrys_law.push_back(henrys_law); } @@ -1379,10 +1339,6 @@ class ArrheniusParser : public IReactionParser arrhenius.products = products; arrhenius.reactants = reactants; arrhenius.unknown_properties = GetComments(object, validation::arrhenius.required_keys, validation::arrhenius.optional_keys); - } - - if (status == ConfigParseStatus::Success) - { reactions.arrhenius.push_back(arrhenius); } From 82b8e02c0595f2f073c3f462a9216b60ee08670e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 23 Jan 2024 17:35:46 -0600 Subject: [PATCH 3/4] complying with vscode --- include/open_atmos/types.hpp | 2 +- src/parser.cpp | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index 1f883bc..bdac4bb 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -33,7 +33,7 @@ namespace open_atmos struct ReactionComponent { std::string species_name; - double coefficient; + double coefficient { 1.0 }; /// @brief Unknown properties, prefixed with two underscores (__) std::unordered_map unknown_properties; }; diff --git a/src/parser.cpp b/src/parser.cpp index b65724b..2fc8d35 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -696,7 +696,9 @@ class SurfaceParser : public IReactionParser surface.gas_phase = object[validation::keys.gas_phase].get(); surface.aerosol_phase = aerosol_phase; surface.gas_phase_products = products; - surface.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; + types::ReactionComponent component; + component.species_name = gas_phase_species; + surface.gas_phase_species = component; surface.unknown_properties = GetComments(object, validation::surface.required_keys, validation::surface.optional_keys); reactions.surface.push_back(surface); } @@ -1044,9 +1046,13 @@ class SimpolPhaseTransferParser : public IReactionParser } simpol_phase_transfer.gas_phase = gas_phase; - simpol_phase_transfer.gas_phase_species = { .species_name = gas_phase_species, .coefficient = 1 }; + types::ReactionComponent gas_component; + gas_component.species_name = gas_phase_species; + simpol_phase_transfer.gas_phase_species = gas_component; simpol_phase_transfer.aerosol_phase = aerosol_phase; - simpol_phase_transfer.aerosol_phase_species = { .species_name = aerosol_phase_species, .coefficient = 1 }; + types::ReactionComponent aerosol_component; + aerosol_component.species_name = aerosol_phase_species; + simpol_phase_transfer.aerosol_phase_species = aerosol_component; simpol_phase_transfer.unknown_properties = GetComments(object, validation::simpol_phase_transfer.required_keys, validation::simpol_phase_transfer.optional_keys); reactions.simpol_phase_transfer.push_back(simpol_phase_transfer); From 1ce127a4f6bde1651fb29954c38217675b792777 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 23 Jan 2024 17:39:35 -0600 Subject: [PATCH 4/4] correcting windows action --- .github/workflows/windows.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f5564e1..1aa0f2b 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -18,11 +18,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Run CMake - run: cmake -S . -B build -A ${{ matrix.architecture }} - if: matrix.build_type == 'Release' - - name: Run CMake - run: cmake -S . -B build -A ${{ matrix.architecture }} - if: matrix.build_type == 'Debug' + run: cmake -S . -B build -A ${{ matrix.architecture }} -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} - name: Build run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 - name: Run tests