Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse aqueous equilibrium #37

Merged
merged 3 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading