Skip to content

Commit

Permalink
Merge pull request #37 from open-atmos/parse_aqueous_equilibrium
Browse files Browse the repository at this point in the history
Parse aqueous equilibrium
  • Loading branch information
K20shores authored Jan 23, 2024
2 parents ef7f3a0 + 09e8273 commit 64f86a0
Show file tree
Hide file tree
Showing 14 changed files with 463 additions and 41 deletions.
28 changes: 1 addition & 27 deletions .github/workflows/mac.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,13 @@ concurrency:
cancel-in-progress: true

jobs:
xcode_macos_12:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
runs-on: macos-12
strategy:
matrix:
# all available versions of xcode: https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#xcode
xcode: ['13.1', '14.1']
build_type: [Debug, Release]
env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer

steps:
- uses: actions/checkout@v3

- name: Run Cmake
run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }}

- name: Build
run: cmake --build build --parallel 10

- name: Run tests
run: |
cd build
ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure . --verbose -j 10
xcode_macos_13:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name
runs-on: macos-13
strategy:
matrix:
# all available versions of xcode: https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode
xcode: ['14.1', '15.0']
xcode: ['15.0']
build_type: [Debug, Release]
env:
DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer
Expand All @@ -63,7 +38,6 @@ jobs:
strategy:
matrix:
compiler:
- { cpp: g++-11, c: gcc-11}
- { cpp: g++-12, c: gcc-12}
- { cpp: clang++, c: clang}
build_type: [Debug, Release]
Expand Down
7 changes: 4 additions & 3 deletions examples/full_configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,10 @@
"species": [
"H2O2_aq",
"H2O_aq",
"ethanol_aq"
"ethanol_aq",
"A",
"B",
"C"
]
},
{
Expand Down Expand Up @@ -120,13 +123,11 @@
},
{
"type": "AQUEOUS_EQUILIBRIUM",
"gas phase": "gas",
"aerosol phase": "aqueous aerosol",
"aerosol-phase water": "H2O_aq",
"A": 1.14e-2,
"C": 2300.0,
"k_reverse": 0.32,
"ion pair": "B-C",
"reactants": [
{
"species name": "A",
Expand Down
3 changes: 2 additions & 1 deletion include/open_atmos/mechanism_configuration/parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ namespace open_atmos
ReactionRequiresUnknownSpecies,
UnknownPhase,
RequestedAerosolSpeciesNotIncludedInAerosolPhase,
TooManyReactionComponents
TooManyReactionComponents,
InvalidIonPair
};
std::string configParseStatusToString(const ConfigParseStatus &status);

Expand Down
22 changes: 15 additions & 7 deletions include/open_atmos/mechanism_configuration/validation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ namespace open_atmos
const std::string FirstOrderLoss_key = "FIRST_ORDER_LOSS";
// also scaling factor

// Aqueous Equilibrium
const std::string AqueousPhaseEquilibrium_key = "AQUEOUS_EQUILIBRIUM";
// also
// aerosol phase
// aerosol-phase water
// A
// C
const std::string k_reverse = "k_reverse";

// Wet Deposition
const std::string WetDeposition_key = "WET_DEPOSITION";
// also
Expand All @@ -133,11 +142,11 @@ namespace open_atmos

} keys;

struct Configuration
struct Mechanism
{
const std::vector<std::string> required_keys{ keys.version, keys.species, keys.phases, keys.reactions };
const std::vector<std::string> optional_keys{ keys.name };
} configuration;
} mechanism;

struct Species
{
Expand Down Expand Up @@ -237,11 +246,10 @@ namespace open_atmos
const std::vector<std::string> optional_keys{ keys.name };
} henrys_law;

struct Mechanism
struct AqueousEquilibrium
{
const std::vector<std::string> required_keys{};
const std::vector<std::string> optional_keys{};
} mechanism;

const std::vector<std::string> required_keys{ keys.type, keys.reactants, keys.products, keys.aerosol_phase, keys.aerosol_phase_water, keys.k_reverse };
const std::vector<std::string> optional_keys{ keys.name, keys.A, keys.C };
} aqueous_equilibrium;
} // namespace validation
} // namespace open_atmos
28 changes: 28 additions & 0 deletions include/open_atmos/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

#include <string>
#include <unordered_map>
#include <array>
#include <vector>
#include <optional>


namespace open_atmos
{
Expand Down Expand Up @@ -240,6 +244,29 @@ namespace open_atmos
/// @brief Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};
struct AqueousEquilibrium
{
/// @brief An identifier, optional, uniqueness not enforced
std::string name;
/// @brief An identifier indicating which gas phase this reaction takes place in
std::string gas_phase;
/// @brief An identifier indicating which aerosol phase this reaction takes place in
std::string aerosol_phase;
/// @brief An identifier indicating the species label of aqueous phase water
std::string aerosol_phase_water;
/// @brief A list of reactants
std::vector<ReactionComponent> reactants;
/// @brief A list of products
std::vector<ReactionComponent> products;
/// @brief Pre-exponential factor (s-1)
double A{ 1 };
/// @brief A constant
double C{ 0 };
/// @brief Reverse reation rate constant (s-1)
double k_reverse{ 0 };
/// @brief Unknown properties, prefixed with two underscores (__)
std::unordered_map<std::string, std::string> unknown_properties;
};

struct WetDeposition
{
Expand Down Expand Up @@ -279,6 +306,7 @@ namespace open_atmos
std::vector<types::CondensedPhasePhotolysis> condensed_phase_photolysis;
std::vector<types::Emission> emission;
std::vector<types::FirstOrderLoss> first_order_loss;
std::vector<types::AqueousEquilibrium> aqueous_equilibrium;
std::vector<types::WetDeposition> wet_deposition;
std::vector<types::HenrysLaw> henrys_law;
std::vector<types::Photolysis> photolysis;
Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ configure_file(version.hpp.in ${PROJECT_SOURCE_DIR}/include/open_atmos/mechanism
add_library(mechanism_configuration)
add_library(open_atmos::mechanism_configuration ALIAS mechanism_configuration)

target_compile_features(mechanism_configuration PUBLIC cxx_std_20)
target_compile_features(mechanism_configuration PUBLIC cxx_std_17)

target_sources(mechanism_configuration
PRIVATE
Expand Down
126 changes: 124 additions & 2 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ namespace open_atmos
case ConfigParseStatus::UnknownPhase: return "UnknownPhase";
case ConfigParseStatus::RequestedAerosolSpeciesNotIncludedInAerosolPhase: return "RequestedAerosolSpeciesNotIncludedInAerosolPhase";
case ConfigParseStatus::TooManyReactionComponents: return "TooManyReactionComponents";
case ConfigParseStatus::InvalidIonPair: return "InvalidIonPair";
default: return "Unknown";
}
}
Expand Down Expand Up @@ -137,7 +138,7 @@ namespace open_atmos
// now, anything left must be standard comment starting with __
for (auto& key : remaining)
{
if (!key.starts_with("__"))
if (key.find("__") == std::string::npos)
{
std::cerr << "Non-standard key '" << key << "' found in object" << object << std::endl;

Expand Down Expand Up @@ -1332,6 +1333,117 @@ namespace open_atmos
return { status, first_order_loss };
}

/// @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<ConfigParseStatus, types::AqueousEquilibrium> ParseAqueousEquilibrium(
const json& object,
const std::vector<types::Species>& existing_species,
const std::vector<types::Phase>& existing_phases)
{
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<types::ReactionComponent> products{};
for (const auto& product : object[validation::keys.products])
{
auto product_parse = ParseReactionComponent(product);
status = product_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
products.push_back(product_parse.second);
}

std::vector<types::ReactionComponent> reactants{};
for (const auto& reactant : object[validation::keys.reactants])
{
auto reactant_parse = ParseReactionComponent(reactant);
status = reactant_parse.first;
if (status != ConfigParseStatus::Success)
{
break;
}
reactants.push_back(reactant_parse.second);
}

if (object.contains(validation::keys.A))
{
aqueous_equilibrium.A = object[validation::keys.A].get<double>();
}
if (object.contains(validation::keys.C))
{
aqueous_equilibrium.C = object[validation::keys.C].get<double>();
}

aqueous_equilibrium.k_reverse = object[validation::keys.k_reverse].get<double>();

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

auto comments = GetComments(object, validation::aqueous_equilibrium.required_keys, validation::aqueous_equilibrium.optional_keys);

std::unordered_map<std::string, std::string> unknown_properties;
for (const auto& key : comments)
{
std::string val = object[key].dump();
unknown_properties[key] = val;
}

std::string aerosol_phase = object[validation::keys.aerosol_phase].get<std::string>();
std::string aerosol_phase_water = object[validation::keys.aerosol_phase_water].get<std::string>();

std::vector<std::string> requested_species;
for (const auto& spec : products)
{
requested_species.push_back(spec.species_name);
}
for (const auto& spec : reactants)
{
requested_species.push_back(spec.species_name);
}
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<std::string> 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;
}

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, aqueous_equilibrium };
}

/// @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
Expand Down Expand Up @@ -1577,6 +1689,16 @@ namespace open_atmos
}
reactions.first_order_loss.push_back(first_order_loss_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);
Expand Down Expand Up @@ -1638,7 +1760,7 @@ namespace open_atmos
ConfigParseStatus status;
types::Mechanism mechanism;

status = ValidateSchema(object, validation::configuration.required_keys, validation::configuration.optional_keys);
status = ValidateSchema(object, validation::mechanism.required_keys, validation::mechanism.optional_keys);

if (status != ConfigParseStatus::Success)
{
Expand Down
1 change: 1 addition & 0 deletions test/integration/test_json_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ TEST(JsonParser, ParsesFullConfiguration)
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.aqueous_equilibrium.size(), 1);
EXPECT_EQ(mechanism.reactions.henrys_law.size(), 1);
EXPECT_EQ(mechanism.reactions.photolysis.size(), 1);
EXPECT_EQ(mechanism.reactions.surface.size(), 1);
Expand Down
1 change: 1 addition & 0 deletions test/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ create_standard_test(NAME parse_species SOURCES test_parse_species.cpp)
create_standard_test(NAME parse_surface SOURCES test_parse_surface.cpp)
create_standard_test(NAME parse_troe SOURCES test_parse_troe.cpp)
create_standard_test(NAME parse_tunneling SOURCES test_parse_tunneling.cpp)
create_standard_test(NAME parse_aqueous_equilibrium SOURCES test_parse_aqueous_equilibrium.cpp)
create_standard_test(NAME parse_wet_deposition SOURCES test_parse_wet_deposition.cpp)

################################################################################
Expand Down
Loading

0 comments on commit 64f86a0

Please sign in to comment.