From ad8f3678a2cd9bba18c2d365fe238eefb5d4a559 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 3 Jan 2024 14:05:03 -0600 Subject: [PATCH 01/26] changes --- examples/full_configuration.json | 71 +++++++++++++++++++------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index cc0dfae..2e615d4 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -1,28 +1,23 @@ { - "camp-data": [ - { - "type": "RELATIVE_TOLERANCE", - "value": 1.0e-30 - }, + "version": "XXX", + "open-atmos-data": [ { "name": "A", "type": "CHEM_SPEC", - "absolute tolerance": 1.0e-30 + "__absolute tolerance": 1.0e-30 }, { "name": "B", - "type": "CHEM_SPEC", - "absolute tolerance": 1.0e-30 + "type": "CHEM_SPEC" }, { "name": "C", - "type": "CHEM_SPEC", - "absolute tolerance": 1.0e-30 + "type": "CHEM_SPEC" }, { "name": "H2O2", "type": "CHEM_SPEC", - "HLC(298K) [M Pa-1]": 1.011596348, + "HLC(298K) [mol m-3 Pa-1]": 1.011596348, "HLC exp factor [K]": 6340, "diffusion coeff [m2 s-1]": 1.46E-05, "N star": 1.74, @@ -41,7 +36,6 @@ { "name": "ethanol_aq", "type": "CHEM_SPEC", - "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.04607, "density [kg m-3]": 1000.0, "absolute tolerance": 1.0e-20 @@ -49,7 +43,6 @@ { "name": "H2O2_aq", "type": "CHEM_SPEC", - "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, "absolute tolerance": 1.0e-10 @@ -57,7 +50,6 @@ { "name": "H2O_aq", "type": "CHEM_SPEC", - "phase": "AEROSOL", "density [kg m-3]": 1000.0, "molecular weight [kg mol-1]": 0.01801 }, @@ -126,7 +118,7 @@ "k2_A": 4e-20, "k2_B": 1.5, "k2_C": -2, - "time unit": "MIN" + "rxn id": "my cmaq2" }, { "type": "CMAQ_OH_HNO3", @@ -151,14 +143,15 @@ "k3_A": 4e-20, "k3_B": 1.5, "k3_C": -2, - "time unit": "MIN" + "rxn id": "my cmaq1" }, { "type": "HL_PHASE_TRANSFER", "gas-phase species": "H2O2", "aerosol phase": "aqueous aerosol", "aerosol-phase species": "H2O2_aq", - "aerosol-phase water": "H2O_aq" + "aerosol-phase water": "H2O_aq", + "rxn id": "my henry's law" }, { "type": "SIMPOL_PHASE_TRANSFER", @@ -170,7 +163,8 @@ 2.91E+00, 1.96E-03, -4.96E-01 - ] + ], + "rxn id": "my simpl" }, { "type": "AQUEOUS_EQUILIBRIUM", @@ -195,7 +189,8 @@ "speices name": "C", "coeff": 1 } - ] + ], + "rxn id": "my aqueous eq" }, { "type": "CONDENSED_PHASE_ARRHENIUS", @@ -217,7 +212,8 @@ "speices name": "C", "coeff": 1 } - ] + ], + "rxn id": "my condensed arrhenius" }, { "type": "CONDENSED_PHASE_PHOTOLYSIS", @@ -236,16 +232,29 @@ "coeff": 1 } ], - "scaling factor": 12.3 + "scaling factor": 12.3, + "rxn id": "condensed photo B" }, { "type": "EMISSION", - "species": "B", + "reactants": [ + { + "speices name": "B", + "coeff": 1 + } + ], + "rxn id": "my emission", "scaling factor": 12.3 }, { "type": "FIRST_ORDER_LOSS", - "species": "B", + "products": [ + { + "speices name": "C", + "coeff": 1 + } + ], + "rxn id": "my first order loss", "scaling factor": 12.3 }, { @@ -262,6 +271,7 @@ "coeff": 1 } ], + "rxn id": "photo B", "scaling factor": 12.3 }, { @@ -274,7 +284,8 @@ "yield": 0.4 } }, - "aerosol phase": "surface reacting phase" + "aerosol phase": "surface reacting phase", + "rxn id": "my surface" }, { "type": "TERNARY_CHEMICAL_ACTIVATION", @@ -298,7 +309,7 @@ "kinf_C": 24, "Fc": 0.9, "N": 0.8, - "time unit": "MIN" + "rxn id": "my ternary" }, { "type": "TROE", @@ -322,7 +333,7 @@ "kinf_C": 24, "Fc": 0.9, "N": 0.8, - "time unit": "MIN" + "rxn id": "my troe" }, { "type": "WENNBERG_NO_RO2", @@ -348,15 +359,16 @@ "Y": 167, "a0": 0.15, "n": 9, - "time unit": "MIN" + "rxn id": "my wennberg" }, { "type": "WET_DEPOSITION", "aerosol phase": "cloud", + "rxn id": "rxn cloud", "scaling factor": 12.3 }, { - "type": "ARRHENIUS", + "type" : "ARRHENIUS", "reactants": [ { "speices name": "B", @@ -373,7 +385,8 @@ "B": -2.3, "C": 102.3, "D": 63.4, - "E": -1.3 + "E": -1.3, + "rxn id": "my arrhenius" } ] } From c6f623f83fbd27d9785d4ae590b3c701a481d572 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 10 Jan 2024 14:15:29 -0600 Subject: [PATCH 02/26] changes again --- examples/full_configuration.json | 537 ++++++++++++++----------------- 1 file changed, 238 insertions(+), 299 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 2e615d4..3424e7e 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -1,22 +1,19 @@ { "version": "XXX", - "open-atmos-data": [ + "name": "Full Configuration", + "species": [ { "name": "A", - "type": "CHEM_SPEC", "__absolute tolerance": 1.0e-30 }, { - "name": "B", - "type": "CHEM_SPEC" + "name": "B" }, { - "name": "C", - "type": "CHEM_SPEC" + "name": "C" }, { "name": "H2O2", - "type": "CHEM_SPEC", "HLC(298K) [mol m-3 Pa-1]": 1.011596348, "HLC exp factor [K]": 6340, "diffusion coeff [m2 s-1]": 1.46E-05, @@ -27,7 +24,6 @@ }, { "name": "ethanol", - "type": "CHEM_SPEC", "diffusion coeff [m2 s-1]": 0.95E-05, "N star": 2.55, "molecular weight [kg mol-1]": 0.04607, @@ -35,36 +31,23 @@ }, { "name": "ethanol_aq", - "type": "CHEM_SPEC", "molecular weight [kg mol-1]": 0.04607, "density [kg m-3]": 1000.0, "absolute tolerance": 1.0e-20 }, { "name": "H2O2_aq", - "type": "CHEM_SPEC", "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, "absolute tolerance": 1.0e-10 }, { "name": "H2O_aq", - "type": "CHEM_SPEC", "density [kg m-3]": 1000.0, "molecular weight [kg mol-1]": 0.01801 }, - { - "name": "aqueous aerosol", - "type": "AERO_PHASE", - "species": [ - "H2O2_aq", - "H2O_aq", - "ethanol_aq" - ] - }, { "name": "aerosol stuff", - "type": "CHEM_SPEC", "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.5, "density [kg m-3]": 1000.0, @@ -72,15 +55,23 @@ }, { "name": "more aerosol stuff", - "type": "CHEM_SPEC", "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.2, "density [kg m-3]": 1000.0, "absolute tolerance": 1.0e-20 + } + ], + "phases": [ + { + "name": "aqueous aerosol", + "species": [ + "H2O2_aq", + "H2O_aq", + "ethanol_aq" + ] }, { "name": "surface reacting phase", - "type": "AERO_PHASE", "species": [ "aerosol stuff", "more aerosol stuff" @@ -88,307 +79,255 @@ }, { "name": "cloud", - "type": "AERO_PHASE", "species": [ "B", "C" ] + } + ], + "reactions": [ + { + "type": "HL_PHASE_TRANSFER", + "gas-phase species": "H2O2", + "aerosol phase": "aqueous aerosol", + "aerosol-phase species": "H2O2_aq", + "aerosol-phase water": "H2O_aq", + "name": "my henry's law" + }, + { + "type": "SIMPOL_PHASE_TRANSFER", + "gas-phase species": "ethanol", + "aerosol phase": "aqueous aerosol", + "aerosol-phase species": "ethanol_aq", + "B": [ + -1.97E+03, + 2.91E+00, + 1.96E-03, + -4.96E-01 + ], + "name": "my simpl" }, { - "name": "CMAQ_H2O2", - "type": "MECHANISM", - "reactions": [ + "type": "AQUEOUS_EQUILIBRIUM", + "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": [ { - "type": "CMAQ_H2O2", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "k1_A": 1476.0, - "k1_B": 60, - "k1_C": -398, - "k2_A": 4e-20, - "k2_B": 1.5, - "k2_C": -2, - "rxn id": "my cmaq2" - }, + "species name": "A", + "coeff": 2 + } + ], + "products": [ { - "type": "CMAQ_OH_HNO3", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "k0_A": 1476.0, - "k0_B": 60, - "k0_C": -398, - "k2_A": 1350, - "k2_B": 58, - "k2_C": -450, - "k3_A": 4e-20, - "k3_B": 1.5, - "k3_C": -2, - "rxn id": "my cmaq1" + "species name": "B", + "coeff": 1 }, { - "type": "HL_PHASE_TRANSFER", - "gas-phase species": "H2O2", - "aerosol phase": "aqueous aerosol", - "aerosol-phase species": "H2O2_aq", - "aerosol-phase water": "H2O_aq", - "rxn id": "my henry's law" - }, + "species name": "C", + "coeff": 1 + } + ], + "name": "my aqueous eq" + }, + { + "type": "CONDENSED_PHASE_ARRHENIUS", + "aerosol phase": "aqueous aerosol", + "aerosol-phase water": "H2O_aq", + "units": "M", + "reactants": [ { - "type": "SIMPOL_PHASE_TRANSFER", - "gas-phase species": "ethanol", - "aerosol phase": "aqueous aerosol", - "aerosol-phase species": "ethanol_aq", - "B": [ - -1.97E+03, - 2.91E+00, - 1.96E-03, - -4.96E-01 - ], - "rxn id": "my simpl" + "species name": "A", + "coeff": 1 }, { - "type": "AQUEOUS_EQUILIBRIUM", - "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": [ - { - "speices name": "A", - "coeff": 2 - } - ], - "products": [ - { - "speices name": "B", - "coeff": 1 - }, - { - "speices name": "C", - "coeff": 1 - } - ], - "rxn id": "my aqueous eq" - }, + "species name": "B", + "coeff": 1 + } + ], + "products": [ { - "type": "CONDENSED_PHASE_ARRHENIUS", - "aerosol phase": "aqueous aerosol", - "aerosol-phase water": "H2O_aq", - "units": "M", - "reactants": [ - { - "speices name": "A", - "coeff": 1 - }, - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "rxn id": "my condensed arrhenius" - }, + "species name": "C", + "coeff": 1 + } + ], + "name": "my condensed arrhenius" + }, + { + "type": "CONDENSED_PHASE_PHOTOLYSIS", + "aerosol phase": "aqueous aerosol", + "aerosol-phase water": "H2O_aq", + "units": "M", + "reactants": [ { - "type": "CONDENSED_PHASE_PHOTOLYSIS", - "aerosol phase": "aqueous aerosol", - "aerosol-phase water": "H2O_aq", - "units": "M", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "scaling factor": 12.3, - "rxn id": "condensed photo B" - }, + "species name": "B", + "coeff": 1 + } + ], + "products": [ { - "type": "EMISSION", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "rxn id": "my emission", - "scaling factor": 12.3 - }, + "species name": "C", + "coeff": 1 + } + ], + "scaling factor": 12.3, + "name": "condensed photo B" + }, + { + "type": "EMISSION", + "reactants": [ { - "type": "FIRST_ORDER_LOSS", - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "rxn id": "my first order loss", - "scaling factor": 12.3 - }, + "species name": "B", + "coeff": 1 + } + ], + "name": "my emission", + "scaling factor": 12.3 + }, + { + "type": "FIRST_ORDER_LOSS", + "products": [ { - "type": "PHOTOLYSIS", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "rxn id": "photo B", - "scaling factor": 12.3 - }, + "species name": "C", + "coeff": 1 + } + ], + "name": "my first order loss", + "scaling factor": 12.3 + }, + { + "type": "PHOTOLYSIS", + "reactants": [ { - "type": "SURFACE", - "gas-phase reactant": "foo", - "reaction probability": 2.0e-2, - "gas-phase products": { - "bar": {}, - "baz": { - "yield": 0.4 - } - }, - "aerosol phase": "surface reacting phase", - "rxn id": "my surface" - }, + "species name": "B", + "coeff": 1 + } + ], + "products": [ { - "type": "TERNARY_CHEMICAL_ACTIVATION", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "k0_A": 1.2e-6, - "k0_B": 167, - "k0_C": 3, - "kinf_A": 136e6, - "kinf_B": 5, - "kinf_C": 24, - "Fc": 0.9, - "N": 0.8, - "rxn id": "my ternary" - }, + "species name": "C", + "coeff": 1 + } + ], + "name": "photo B", + "scaling factor": 12.3 + }, + { + "type": "SURFACE", + "gas-phase reactant": "foo", + "reaction probability": 2.0e-2, + "gas-phase products": { + "bar": {}, + "baz": { + "yield": 0.4 + } + }, + "aerosol phase": "surface reacting phase", + "name": "my surface" + }, + { + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": [ { - "type": "TROE", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "k0_A": 1.2e-12, - "k0_B": 167, - "k0_C": 3, - "kinf_A": 136, - "kinf_B": 5, - "kinf_C": 24, - "Fc": 0.9, - "N": 0.8, - "rxn id": "my troe" - }, + "species name": "B", + "coeff": 1 + } + ], + "products": [ { - "type": "WENNBERG_NO_RO2", - "reactants": [ - { - "speices name": "A", - "coeff": 1 - } - ], - "alkoxy products": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "nitrate products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "X": 1.2e-4, - "Y": 167, - "a0": 0.15, - "n": 9, - "rxn id": "my wennberg" - }, + "species name": "C", + "coeff": 1 + } + ], + "k0_A": 1.2e-6, + "k0_B": 167, + "k0_C": 3, + "kinf_A": 136e6, + "kinf_B": 5, + "kinf_C": 24, + "Fc": 0.9, + "N": 0.8, + "name": "my ternary" + }, + { + "type": "TROE", + "reactants": [ { - "type": "WET_DEPOSITION", - "aerosol phase": "cloud", - "rxn id": "rxn cloud", - "scaling factor": 12.3 - }, + "species name": "B", + "coeff": 1 + } + ], + "products": [ { - "type" : "ARRHENIUS", - "reactants": [ - { - "speices name": "B", - "coeff": 1 - } - ], - "products": [ - { - "speices name": "C", - "coeff": 1 - } - ], - "A": 32.1, - "B": -2.3, - "C": 102.3, - "D": 63.4, - "E": -1.3, - "rxn id": "my arrhenius" + "species name": "C", + "coeff": 1 } - ] + ], + "k0_A": 1.2e-12, + "k0_B": 167, + "k0_C": 3, + "kinf_A": 136, + "kinf_B": 5, + "kinf_C": 24, + "Fc": 0.9, + "N": 0.8, + "name": "my troe" + }, + { + "type": "WENNBERG_NO_RO2", + "reactants": [ + { + "species name": "A", + "coeff": 1 + } + ], + "alkoxy products": [ + { + "species name": "B", + "coeff": 1 + } + ], + "nitrate products": [ + { + "species name": "C", + "coeff": 1 + } + ], + "X": 1.2e-4, + "Y": 167, + "a0": 0.15, + "n": 9, + "name": "my wennberg" + }, + { + "type": "WET_DEPOSITION", + "aerosol phase": "cloud", + "name": "rxn cloud", + "scaling factor": 12.3 + }, + { + "type": "ARRHENIUS", + "reactants": [ + { + "species name": "B", + "coeff": 1 + } + ], + "products": [ + { + "species name": "C", + "coeff": 1 + } + ], + "A": 32.1, + "B": -2.3, + "C": 102.3, + "D": 63.4, + "E": -1.3, + "name": "my arrhenius" } ] } \ No newline at end of file From 83de7887caa3e0b0475fcf06a03e589fc7c65258 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 10 Jan 2024 14:17:09 -0600 Subject: [PATCH 03/26] gas phase --- examples/full_configuration.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 3424e7e..3852140 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -62,6 +62,14 @@ } ], "phases": [ + { + "name": "gas", + "species": [ + "A", + "B", + "C" + ] + }, { "name": "aqueous aerosol", "species": [ From 07c1936cdc5cbaf4db83acff08818166dc75e468 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 11:26:52 -0600 Subject: [PATCH 04/26] added missing reactions, removed ternary chemical activation --- examples/full_configuration.json | 59 ++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 3852140..f49a963 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -96,6 +96,7 @@ "reactions": [ { "type": "HL_PHASE_TRANSFER", + "gas-phase": "gas", "gas-phase species": "H2O2", "aerosol phase": "aqueous aerosol", "aerosol-phase species": "H2O2_aq", @@ -104,6 +105,7 @@ }, { "type": "SIMPOL_PHASE_TRANSFER", + "gas-phase": "gas", "gas-phase species": "ethanol", "aerosol phase": "aqueous aerosol", "aerosol-phase species": "ethanol_aq", @@ -117,6 +119,7 @@ }, { "type": "AQUEOUS_EQUILIBRIUM", + "gas-phase": "gas", "aerosol phase": "aqueous aerosol", "aerosol-phase water": "H2O_aq", "A": 1.14e-2, @@ -186,6 +189,7 @@ }, { "type": "EMISSION", + "gas-phase": "gas", "reactants": [ { "species name": "B", @@ -197,6 +201,7 @@ }, { "type": "FIRST_ORDER_LOSS", + "gas-phase": "gas", "products": [ { "species name": "C", @@ -208,6 +213,7 @@ }, { "type": "PHOTOLYSIS", + "gas-phase": "gas", "reactants": [ { "species name": "B", @@ -225,6 +231,7 @@ }, { "type": "SURFACE", + "gas-phase": "gas", "gas-phase reactant": "foo", "reaction probability": 2.0e-2, "gas-phase products": { @@ -237,34 +244,15 @@ "name": "my surface" }, { - "type": "TERNARY_CHEMICAL_ACTIVATION", + "type": "TROE", + "gas-phase": "gas", "reactants": [ { "species name": "B", "coeff": 1 - } - ], - "products": [ - { - "species name": "C", - "coeff": 1 - } - ], - "k0_A": 1.2e-6, - "k0_B": 167, - "k0_C": 3, - "kinf_A": 136e6, - "kinf_B": 5, - "kinf_C": 24, - "Fc": 0.9, - "N": 0.8, - "name": "my ternary" - }, - { - "type": "TROE", - "reactants": [ + }, { - "species name": "B", + "species name": "M", "coeff": 1 } ], @@ -285,7 +273,8 @@ "name": "my troe" }, { - "type": "WENNBERG_NO_RO2", + "type": "BRANCHED_NO_RO2", + "gas-phase": "gas", "reactants": [ { "species name": "A", @@ -308,16 +297,36 @@ "Y": 167, "a0": 0.15, "n": 9, - "name": "my wennberg" + "name": "my branched" + }, + { + "type" : "TUNNELING", + "A" : 123.45, + "B" : 1200.0, + "C" : 1.0e8, + "reactants": [ + { + "species name": "B", + "coeff": 1 + } + ], + "products": [ + { + "species name": "C", + "coeff": 1 + } + ] }, { "type": "WET_DEPOSITION", + "gas-phase": "gas", "aerosol phase": "cloud", "name": "rxn cloud", "scaling factor": 12.3 }, { "type": "ARRHENIUS", + "gas-phase": "gas", "reactants": [ { "species name": "B", From d0e9c1e7eb30a6175ebb13f15ce1e06fd30be500 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 11:30:21 -0600 Subject: [PATCH 05/26] full names --- examples/full_configuration.json | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index f49a963..7b1b6f4 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -1,5 +1,5 @@ { - "version": "XXX", + "version": "1.0.0", "name": "Full Configuration", "species": [ { @@ -15,8 +15,8 @@ { "name": "H2O2", "HLC(298K) [mol m-3 Pa-1]": 1.011596348, - "HLC exp factor [K]": 6340, - "diffusion coeff [m2 s-1]": 1.46E-05, + "HLC exponential factor [K]": 6340, + "diffusion coefficient [m2 s-1]": 1.46E-05, "N star": 1.74, "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, @@ -24,7 +24,7 @@ }, { "name": "ethanol", - "diffusion coeff [m2 s-1]": 0.95E-05, + "diffusion coefficient [m2 s-1]": 0.95E-05, "N star": 2.55, "molecular weight [kg mol-1]": 0.04607, "absolute tolerance": 1.0e-20 @@ -129,17 +129,17 @@ "reactants": [ { "species name": "A", - "coeff": 2 + "coefficient": 2 } ], "products": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 }, { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "name": "my aqueous eq" @@ -152,17 +152,17 @@ "reactants": [ { "species name": "A", - "coeff": 1 + "coefficient": 1 }, { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "name": "my condensed arrhenius" @@ -175,13 +175,13 @@ "reactants": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "scaling factor": 12.3, @@ -193,7 +193,7 @@ "reactants": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "name": "my emission", @@ -205,7 +205,7 @@ "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "name": "my first order loss", @@ -217,13 +217,13 @@ "reactants": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "name": "photo B", @@ -249,17 +249,17 @@ "reactants": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 }, { "species name": "M", - "coeff": 1 + "coefficient": 1 } ], "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "k0_A": 1.2e-12, @@ -278,19 +278,19 @@ "reactants": [ { "species name": "A", - "coeff": 1 + "coefficient": 1 } ], "alkoxy products": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "nitrate products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "X": 1.2e-4, @@ -307,13 +307,13 @@ "reactants": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ] }, @@ -330,13 +330,13 @@ "reactants": [ { "species name": "B", - "coeff": 1 + "coefficient": 1 } ], "products": [ { "species name": "C", - "coeff": 1 + "coefficient": 1 } ], "A": 32.1, From 8e32b08b00305978abe33b45f12d974b50adc438 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 11:31:06 -0600 Subject: [PATCH 06/26] removing hyphen --- examples/full_configuration.json | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 7b1b6f4..0ffc53a 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -96,7 +96,7 @@ "reactions": [ { "type": "HL_PHASE_TRANSFER", - "gas-phase": "gas", + "gas phase": "gas", "gas-phase species": "H2O2", "aerosol phase": "aqueous aerosol", "aerosol-phase species": "H2O2_aq", @@ -105,7 +105,7 @@ }, { "type": "SIMPOL_PHASE_TRANSFER", - "gas-phase": "gas", + "gas phase": "gas", "gas-phase species": "ethanol", "aerosol phase": "aqueous aerosol", "aerosol-phase species": "ethanol_aq", @@ -119,7 +119,7 @@ }, { "type": "AQUEOUS_EQUILIBRIUM", - "gas-phase": "gas", + "gas phase": "gas", "aerosol phase": "aqueous aerosol", "aerosol-phase water": "H2O_aq", "A": 1.14e-2, @@ -189,7 +189,7 @@ }, { "type": "EMISSION", - "gas-phase": "gas", + "gas phase": "gas", "reactants": [ { "species name": "B", @@ -201,7 +201,7 @@ }, { "type": "FIRST_ORDER_LOSS", - "gas-phase": "gas", + "gas phase": "gas", "products": [ { "species name": "C", @@ -213,7 +213,7 @@ }, { "type": "PHOTOLYSIS", - "gas-phase": "gas", + "gas phase": "gas", "reactants": [ { "species name": "B", @@ -231,7 +231,7 @@ }, { "type": "SURFACE", - "gas-phase": "gas", + "gas phase": "gas", "gas-phase reactant": "foo", "reaction probability": 2.0e-2, "gas-phase products": { @@ -245,7 +245,7 @@ }, { "type": "TROE", - "gas-phase": "gas", + "gas phase": "gas", "reactants": [ { "species name": "B", @@ -274,7 +274,7 @@ }, { "type": "BRANCHED_NO_RO2", - "gas-phase": "gas", + "gas phase": "gas", "reactants": [ { "species name": "A", @@ -319,14 +319,14 @@ }, { "type": "WET_DEPOSITION", - "gas-phase": "gas", + "gas phase": "gas", "aerosol phase": "cloud", "name": "rxn cloud", "scaling factor": 12.3 }, { "type": "ARRHENIUS", - "gas-phase": "gas", + "gas phase": "gas", "reactants": [ { "species name": "B", From f49895dd797a2805b4e9c891167a8689eadded5d Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 11:31:38 -0600 Subject: [PATCH 07/26] removing units on condensed phase things --- examples/full_configuration.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 0ffc53a..375d084 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -148,7 +148,6 @@ "type": "CONDENSED_PHASE_ARRHENIUS", "aerosol phase": "aqueous aerosol", "aerosol-phase water": "H2O_aq", - "units": "M", "reactants": [ { "species name": "A", @@ -171,7 +170,6 @@ "type": "CONDENSED_PHASE_PHOTOLYSIS", "aerosol phase": "aqueous aerosol", "aerosol-phase water": "H2O_aq", - "units": "M", "reactants": [ { "species name": "B", From 437afcc58f52d59dbb5da3389b959d868d6950de Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 11:34:00 -0600 Subject: [PATCH 08/26] aerosol phase species --- examples/full_configuration.json | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 375d084..aadc49b 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -150,17 +150,17 @@ "aerosol-phase water": "H2O_aq", "reactants": [ { - "species name": "A", + "species name": "H2O2_aq", "coefficient": 1 }, { - "species name": "B", + "species name": "H2O_aq", "coefficient": 1 } ], "products": [ { - "species name": "C", + "species name": "ethanol_aq", "coefficient": 1 } ], @@ -172,13 +172,17 @@ "aerosol-phase water": "H2O_aq", "reactants": [ { - "species name": "B", + "species name": "H2O2_aq", + "coefficient": 1 + }, + { + "species name": "H2O_aq", "coefficient": 1 } ], "products": [ { - "species name": "C", + "species name": "ethanol_aq", "coefficient": 1 } ], From aed445e870f90fa5cb53afbf300e6e2710b69080 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 12:10:40 -0600 Subject: [PATCH 09/26] refining interface --- CMakeLists.txt | 5 ++ Testing/Temporary/CTestCostData.txt | 1 + Testing/Temporary/LastTest.log | 3 + .../mechanism_configuration/conversions.hpp | 7 ++ .../mechanism_configuration/parser.hpp | 64 +++++-------------- include/open_atmos/types.hpp | 17 +++++ src/parser.cpp | 42 +++++++++++- test/CMakeLists.txt | 1 + test/integration/CMakeLists.txt | 9 +++ test/{unit => integration}/test_parser.cpp | 0 test/unit/CMakeLists.txt | 2 +- test/unit/test_parse_species.cpp | 10 +++ 12 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 Testing/Temporary/CTestCostData.txt create mode 100644 Testing/Temporary/LastTest.log create mode 100644 include/open_atmos/mechanism_configuration/conversions.hpp create mode 100644 include/open_atmos/types.hpp create mode 100644 test/integration/CMakeLists.txt rename test/{unit => integration}/test_parser.cpp (100%) create mode 100644 test/unit/test_parse_species.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bf196d..515931b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,11 @@ add_subdirectory(src) if(PROJECT_IS_TOP_LEVEL AND ENABLE_TESTS) enable_testing() add_subdirectory(test) + + # Copy example folder to use as test data + + add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/examples ${CMAKE_BINARY_DIR}/examples) endif() ################################################################################ diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt new file mode 100644 index 0000000..ed97d53 --- /dev/null +++ b/Testing/Temporary/CTestCostData.txt @@ -0,0 +1 @@ +--- diff --git a/Testing/Temporary/LastTest.log b/Testing/Temporary/LastTest.log new file mode 100644 index 0000000..7c461c9 --- /dev/null +++ b/Testing/Temporary/LastTest.log @@ -0,0 +1,3 @@ +Start testing: Jan 11 11:49 CST +---------------------------------------------------------- +End testing: Jan 11 11:49 CST diff --git a/include/open_atmos/mechanism_configuration/conversions.hpp b/include/open_atmos/mechanism_configuration/conversions.hpp new file mode 100644 index 0000000..5eea4ae --- /dev/null +++ b/include/open_atmos/mechanism_configuration/conversions.hpp @@ -0,0 +1,7 @@ +// Copyright (C) 2023-2024 National Center for Atmospheric Research, University of Illinois at Urbana-Champaign +// +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +constexpr double MolesM3ToMoleculesCm3 = 1.0e-6 * 6.02214076e23; \ No newline at end of file diff --git a/include/open_atmos/mechanism_configuration/parser.hpp b/include/open_atmos/mechanism_configuration/parser.hpp index d3f0645..0d719ad 100644 --- a/include/open_atmos/mechanism_configuration/parser.hpp +++ b/include/open_atmos/mechanism_configuration/parser.hpp @@ -4,10 +4,15 @@ #pragma once -#include +#include #include #include #include +#include +#include +#include + +#include namespace open_atmos { @@ -19,10 +24,10 @@ namespace open_atmos None, InvalidKey, UnknownKey, - InvalidCAMPFilePath, + InvalidFilePath, NoConfigFilesFound, - CAMPFilesSectionNotFound, - CAMPDataSectionNotFound, + FilesSectionNotFound, + DataSectionNotFound, InvalidSpecies, InvalidMechanism, ObjectTypeNotFound, @@ -30,56 +35,21 @@ namespace open_atmos ContainsNonStandardKey, MutuallyExclusiveOption }; + std::string configParseStatusToString(const ConfigParseStatus &status); - constexpr double MolesM3ToMoleculesCm3 = 1.0e-6 * 6.02214076e23; - - inline 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::InvalidCAMPFilePath: - return "InvalidCAMPFilePath"; - case ConfigParseStatus::NoConfigFilesFound: - return "NoConfigFilesFound"; - case ConfigParseStatus::CAMPFilesSectionNotFound: - return "CAMPFilesSectionNotFound"; - case ConfigParseStatus::CAMPDataSectionNotFound: - return "CAMPDataSectionNotFound"; - case ConfigParseStatus::InvalidSpecies: - return "InvalidSpecies"; - case ConfigParseStatus::InvalidMechanism: - return "InvalidMechanism"; - case ConfigParseStatus::ObjectTypeNotFound: - return "ObjectTypeNotFound"; - case ConfigParseStatus::RequiredKeyNotFound: - return "RequiredKeyNotFound"; - case ConfigParseStatus::ContainsNonStandardKey: - return "ContainsNonStandardKey"; - case ConfigParseStatus::MutuallyExclusiveOption: - return "MutuallyExclusiveOption"; - default: - return "Unknown"; - } - } + struct Mechanism + { + std::vector species_arr_; + std::unordered_map phases_; + }; class JsonReaderPolicy { public: - /// @brief Parse configures /// @param config_path Path to a the CAMP configuration directory or file /// @return True for successful parsing - ConfigParseStatus Parse(const std::filesystem::path &config_path); - - private: + std::pair Parse(const std::filesystem::path &config_path); }; /// @brief Public interface to read and parse config @@ -90,7 +60,7 @@ namespace open_atmos /// @brief Reads and parses configures /// @param config_dir Path to a the configuration directory /// @return an enum indicating the success or failure of the parse - [[nodiscard]] ConfigParseStatus ReadAndParse(const std::filesystem::path &config_dir) + [[nodiscard]] std::pair ReadAndParse(const std::filesystem::path &config_dir) { return this->Parse(config_dir); } diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp new file mode 100644 index 0000000..cc569ec --- /dev/null +++ b/include/open_atmos/types.hpp @@ -0,0 +1,17 @@ +// Copyright (C) 2023-2024 National Center for Atmospheric Research, University of Illinois at Urbana-Champaign +// +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +namespace open_atmos +{ + namespace types + { + struct Species { + }; + + struct Phase { + }; + } +} \ No newline at end of file diff --git a/src/parser.cpp b/src/parser.cpp index 652d998..d5426e0 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -9,11 +9,49 @@ namespace open_atmos { namespace mechanism_configuration { + // explicit template instanatiation template class ConfigurationReader; - ConfigParseStatus JsonReaderPolicy::Parse(const std::filesystem::path &config_path) { - return ConfigParseStatus::Success; + 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::InvalidCAMPFilePath: + return "InvalidCAMPFilePath"; + case ConfigParseStatus::NoConfigFilesFound: + return "NoConfigFilesFound"; + case ConfigParseStatus::CAMPFilesSectionNotFound: + return "CAMPFilesSectionNotFound"; + case ConfigParseStatus::CAMPDataSectionNotFound: + return "CAMPDataSectionNotFound"; + case ConfigParseStatus::InvalidSpecies: + return "InvalidSpecies"; + case ConfigParseStatus::InvalidMechanism: + return "InvalidMechanism"; + case ConfigParseStatus::ObjectTypeNotFound: + return "ObjectTypeNotFound"; + case ConfigParseStatus::RequiredKeyNotFound: + return "RequiredKeyNotFound"; + case ConfigParseStatus::ContainsNonStandardKey: + return "ContainsNonStandardKey"; + case ConfigParseStatus::MutuallyExclusiveOption: + return "MutuallyExclusiveOption"; + default: + return "Unknown"; + } + } + + std::pair JsonReaderPolicy::Parse(const std::filesystem::path &config_path) { + return {ConfigParseStatus::Success, Mechanism()}; } } } \ No newline at end of file diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 059f2a2..e138dc8 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(integration) add_subdirectory(unit) \ No newline at end of file diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt new file mode 100644 index 0000000..7dbc1e2 --- /dev/null +++ b/test/integration/CMakeLists.txt @@ -0,0 +1,9 @@ +################################################################################ +# Test utilities + +include(test_util) + +################################################################################ +# Tests + +create_standard_test(NAME parser SOURCES test_parser.cpp) \ No newline at end of file diff --git a/test/unit/test_parser.cpp b/test/integration/test_parser.cpp similarity index 100% rename from test/unit/test_parser.cpp rename to test/integration/test_parser.cpp diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 3fc656b..113c111 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -6,7 +6,7 @@ include(test_util) ################################################################################ # Tests -create_standard_test(NAME parser SOURCES test_parser.cpp) +create_standard_test(NAME parse_species SOURCES test_parse_species.cpp) ################################################################################ # Copy test data diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp new file mode 100644 index 0000000..b5b9873 --- /dev/null +++ b/test/unit/test_parse_species.cpp @@ -0,0 +1,10 @@ +#include + +#include + +using namespace open_atmos::mechanism_configuration; + +TEST(Parser, Returns) +{ + ConfigurationReader reader; +} \ No newline at end of file From 566cd78b0e6aa96f75157dddc188a328c133936f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 12:19:33 -0600 Subject: [PATCH 10/26] absolut tolerance prefix --- examples/full_configuration.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index aadc49b..6635e4c 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -20,26 +20,26 @@ "N star": 1.74, "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, - "absolute tolerance": 1.0e-10 + "__absolute tolerance": 1.0e-10 }, { "name": "ethanol", "diffusion coefficient [m2 s-1]": 0.95E-05, "N star": 2.55, "molecular weight [kg mol-1]": 0.04607, - "absolute tolerance": 1.0e-20 + "__absolute tolerance": 1.0e-20 }, { "name": "ethanol_aq", "molecular weight [kg mol-1]": 0.04607, "density [kg m-3]": 1000.0, - "absolute tolerance": 1.0e-20 + "__absolute tolerance": 1.0e-20 }, { "name": "H2O2_aq", "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, - "absolute tolerance": 1.0e-10 + "__absolute tolerance": 1.0e-10 }, { "name": "H2O_aq", @@ -51,14 +51,14 @@ "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.5, "density [kg m-3]": 1000.0, - "absolute tolerance": 1.0e-20 + "__absolute tolerance": 1.0e-20 }, { "name": "more aerosol stuff", "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.2, "density [kg m-3]": 1000.0, - "absolute tolerance": 1.0e-20 + "__absolute tolerance": 1.0e-20 } ], "phases": [ From ad07e4f0f2c6a37cfd8edef44b7fb985e2877934 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 11 Jan 2024 17:00:54 -0600 Subject: [PATCH 11/26] mostly parsed species --- .clang-format | 26 ++ .github/workflows/clang-format.yml | 58 ++++ .../mechanism_configuration/parser.hpp | 57 ++-- .../mechanism_configuration/validation.hpp | 71 +++++ .../mechanism_configuration/version.hpp | 2 +- include/open_atmos/types.hpp | 26 +- src/CMakeLists.txt | 2 +- src/parser.cpp | 274 +++++++++++++++--- src/version.hpp.in | 2 +- test/integration/CMakeLists.txt | 2 +- .../{test_parser.cpp => test_json_parser.cpp} | 4 +- test/unit/test_parse_species.cpp | 8 +- test/unit/unit_configs/.gitkeep | 0 test/unit/unit_configs/species.json | 30 ++ 14 files changed, 477 insertions(+), 85 deletions(-) create mode 100644 .clang-format create mode 100644 .github/workflows/clang-format.yml create mode 100644 include/open_atmos/mechanism_configuration/validation.hpp rename test/integration/{test_parser.cpp => test_json_parser.cpp} (66%) delete mode 100644 test/unit/unit_configs/.gitkeep create mode 100644 test/unit/unit_configs/species.json diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..edb2924 --- /dev/null +++ b/.clang-format @@ -0,0 +1,26 @@ +--- +BasedOnStyle: Google +AlignAfterOpenBracket: 'AlwaysBreak' +AllowAllConstructorInitializersOnNextLine: 'false' +AllowAllParametersOfDeclarationOnNextLine: 'false' +AlignConsecutiveMacros: 'true' +AllowShortCaseLabelsOnASingleLine: 'true' +AllowShortFunctionsOnASingleLine: 'None' +AllowShortIfStatementsOnASingleLine: 'Never' +AllowShortLoopsOnASingleLine: 'false' +BreakBeforeBraces: Allman +BinPackArguments: 'false' +BinPackParameters: 'false' +Cpp11BracedListStyle: 'false' +ColumnLimit: 150 +IndentWidth: 2 +IndentPPDirectives: AfterHash +NamespaceIndentation: All +PackConstructorInitializers: 'Never' +SpaceAfterTemplateKeyword: 'false' +SpaceBeforeCtorInitializerColon: 'true' +SpaceBeforeInheritanceColon: 'true' +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: 'true' +SpaceInEmptyBlock: true +Standard: 'Latest' \ No newline at end of file diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml new file mode 100644 index 0000000..d5e3131 --- /dev/null +++ b/.github/workflows/clang-format.yml @@ -0,0 +1,58 @@ +name: Clang-Format + +on: + push: + branches: + - main + +jobs: + format: + name: Run Clang-Format + runs-on: ubuntu-latest + + steps: + - name: Install Clang-Format + run: sudo apt-get update && sudo apt-get install clang-format + + - name: Check out code, run clang format, push changes + uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Format code + run: | + find include -type f \( -name '*.hpp' -o -name '*.h' \) -exec clang-format -i --style=file {} + + find src -type f \( -name '*.cu' -o -name '*.hpp' -o -name '*.h' -o -name '*.cpp' \) -exec clang-format -i --style=file {} + + find test -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.cpp' \) ! -path 'test/tutorial/*' -exec clang-format -i --style=file {} + + + - name: Check for changes + id: check-changes + run: | + git diff --exit-code + continue-on-error: true + + - name: Commit and push changes + # a failue of this step means changes were detected + if: steps.check-changes.outcome != 'success' + run: | + git config --global user.name "GitHub Actions" + git config --global user.email "actions@github.com" + git commit -am "Auto-format code using Clang-Format" || echo "No changes to commit" + + - name: Push changes to main-formatting branch + # a failue of this step means changes were detected + if: steps.check-changes.outcome != 'success' + run: | + git push origin HEAD:main-formatting + + - name: Create Pull Request + # a failue of this step means changes were detected + if: steps.check-changes.outcome != 'success' + uses: peter-evans/create-pull-request@v3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "Auto-format code using Clang-Format" + title: "Auto-format code changes" + body: "This is an automated pull request to apply code formatting using Clang-Format." + branch: "main-formatting" \ No newline at end of file diff --git a/include/open_atmos/mechanism_configuration/parser.hpp b/include/open_atmos/mechanism_configuration/parser.hpp index 0d719ad..547b585 100644 --- a/include/open_atmos/mechanism_configuration/parser.hpp +++ b/include/open_atmos/mechanism_configuration/parser.hpp @@ -4,15 +4,15 @@ #pragma once -#include #include #include #include -#include +#include +#include #include +#include #include - -#include +#include namespace open_atmos { @@ -26,44 +26,33 @@ namespace open_atmos UnknownKey, InvalidFilePath, NoConfigFilesFound, - FilesSectionNotFound, DataSectionNotFound, InvalidSpecies, - InvalidMechanism, ObjectTypeNotFound, RequiredKeyNotFound, ContainsNonStandardKey, - MutuallyExclusiveOption + MutuallyExclusiveOption, + InvalidVersion }; std::string configParseStatusToString(const ConfigParseStatus &status); - struct Mechanism - { - std::vector species_arr_; - std::unordered_map phases_; - }; - - class JsonReaderPolicy + class JsonParser { - public: - /// @brief Parse configures - /// @param config_path Path to a the CAMP configuration directory or file - /// @return True for successful parsing - std::pair Parse(const std::filesystem::path &config_path); - }; + public: + /// @brief Reads a configuration from a json object + /// @param object a json object + /// @return A pair containing the parsing status and mechanism + std::pair Parse(const nlohmann::json &object); - /// @brief Public interface to read and parse config - template - class ConfigurationReader : public ConfigTypePolicy - { - public: - /// @brief Reads and parses configures - /// @param config_dir Path to a the configuration directory - /// @return an enum indicating the success or failure of the parse - [[nodiscard]] std::pair ReadAndParse(const std::filesystem::path &config_dir) - { - return this->Parse(config_dir); - } + /// @brief Reads a configuration from a file path + /// @param file_path A path to single json configuration + /// @return A pair containing the parsing status and mechanism + std::pair Parse(const std::filesystem::path &file_path); + + /// @brief Reads a configuration from a file path + /// @param file_path A path to single json configuration + /// @return A pair containing the parsing status and mechanism + std::pair Parse(const std::string &file_path); }; - } -} + } // namespace mechanism_configuration +} // namespace open_atmos diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp new file mode 100644 index 0000000..51833f6 --- /dev/null +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -0,0 +1,71 @@ +// Copyright (C) 2023-2024 National Center for Atmospheric Research, University of Illinois at Urbana-Champaign +// +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include + +namespace open_atmos +{ + namespace validation + { + struct Keys + { + const std::string version = "version"; + const std::string name = "name"; + + // Configuration + const std::string species = "species"; + const std::string phases = "phases"; + const std::string reactions = "reactions"; + + // Species + const std::string tracer_type = "tracer type"; + const std::string absolute_tolerance = "absolute tolerance"; + const std::string diffusion_coefficient = "diffusion coefficient [m2 s-1]"; + const std::string molecular_weight = "molecular weight [kg mol-1]"; + const std::string henrys_law_constant_298 = "HLC(298K) [mol m-3 Pa-1]"; + const std::string henrys_law_constant_exponential_factor = "HLC exponential factor [K]"; + const std::string phase = "phase"; + const std::string n_star = "N star"; + const std::string density = "density [kg m-3]"; + } keys; + + // Initialize static const members of Keys + + struct Configuration + { + const std::vector required_keys{ keys.version, keys.species, keys.phases, keys.reactions }; + const std::vector optional_keys{ keys.name }; + } configuration; + + struct Species + { + const std::vector required_keys{ keys.name }; + const std::vector optional_keys{ keys.tracer_type, + keys.absolute_tolerance, + keys.diffusion_coefficient, + keys.molecular_weight, + keys.phase, + keys.henrys_law_constant_298, + keys.henrys_law_constant_exponential_factor, + keys.n_star, + keys.density }; + } species; + + struct Phase + { + const std::vector required_keys; + const std::vector optional_keys; + } phase; + + struct Mechanism + { + const std::vector required_keys; + const std::vector optional_keys; + } mechanism; + + } // namespace validation +} // namespace open_atmos \ No newline at end of file diff --git a/include/open_atmos/mechanism_configuration/version.hpp b/include/open_atmos/mechanism_configuration/version.hpp index af59680..7ceb4fe 100644 --- a/include/open_atmos/mechanism_configuration/version.hpp +++ b/include/open_atmos/mechanism_configuration/version.hpp @@ -9,7 +9,7 @@ namespace open_atmos extern "C" { #endif - const char* getmechanism_configurationVersion() + const char* getVersionString() { return "1.0.0"; } diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index cc569ec..9f0e117 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -4,14 +4,32 @@ #pragma once +#include +#include + namespace open_atmos { namespace types { - struct Species { + struct Species + { + std::string name; + + std::map optional_properties; + + std::unordered_map unknown_properties; }; - struct Phase { + struct Phase + { }; - } -} \ No newline at end of file + + struct Mechanism + { + std::string name; // optional + std::vector species; + std::unordered_map phases; + }; + + } // namespace types +} // namespace open_atmos \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 186904e..861f1b9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,4 +18,4 @@ target_include_directories(mechanism_configuration $ ) -target_link_libraries(mechanism_configuration PRIVATE nlohmann_json::nlohmann_json) \ No newline at end of file +target_link_libraries(mechanism_configuration PUBLIC nlohmann_json::nlohmann_json) \ No newline at end of file diff --git a/src/parser.cpp b/src/parser.cpp index d5426e0..599b63b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -3,55 +3,251 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include +#include +#include namespace open_atmos { namespace mechanism_configuration { + using nlohmann::json; - // explicit template instanatiation - template class ConfigurationReader; - - std::string configParseStatusToString(const ConfigParseStatus &status) + 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::InvalidCAMPFilePath: - return "InvalidCAMPFilePath"; - case ConfigParseStatus::NoConfigFilesFound: - return "NoConfigFilesFound"; - case ConfigParseStatus::CAMPFilesSectionNotFound: - return "CAMPFilesSectionNotFound"; - case ConfigParseStatus::CAMPDataSectionNotFound: - return "CAMPDataSectionNotFound"; - case ConfigParseStatus::InvalidSpecies: - return "InvalidSpecies"; - case ConfigParseStatus::InvalidMechanism: - return "InvalidMechanism"; - case ConfigParseStatus::ObjectTypeNotFound: - return "ObjectTypeNotFound"; - case ConfigParseStatus::RequiredKeyNotFound: - return "RequiredKeyNotFound"; - case ConfigParseStatus::ContainsNonStandardKey: - return "ContainsNonStandardKey"; - case ConfigParseStatus::MutuallyExclusiveOption: - return "MutuallyExclusiveOption"; - default: - return "Unknown"; + 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::NoConfigFilesFound: return "NoConfigFilesFound"; + case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; + case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; + case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound"; + case ConfigParseStatus::ContainsNonStandardKey: return "ContainsNonStandardKey"; + case ConfigParseStatus::MutuallyExclusiveOption: return "MutuallyExclusiveOption"; + default: return "Unknown"; + } + } + + std::vector + 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)); + + return remaining; + } + + /// @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 + + // 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.starts_with("__")) + { + std::cerr << "Non-standard key '" << key << "' found in object" << object << std::endl; + + return ConfigParseStatus::ContainsNonStandardKey; + } + } + return ConfigParseStatus::Success; + } + + std::pair> ParseSpecies(const json& objects) + { + ConfigParseStatus status = ConfigParseStatus::Success; + std::vector all_species; + + for (const auto& object : objects) + { + types::Species species; + auto status = ValidateSchema(object, validation::species.required_keys, validation::species.optional_keys); + if (status != ConfigParseStatus::Success) + { + break; + } + + std::cout << "object:\n" << object.dump(4) << std::endl; + + std::string name = object[validation::keys.name].get(); + std::map properties{}; + for (const auto& key : validation::species.optional_keys) + { + if (object.contains(key)) + { + double val = object[key].get(); + properties[key] = val; + } + } + + auto comments = GetComments(object, validation::species.required_keys, validation::species.optional_keys); + + std::unordered_map unknown_properties; + for (const auto& key : validation::species.optional_keys) + { + std::string val = object[key].dump(); + unknown_properties[key] = val; + } + + species.name = name; + species.optional_properties = properties; + species.unknown_properties = unknown_properties; + + all_species.push_back(species); } + + return { status, all_species }; + } + + std::pair JsonParser::Parse(const std::string& file_path) + { + return JsonParser::Parse(std::filesystem::path(file_path)); + } + + std::pair JsonParser::Parse(const std::filesystem::path& file_path) + { + ConfigParseStatus status; + + if (!std::filesystem::exists(file_path) || std::filesystem::is_directory(file_path)) + { + status = ConfigParseStatus::InvalidFilePath; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return { status, types::Mechanism() }; + } + + json config = json::parse(std::ifstream(file_path)); + + return JsonParser::Parse(config); } - std::pair JsonReaderPolicy::Parse(const std::filesystem::path &config_path) { - return {ConfigParseStatus::Success, Mechanism()}; + std::pair JsonParser::Parse(const nlohmann::json& object) + { + ConfigParseStatus status; + types::Mechanism mechanism; + + status = ValidateSchema(object, validation::configuration.required_keys, validation::configuration.optional_keys); + + if (status != ConfigParseStatus::Success) + { + std::string msg = configParseStatusToString(status); + std::cerr << "[" << msg << "] Invalid top level configuration." << std::endl; + return { status, mechanism }; + } + + std::string version = object[validation::keys.version].get(); + + if (version != getVersionString()) + { + status = ConfigParseStatus::InvalidVersion; + std::string msg = configParseStatusToString(status); + std::cerr << "[" << msg << "] This parser supports version " << getVersionString() << " and you requested version " << version + << ". Please download the appropriate version of the parser or switch to the supported format's version." << std::endl; + } + + std::string name = object[validation::keys.name].get(); + mechanism.name = name; + + auto species_parsing = ParseSpecies(object["species"]); + + if (species_parsing.first != ConfigParseStatus::Success) + { + status = species_parsing.first; + std::string msg = configParseStatusToString(status); + std::cerr << "[" << msg << "] Failed to parse the species." << std::endl; + } + + mechanism.species = species_parsing.second; + + return { status, mechanism }; } - } -} \ No newline at end of file + } // namespace mechanism_configuration +} // namespace open_atmos \ No newline at end of file diff --git a/src/version.hpp.in b/src/version.hpp.in index 2948896..3274919 100644 --- a/src/version.hpp.in +++ b/src/version.hpp.in @@ -9,7 +9,7 @@ namespace open_atmos extern "C" { #endif - const char* getmechanism_configurationVersion() + const char* getVersionString() { return "@mechanism_configuration_VERSION@"; } diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 7dbc1e2..5380dda 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -6,4 +6,4 @@ include(test_util) ################################################################################ # Tests -create_standard_test(NAME parser SOURCES test_parser.cpp) \ No newline at end of file +create_standard_test(NAME json_parser SOURCES test_json_parser.cpp) \ No newline at end of file diff --git a/test/integration/test_parser.cpp b/test/integration/test_json_parser.cpp similarity index 66% rename from test/integration/test_parser.cpp rename to test/integration/test_json_parser.cpp index b5b9873..fc963bd 100644 --- a/test/integration/test_parser.cpp +++ b/test/integration/test_json_parser.cpp @@ -4,7 +4,7 @@ using namespace open_atmos::mechanism_configuration; -TEST(Parser, Returns) +TEST(JsonParser, Returns) { - ConfigurationReader reader; + JsonParser parser; } \ No newline at end of file diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp index b5b9873..3076f5e 100644 --- a/test/unit/test_parse_species.cpp +++ b/test/unit/test_parse_species.cpp @@ -4,7 +4,11 @@ using namespace open_atmos::mechanism_configuration; -TEST(Parser, Returns) +TEST(JsonParser, CanParseSpecies) { - ConfigurationReader reader; + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/species.json")); + + EXPECT_EQ(status, ConfigParseStatus::Success); + EXPECT_EQ(mechanism.species.size(), 3); } \ No newline at end of file diff --git a/test/unit/unit_configs/.gitkeep b/test/unit/unit_configs/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/test/unit/unit_configs/species.json b/test/unit/unit_configs/species.json new file mode 100644 index 0000000..4acdbc2 --- /dev/null +++ b/test/unit/unit_configs/species.json @@ -0,0 +1,30 @@ +{ + "version": "1.0.0", + "name": "Full Configuration", + "species": [ + { + "name": "A", + "__absolute tolerance": 1.0e-30 + }, + { + "name": "H2O2", + "HLC(298K) [mol m-3 Pa-1]": 1.011596348, + "HLC exponential factor [K]": 6340, + "diffusion coefficient [m2 s-1]": 1.46E-05, + "N star": 1.74, + "molecular weight [kg mol-1]": 0.0340147, + "density [kg m-3]": 1000.0, + "__absolute tolerance": 1.0e-10 + }, + { + "name": "aerosol stuff", + "phase": "AEROSOL", + "molecular weight [kg mol-1]": 0.5, + "density [kg m-3]": 1000.0, + "tracer type" : "CONSTANT", + "__absolute tolerance": 1.0e-20 + } + ], + "phases": [ ], + "reactions": [ ] +} \ No newline at end of file From 29fb30ea41b1aeafd6313e910b4ed19666ca6e82 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 09:29:50 -0600 Subject: [PATCH 12/26] checking duplicate species --- .../mechanism_configuration/parser.hpp | 3 +- .../mechanism_configuration/validation.hpp | 3 +- include/open_atmos/types.hpp | 4 +- src/parser.cpp | 34 +++++++++++++--- test/integration/test_json_parser.cpp | 2 + test/unit/test_parse_species.cpp | 39 ++++++++++++++++++- test/unit/unit_configs/duplicate_species.json | 16 ++++++++ .../{species.json => valid_species.json} | 2 + 8 files changed, 91 insertions(+), 12 deletions(-) create mode 100644 test/unit/unit_configs/duplicate_species.json rename test/unit/unit_configs/{species.json => valid_species.json} (94%) diff --git a/include/open_atmos/mechanism_configuration/parser.hpp b/include/open_atmos/mechanism_configuration/parser.hpp index 547b585..8f6ab7b 100644 --- a/include/open_atmos/mechanism_configuration/parser.hpp +++ b/include/open_atmos/mechanism_configuration/parser.hpp @@ -32,7 +32,8 @@ namespace open_atmos RequiredKeyNotFound, ContainsNonStandardKey, MutuallyExclusiveOption, - InvalidVersion + InvalidVersion, + DuplicateSpeciesDetected }; std::string configParseStatusToString(const ConfigParseStatus &status); diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index 51833f6..8913281 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -43,12 +43,11 @@ namespace open_atmos struct Species { - const std::vector required_keys{ keys.name }; + const std::vector required_keys{ keys.name, keys.phase }; const std::vector optional_keys{ keys.tracer_type, keys.absolute_tolerance, keys.diffusion_coefficient, keys.molecular_weight, - keys.phase, keys.henrys_law_constant_298, keys.henrys_law_constant_exponential_factor, keys.n_star, diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index 9f0e117..59c0436 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -14,8 +14,10 @@ namespace open_atmos struct Species { std::string name; + std::string phase; - std::map optional_properties; + std::map optional_numerical_properties; + std::map optional_string_properties; std::unordered_map unknown_properties; }; diff --git a/src/parser.cpp b/src/parser.cpp index 599b63b..67afb72 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -27,6 +27,7 @@ namespace open_atmos case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound"; case ConfigParseStatus::ContainsNonStandardKey: return "ContainsNonStandardKey"; case ConfigParseStatus::MutuallyExclusiveOption: return "MutuallyExclusiveOption"; + case ConfigParseStatus::DuplicateSpeciesDetected: return "DuplicateSpeciesDetected"; default: return "Unknown"; } } @@ -155,35 +156,56 @@ namespace open_atmos break; } - std::cout << "object:\n" << object.dump(4) << std::endl; + // std::cout << "object:\n" << object.dump(4) << std::endl; std::string name = object[validation::keys.name].get(); - std::map properties{}; + std::string phase = object[validation::keys.phase].get(); + + std::map numerical_properties{}; + std::map string_properties{}; for (const auto& key : validation::species.optional_keys) { if (object.contains(key)) { - double val = object[key].get(); - properties[key] = val; + if (key == validation::keys.tracer_type) { + std::string val = object[key].get(); + string_properties[key] = val; + } + else { + double val = object[key].get(); + numerical_properties[key] = val; + } } } auto comments = GetComments(object, validation::species.required_keys, validation::species.optional_keys); std::unordered_map unknown_properties; - for (const auto& key : validation::species.optional_keys) + for (const auto& key : comments) { std::string val = object[key].dump(); unknown_properties[key] = val; } species.name = name; - species.optional_properties = properties; + species.phase = phase; + species.optional_numerical_properties = numerical_properties; + species.optional_string_properties = string_properties; species.unknown_properties = unknown_properties; all_species.push_back(species); } + for(size_t i = 0; i < all_species.size(); ++i) { + for(size_t j = i+1; j < all_species.size(); ++j) { + if (all_species[i].name == all_species[j].name) { + status = ConfigParseStatus::DuplicateSpeciesDetected; + } + break; + } + if (status != ConfigParseStatus::Success) break; + } + return { status, all_species }; } diff --git a/test/integration/test_json_parser.cpp b/test/integration/test_json_parser.cpp index fc963bd..c29a40a 100644 --- a/test/integration/test_json_parser.cpp +++ b/test/integration/test_json_parser.cpp @@ -7,4 +7,6 @@ using namespace open_atmos::mechanism_configuration; TEST(JsonParser, Returns) { JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("examples/full_configuration.json")); + EXPECT_EQ(status, ConfigParseStatus::Success); } \ No newline at end of file diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp index 3076f5e..c814b68 100644 --- a/test/unit/test_parse_species.cpp +++ b/test/unit/test_parse_species.cpp @@ -4,11 +4,46 @@ using namespace open_atmos::mechanism_configuration; -TEST(JsonParser, CanParseSpecies) +TEST(JsonParser, CanParseValidSpecies) { JsonParser parser; - auto [status, mechanism] = parser.Parse(std::string("unit_configs/species.json")); + auto [status, mechanism] = parser.Parse(std::string("unit_configs/valid_species.json")); EXPECT_EQ(status, ConfigParseStatus::Success); EXPECT_EQ(mechanism.species.size(), 3); + + EXPECT_EQ(mechanism.species[0].name, "A"); + EXPECT_EQ(mechanism.species[0].phase, "gas"); + EXPECT_EQ(mechanism.species[0].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.species[0].unknown_properties["__absolute tolerance"], "1e-30"); + + EXPECT_EQ(mechanism.species[1].name, "H2O2"); + EXPECT_EQ(mechanism.species[1].phase, "gas"); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties.size(), 6); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties["HLC(298K) [mol m-3 Pa-1]"], 1.011596348); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties["HLC exponential factor [K]"], 6340); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties["diffusion coefficient [m2 s-1]"], 1.46e-05); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties["N star"], 1.74); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties["molecular weight [kg mol-1]"], 0.0340147); + EXPECT_EQ(mechanism.species[1].optional_numerical_properties["density [kg m-3]"], 1000.0); + EXPECT_EQ(mechanism.species[1].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.species[1].unknown_properties["__absolute tolerance"], "1e-10"); + + EXPECT_EQ(mechanism.species[2].name, "aerosol stuff"); + EXPECT_EQ(mechanism.species[2].phase, "AEROSOL"); + EXPECT_EQ(mechanism.species[2].optional_numerical_properties.size(), 2); + EXPECT_EQ(mechanism.species[2].optional_numerical_properties["molecular weight [kg mol-1]"], 0.5); + EXPECT_EQ(mechanism.species[2].optional_numerical_properties["density [kg m-3]"], 1000.0); + EXPECT_EQ(mechanism.species[2].optional_string_properties.size(), 1); + EXPECT_EQ(mechanism.species[2].optional_string_properties["tracer type"], "CONSTANT"); + EXPECT_EQ(mechanism.species[2].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.species[2].unknown_properties["__absolute tolerance"], "1e-20"); +} + +TEST(JsonParser, DetectsDuplicateSpecies) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/duplicate_species.json")); + + EXPECT_EQ(status, ConfigParseStatus::DuplicateSpeciesDetected); } \ No newline at end of file diff --git a/test/unit/unit_configs/duplicate_species.json b/test/unit/unit_configs/duplicate_species.json new file mode 100644 index 0000000..f65630e --- /dev/null +++ b/test/unit/unit_configs/duplicate_species.json @@ -0,0 +1,16 @@ +{ + "version": "1.0.0", + "name": "Full Configuration", + "species": [ + { + "name": "A", + "phase": "gas" + }, + { + "name": "A", + "phase": "gas" + } + ], + "phases": [ ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/species.json b/test/unit/unit_configs/valid_species.json similarity index 94% rename from test/unit/unit_configs/species.json rename to test/unit/unit_configs/valid_species.json index 4acdbc2..a1bd5fe 100644 --- a/test/unit/unit_configs/species.json +++ b/test/unit/unit_configs/valid_species.json @@ -4,10 +4,12 @@ "species": [ { "name": "A", + "phase": "gas", "__absolute tolerance": 1.0e-30 }, { "name": "H2O2", + "phase": "gas", "HLC(298K) [mol m-3 Pa-1]": 1.011596348, "HLC exponential factor [K]": 6340, "diffusion coefficient [m2 s-1]": 1.46E-05, From a9521a05372779da724ad0b6d10df18fa7354253 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 09:31:22 -0600 Subject: [PATCH 13/26] comments --- src/parser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/parser.cpp b/src/parser.cpp index 67afb72..fe390cd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -196,6 +196,7 @@ namespace open_atmos all_species.push_back(species); } + // check for duplicate species for(size_t i = 0; i < all_species.size(); ++i) { for(size_t j = i+1; j < all_species.size(); ++j) { if (all_species[i].name == all_species[j].name) { @@ -258,6 +259,7 @@ namespace open_atmos std::string name = object[validation::keys.name].get(); mechanism.name = name; + // parse all of the species at once auto species_parsing = ParseSpecies(object["species"]); if (species_parsing.first != ConfigParseStatus::Success) From c7ad0610a5f541beca1fbdd640f209c6fe8c55da Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 09:52:46 -0600 Subject: [PATCH 14/26] parsing species --- examples/full_configuration.json | 14 +++++++-- .../mechanism_configuration/parser.hpp | 4 --- src/parser.cpp | 29 ++++++++++--------- test/unit/test_parse_species.cpp | 16 ++++++++++ test/unit/unit_configs/duplicate_species.json | 2 +- test/unit/unit_configs/invalid_key.json | 13 +++++++++ .../unit_configs/missing_required_key.json | 12 ++++++++ test/unit/unit_configs/valid_species.json | 2 +- 8 files changed, 69 insertions(+), 23 deletions(-) create mode 100644 test/unit/unit_configs/invalid_key.json create mode 100644 test/unit/unit_configs/missing_required_key.json diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 6635e4c..0beb0ec 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -4,16 +4,20 @@ "species": [ { "name": "A", - "__absolute tolerance": 1.0e-30 + "__absolute tolerance": 1.0e-30, + "phase": "gas" }, { - "name": "B" + "name": "B", + "phase": "gas" }, { - "name": "C" + "name": "C", + "phase": "gas" }, { "name": "H2O2", + "phase": "gas", "HLC(298K) [mol m-3 Pa-1]": 1.011596348, "HLC exponential factor [K]": 6340, "diffusion coefficient [m2 s-1]": 1.46E-05, @@ -24,6 +28,7 @@ }, { "name": "ethanol", + "phase": "gas", "diffusion coefficient [m2 s-1]": 0.95E-05, "N star": 2.55, "molecular weight [kg mol-1]": 0.04607, @@ -31,18 +36,21 @@ }, { "name": "ethanol_aq", + "phase": "gas", "molecular weight [kg mol-1]": 0.04607, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-20 }, { "name": "H2O2_aq", + "phase": "gas", "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-10 }, { "name": "H2O_aq", + "phase": "gas", "density [kg m-3]": 1000.0, "molecular weight [kg mol-1]": 0.01801 }, diff --git a/include/open_atmos/mechanism_configuration/parser.hpp b/include/open_atmos/mechanism_configuration/parser.hpp index 8f6ab7b..3cd6aa1 100644 --- a/include/open_atmos/mechanism_configuration/parser.hpp +++ b/include/open_atmos/mechanism_configuration/parser.hpp @@ -25,12 +25,8 @@ namespace open_atmos InvalidKey, UnknownKey, InvalidFilePath, - NoConfigFilesFound, - DataSectionNotFound, - InvalidSpecies, ObjectTypeNotFound, RequiredKeyNotFound, - ContainsNonStandardKey, MutuallyExclusiveOption, InvalidVersion, DuplicateSpeciesDetected diff --git a/src/parser.cpp b/src/parser.cpp index fe390cd..a6a524f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -21,11 +21,8 @@ namespace open_atmos case ConfigParseStatus::InvalidKey: return "InvalidKey"; case ConfigParseStatus::UnknownKey: return "UnknownKey"; case ConfigParseStatus::InvalidFilePath: return "InvalidFilePath"; - case ConfigParseStatus::NoConfigFilesFound: return "NoConfigFilesFound"; - case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound"; - case ConfigParseStatus::ContainsNonStandardKey: return "ContainsNonStandardKey"; case ConfigParseStatus::MutuallyExclusiveOption: return "MutuallyExclusiveOption"; case ConfigParseStatus::DuplicateSpeciesDetected: return "DuplicateSpeciesDetected"; default: return "Unknown"; @@ -136,7 +133,7 @@ namespace open_atmos { std::cerr << "Non-standard key '" << key << "' found in object" << object << std::endl; - return ConfigParseStatus::ContainsNonStandardKey; + return ConfigParseStatus::InvalidKey; } } return ConfigParseStatus::Success; @@ -150,14 +147,12 @@ namespace open_atmos for (const auto& object : objects) { types::Species species; - auto status = ValidateSchema(object, validation::species.required_keys, validation::species.optional_keys); + status = ValidateSchema(object, validation::species.required_keys, validation::species.optional_keys); if (status != ConfigParseStatus::Success) { break; } - // std::cout << "object:\n" << object.dump(4) << std::endl; - std::string name = object[validation::keys.name].get(); std::string phase = object[validation::keys.phase].get(); @@ -167,11 +162,13 @@ namespace open_atmos { if (object.contains(key)) { - if (key == validation::keys.tracer_type) { + if (key == validation::keys.tracer_type) + { std::string val = object[key].get(); string_properties[key] = val; } - else { + else + { double val = object[key].get(); numerical_properties[key] = val; } @@ -197,14 +194,18 @@ namespace open_atmos } // check for duplicate species - for(size_t i = 0; i < all_species.size(); ++i) { - for(size_t j = i+1; j < all_species.size(); ++j) { - if (all_species[i].name == all_species[j].name) { + for (size_t i = 0; i < all_species.size(); ++i) + { + for (size_t j = i + 1; j < all_species.size(); ++j) + { + if (all_species[i].name == all_species[j].name) + { status = ConfigParseStatus::DuplicateSpeciesDetected; } break; } - if (status != ConfigParseStatus::Success) break; + if (status != ConfigParseStatus::Success) + break; } return { status, all_species }; @@ -274,4 +275,4 @@ namespace open_atmos return { status, mechanism }; } } // namespace mechanism_configuration -} // namespace open_atmos \ No newline at end of file +} // namespace open_atmos diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp index c814b68..4ad0d30 100644 --- a/test/unit/test_parse_species.cpp +++ b/test/unit/test_parse_species.cpp @@ -46,4 +46,20 @@ TEST(JsonParser, DetectsDuplicateSpecies) auto [status, mechanism] = parser.Parse(std::string("unit_configs/duplicate_species.json")); EXPECT_EQ(status, ConfigParseStatus::DuplicateSpeciesDetected); +} + +TEST(JsonParser, DetectsMissingRequiredKeys) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/missing_required_key.json")); + + EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound); +} + +TEST(JsonParser, DetectsInvalidKeys) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/invalid_key.json")); + + EXPECT_EQ(status, ConfigParseStatus::InvalidKey); } \ No newline at end of file diff --git a/test/unit/unit_configs/duplicate_species.json b/test/unit/unit_configs/duplicate_species.json index f65630e..2a254d4 100644 --- a/test/unit/unit_configs/duplicate_species.json +++ b/test/unit/unit_configs/duplicate_species.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "name": "Full Configuration", + "name": "Duplicate Species", "species": [ { "name": "A", diff --git a/test/unit/unit_configs/invalid_key.json b/test/unit/unit_configs/invalid_key.json new file mode 100644 index 0000000..8ef7994 --- /dev/null +++ b/test/unit/unit_configs/invalid_key.json @@ -0,0 +1,13 @@ +{ + "version": "1.0.0", + "name": "Invalid key", + "species": [ + { + "name": "A", + "phase": "gas", + "_absolute tolerance": 1.0e-30 + } + ], + "phases": [ ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/missing_required_key.json b/test/unit/unit_configs/missing_required_key.json new file mode 100644 index 0000000..477df85 --- /dev/null +++ b/test/unit/unit_configs/missing_required_key.json @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "name": "Invalid key", + "species": [ + { + "Name": "A", + "Phase": "gas" + } + ], + "phases": [ ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/valid_species.json b/test/unit/unit_configs/valid_species.json index a1bd5fe..74fe68c 100644 --- a/test/unit/unit_configs/valid_species.json +++ b/test/unit/unit_configs/valid_species.json @@ -1,6 +1,6 @@ { "version": "1.0.0", - "name": "Full Configuration", + "name": "Valid species configuration", "species": [ { "name": "A", From 4b230738b3c31041738eaae3d4db39a5e3e85e47 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 09:54:52 -0600 Subject: [PATCH 15/26] removing thing --- Testing/Temporary/CTestCostData.txt | 1 - Testing/Temporary/LastTest.log | 3 --- 2 files changed, 4 deletions(-) delete mode 100644 Testing/Temporary/CTestCostData.txt delete mode 100644 Testing/Temporary/LastTest.log diff --git a/Testing/Temporary/CTestCostData.txt b/Testing/Temporary/CTestCostData.txt deleted file mode 100644 index ed97d53..0000000 --- a/Testing/Temporary/CTestCostData.txt +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/Testing/Temporary/LastTest.log b/Testing/Temporary/LastTest.log deleted file mode 100644 index 7c461c9..0000000 --- a/Testing/Temporary/LastTest.log +++ /dev/null @@ -1,3 +0,0 @@ -Start testing: Jan 11 11:49 CST ----------------------------------------------------------- -End testing: Jan 11 11:49 CST From eb8a83096469ed2a0e55144a03b0d4571a6bbaa4 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 09:56:26 -0600 Subject: [PATCH 16/26] removed tracer type --- include/open_atmos/types.hpp | 1 - src/parser.cpp | 14 ++------------ test/unit/test_parse_species.cpp | 2 -- test/unit/unit_configs/valid_species.json | 1 - 4 files changed, 2 insertions(+), 16 deletions(-) diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index 59c0436..0f8341e 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -17,7 +17,6 @@ namespace open_atmos std::string phase; std::map optional_numerical_properties; - std::map optional_string_properties; std::unordered_map unknown_properties; }; diff --git a/src/parser.cpp b/src/parser.cpp index a6a524f..dc4099d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -157,21 +157,12 @@ namespace open_atmos std::string phase = object[validation::keys.phase].get(); std::map numerical_properties{}; - std::map string_properties{}; for (const auto& key : validation::species.optional_keys) { if (object.contains(key)) { - if (key == validation::keys.tracer_type) - { - std::string val = object[key].get(); - string_properties[key] = val; - } - else - { - double val = object[key].get(); - numerical_properties[key] = val; - } + double val = object[key].get(); + numerical_properties[key] = val; } } @@ -187,7 +178,6 @@ namespace open_atmos species.name = name; species.phase = phase; species.optional_numerical_properties = numerical_properties; - species.optional_string_properties = string_properties; species.unknown_properties = unknown_properties; all_species.push_back(species); diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp index 4ad0d30..0062eae 100644 --- a/test/unit/test_parse_species.cpp +++ b/test/unit/test_parse_species.cpp @@ -34,8 +34,6 @@ TEST(JsonParser, CanParseValidSpecies) EXPECT_EQ(mechanism.species[2].optional_numerical_properties.size(), 2); EXPECT_EQ(mechanism.species[2].optional_numerical_properties["molecular weight [kg mol-1]"], 0.5); EXPECT_EQ(mechanism.species[2].optional_numerical_properties["density [kg m-3]"], 1000.0); - EXPECT_EQ(mechanism.species[2].optional_string_properties.size(), 1); - EXPECT_EQ(mechanism.species[2].optional_string_properties["tracer type"], "CONSTANT"); EXPECT_EQ(mechanism.species[2].unknown_properties.size(), 1); EXPECT_EQ(mechanism.species[2].unknown_properties["__absolute tolerance"], "1e-20"); } diff --git a/test/unit/unit_configs/valid_species.json b/test/unit/unit_configs/valid_species.json index 74fe68c..cac6ee3 100644 --- a/test/unit/unit_configs/valid_species.json +++ b/test/unit/unit_configs/valid_species.json @@ -23,7 +23,6 @@ "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.5, "density [kg m-3]": 1000.0, - "tracer type" : "CONSTANT", "__absolute tolerance": 1.0e-20 } ], From 19c4492b328711179a3c866d5928885e9fe30920 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 10:10:01 -0600 Subject: [PATCH 17/26] trying to make clang happy --- .../open_atmos/mechanism_configuration/validation.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index 8913281..5172302 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -13,6 +13,7 @@ namespace open_atmos { struct Keys { + // Shared, but also Mechanism const std::string version = "version"; const std::string name = "name"; @@ -56,14 +57,14 @@ namespace open_atmos struct Phase { - const std::vector required_keys; - const std::vector optional_keys; + const std::vector required_keys {}; + const std::vector optional_keys {}; } phase; struct Mechanism { - const std::vector required_keys; - const std::vector optional_keys; + const std::vector required_keys {}; + const std::vector optional_keys {}; } mechanism; } // namespace validation From 6626f290a905e404e07b76c3fa0d70d33c062361 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 10:16:32 -0600 Subject: [PATCH 18/26] put check in function --- src/parser.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index dc4099d..ccbe84c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -139,6 +139,21 @@ namespace open_atmos return ConfigParseStatus::Success; } + bool ContainsOnlyUniqueSpecies(const std::vector& all_species) + { + for (size_t i = 0; i < all_species.size(); ++i) + { + for (size_t j = i + 1; j < all_species.size(); ++j) + { + if (all_species[i].name == all_species[j].name) + { + return false; + } + } + } + return true; + } + std::pair> ParseSpecies(const json& objects) { ConfigParseStatus status = ConfigParseStatus::Success; @@ -183,20 +198,8 @@ namespace open_atmos all_species.push_back(species); } - // check for duplicate species - for (size_t i = 0; i < all_species.size(); ++i) - { - for (size_t j = i + 1; j < all_species.size(); ++j) - { - if (all_species[i].name == all_species[j].name) - { - status = ConfigParseStatus::DuplicateSpeciesDetected; - } - break; - } - if (status != ConfigParseStatus::Success) - break; - } + if (!ContainsOnlyUniqueSpecies(all_species)) + status = ConfigParseStatus::DuplicateSpeciesDetected; return { status, all_species }; } From b537a45f5b8862740e1872497b54c1c20174ea49 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 10:19:45 -0600 Subject: [PATCH 19/26] removing comments --- include/open_atmos/mechanism_configuration/validation.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index 5172302..1338489 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -34,8 +34,6 @@ namespace open_atmos const std::string density = "density [kg m-3]"; } keys; - // Initialize static const members of Keys - struct Configuration { const std::vector required_keys{ keys.version, keys.species, keys.phases, keys.reactions }; From f44b7a5d66ec4dedd0d8b9a4d2823059d6b1c4ea Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 10:20:12 -0600 Subject: [PATCH 20/26] reomving tracer type --- include/open_atmos/mechanism_configuration/validation.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index 1338489..a301535 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -23,7 +23,6 @@ namespace open_atmos const std::string reactions = "reactions"; // Species - const std::string tracer_type = "tracer type"; const std::string absolute_tolerance = "absolute tolerance"; const std::string diffusion_coefficient = "diffusion coefficient [m2 s-1]"; const std::string molecular_weight = "molecular weight [kg mol-1]"; @@ -43,8 +42,7 @@ namespace open_atmos struct Species { const std::vector required_keys{ keys.name, keys.phase }; - const std::vector optional_keys{ keys.tracer_type, - keys.absolute_tolerance, + const std::vector optional_keys{ keys.absolute_tolerance, keys.diffusion_coefficient, keys.molecular_weight, keys.henrys_law_constant_298, From 52324f61a348f1f84e84a5ad877c660146831bd2 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 10:21:32 -0600 Subject: [PATCH 21/26] adding comment --- src/parser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/parser.cpp b/src/parser.cpp index ccbe84c..a3e04b1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -29,6 +29,7 @@ namespace open_atmos } } + // 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) { From c85a8fb949982d53221c46473791cd328d801ca4 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 12 Jan 2024 12:21:47 -0600 Subject: [PATCH 22/26] removing phase from species --- examples/full_configuration.json | 16 +++------------- .../mechanism_configuration/validation.hpp | 2 +- include/open_atmos/types.hpp | 1 - src/parser.cpp | 2 -- test/unit/test_parse_species.cpp | 3 --- test/unit/unit_configs/duplicate_species.json | 6 ++---- test/unit/unit_configs/invalid_key.json | 1 - test/unit/unit_configs/missing_required_key.json | 3 +-- test/unit/unit_configs/valid_species.json | 3 --- 9 files changed, 7 insertions(+), 30 deletions(-) diff --git a/examples/full_configuration.json b/examples/full_configuration.json index 0beb0ec..492e8b4 100644 --- a/examples/full_configuration.json +++ b/examples/full_configuration.json @@ -4,20 +4,16 @@ "species": [ { "name": "A", - "__absolute tolerance": 1.0e-30, - "phase": "gas" + "__absolute tolerance": 1.0e-30 }, { - "name": "B", - "phase": "gas" + "name": "B" }, { - "name": "C", - "phase": "gas" + "name": "C" }, { "name": "H2O2", - "phase": "gas", "HLC(298K) [mol m-3 Pa-1]": 1.011596348, "HLC exponential factor [K]": 6340, "diffusion coefficient [m2 s-1]": 1.46E-05, @@ -28,7 +24,6 @@ }, { "name": "ethanol", - "phase": "gas", "diffusion coefficient [m2 s-1]": 0.95E-05, "N star": 2.55, "molecular weight [kg mol-1]": 0.04607, @@ -36,34 +31,29 @@ }, { "name": "ethanol_aq", - "phase": "gas", "molecular weight [kg mol-1]": 0.04607, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-20 }, { "name": "H2O2_aq", - "phase": "gas", "molecular weight [kg mol-1]": 0.0340147, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-10 }, { "name": "H2O_aq", - "phase": "gas", "density [kg m-3]": 1000.0, "molecular weight [kg mol-1]": 0.01801 }, { "name": "aerosol stuff", - "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.5, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-20 }, { "name": "more aerosol stuff", - "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.2, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-20 diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index a301535..3eb7289 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -41,7 +41,7 @@ namespace open_atmos struct Species { - const std::vector required_keys{ keys.name, keys.phase }; + const std::vector required_keys{ keys.name }; const std::vector optional_keys{ keys.absolute_tolerance, keys.diffusion_coefficient, keys.molecular_weight, diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index 0f8341e..455b2c9 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -14,7 +14,6 @@ namespace open_atmos struct Species { std::string name; - std::string phase; std::map optional_numerical_properties; diff --git a/src/parser.cpp b/src/parser.cpp index a3e04b1..9178683 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -170,7 +170,6 @@ namespace open_atmos } std::string name = object[validation::keys.name].get(); - std::string phase = object[validation::keys.phase].get(); std::map numerical_properties{}; for (const auto& key : validation::species.optional_keys) @@ -192,7 +191,6 @@ namespace open_atmos } species.name = name; - species.phase = phase; species.optional_numerical_properties = numerical_properties; species.unknown_properties = unknown_properties; diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp index 0062eae..f9bb628 100644 --- a/test/unit/test_parse_species.cpp +++ b/test/unit/test_parse_species.cpp @@ -13,12 +13,10 @@ TEST(JsonParser, CanParseValidSpecies) EXPECT_EQ(mechanism.species.size(), 3); EXPECT_EQ(mechanism.species[0].name, "A"); - EXPECT_EQ(mechanism.species[0].phase, "gas"); EXPECT_EQ(mechanism.species[0].unknown_properties.size(), 1); EXPECT_EQ(mechanism.species[0].unknown_properties["__absolute tolerance"], "1e-30"); EXPECT_EQ(mechanism.species[1].name, "H2O2"); - EXPECT_EQ(mechanism.species[1].phase, "gas"); EXPECT_EQ(mechanism.species[1].optional_numerical_properties.size(), 6); EXPECT_EQ(mechanism.species[1].optional_numerical_properties["HLC(298K) [mol m-3 Pa-1]"], 1.011596348); EXPECT_EQ(mechanism.species[1].optional_numerical_properties["HLC exponential factor [K]"], 6340); @@ -30,7 +28,6 @@ TEST(JsonParser, CanParseValidSpecies) EXPECT_EQ(mechanism.species[1].unknown_properties["__absolute tolerance"], "1e-10"); EXPECT_EQ(mechanism.species[2].name, "aerosol stuff"); - EXPECT_EQ(mechanism.species[2].phase, "AEROSOL"); EXPECT_EQ(mechanism.species[2].optional_numerical_properties.size(), 2); EXPECT_EQ(mechanism.species[2].optional_numerical_properties["molecular weight [kg mol-1]"], 0.5); EXPECT_EQ(mechanism.species[2].optional_numerical_properties["density [kg m-3]"], 1000.0); diff --git a/test/unit/unit_configs/duplicate_species.json b/test/unit/unit_configs/duplicate_species.json index 2a254d4..ad41dbc 100644 --- a/test/unit/unit_configs/duplicate_species.json +++ b/test/unit/unit_configs/duplicate_species.json @@ -3,12 +3,10 @@ "name": "Duplicate Species", "species": [ { - "name": "A", - "phase": "gas" + "name": "A" }, { - "name": "A", - "phase": "gas" + "name": "A" } ], "phases": [ ], diff --git a/test/unit/unit_configs/invalid_key.json b/test/unit/unit_configs/invalid_key.json index 8ef7994..5ae7360 100644 --- a/test/unit/unit_configs/invalid_key.json +++ b/test/unit/unit_configs/invalid_key.json @@ -4,7 +4,6 @@ "species": [ { "name": "A", - "phase": "gas", "_absolute tolerance": 1.0e-30 } ], diff --git a/test/unit/unit_configs/missing_required_key.json b/test/unit/unit_configs/missing_required_key.json index 477df85..0ac2635 100644 --- a/test/unit/unit_configs/missing_required_key.json +++ b/test/unit/unit_configs/missing_required_key.json @@ -3,8 +3,7 @@ "name": "Invalid key", "species": [ { - "Name": "A", - "Phase": "gas" + "Name": "A" } ], "phases": [ ], diff --git a/test/unit/unit_configs/valid_species.json b/test/unit/unit_configs/valid_species.json index cac6ee3..d45ac2d 100644 --- a/test/unit/unit_configs/valid_species.json +++ b/test/unit/unit_configs/valid_species.json @@ -4,12 +4,10 @@ "species": [ { "name": "A", - "phase": "gas", "__absolute tolerance": 1.0e-30 }, { "name": "H2O2", - "phase": "gas", "HLC(298K) [mol m-3 Pa-1]": 1.011596348, "HLC exponential factor [K]": 6340, "diffusion coefficient [m2 s-1]": 1.46E-05, @@ -20,7 +18,6 @@ }, { "name": "aerosol stuff", - "phase": "AEROSOL", "molecular weight [kg mol-1]": 0.5, "density [kg m-3]": 1000.0, "__absolute tolerance": 1.0e-20 From fb6d30d9e1d0a5370cca9a706341b7fe8523fa1a Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 16 Jan 2024 14:53:25 -0600 Subject: [PATCH 23/26] parsing phases --- .../mechanism_configuration/parser.hpp | 4 +- .../mechanism_configuration/validation.hpp | 8 +- include/open_atmos/types.hpp | 5 +- src/parser.cpp | 89 +++++++++++++++++-- test/unit/CMakeLists.txt | 1 + test/unit/test_parse_phases.cpp | 61 +++++++++++++ test/unit/test_parse_species.cpp | 8 +- .../unit_configs/phases/duplicate_phases.json | 32 +++++++ .../unit/unit_configs/phases/invalid_key.json | 26 ++++++ .../phases/missing_required_key.json | 12 +++ .../unit_configs/phases/unknown_species.json | 26 ++++++ .../unit_configs/phases/valid_phases.json | 34 +++++++ .../{ => species}/duplicate_species.json | 0 .../{ => species}/invalid_key.json | 0 .../{ => species}/missing_required_key.json | 0 .../{ => species}/valid_species.json | 0 16 files changed, 291 insertions(+), 15 deletions(-) create mode 100644 test/unit/test_parse_phases.cpp create mode 100644 test/unit/unit_configs/phases/duplicate_phases.json create mode 100644 test/unit/unit_configs/phases/invalid_key.json create mode 100644 test/unit/unit_configs/phases/missing_required_key.json create mode 100644 test/unit/unit_configs/phases/unknown_species.json create mode 100644 test/unit/unit_configs/phases/valid_phases.json rename test/unit/unit_configs/{ => species}/duplicate_species.json (100%) rename test/unit/unit_configs/{ => species}/invalid_key.json (100%) rename test/unit/unit_configs/{ => species}/missing_required_key.json (100%) rename test/unit/unit_configs/{ => species}/valid_species.json (100%) diff --git a/include/open_atmos/mechanism_configuration/parser.hpp b/include/open_atmos/mechanism_configuration/parser.hpp index 3cd6aa1..3a542f9 100644 --- a/include/open_atmos/mechanism_configuration/parser.hpp +++ b/include/open_atmos/mechanism_configuration/parser.hpp @@ -29,7 +29,9 @@ namespace open_atmos RequiredKeyNotFound, MutuallyExclusiveOption, InvalidVersion, - DuplicateSpeciesDetected + DuplicateSpeciesDetected, + DuplicatePhasesDetected, + PhaseRequiresUnknownSpecies, }; std::string configParseStatusToString(const ConfigParseStatus &status); diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index 3eb7289..b27eea8 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -53,14 +53,14 @@ namespace open_atmos struct Phase { - const std::vector required_keys {}; - const std::vector optional_keys {}; + const std::vector required_keys{ keys.name, keys.species }; + const std::vector optional_keys{}; } phase; struct Mechanism { - const std::vector required_keys {}; - const std::vector optional_keys {}; + const std::vector required_keys{}; + const std::vector optional_keys{}; } mechanism; } // namespace validation diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index 455b2c9..a95b9c7 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -22,13 +22,16 @@ namespace open_atmos struct Phase { + std::string name; + std::vector species; + std::unordered_map unknown_properties; }; struct Mechanism { std::string name; // optional std::vector species; - std::unordered_map phases; + std::vector phases; }; } // namespace types diff --git a/src/parser.cpp b/src/parser.cpp index 9178683..bb84a92 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -25,6 +25,8 @@ namespace open_atmos 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"; default: return "Unknown"; } } @@ -140,13 +142,14 @@ namespace open_atmos return ConfigParseStatus::Success; } - bool ContainsOnlyUniqueSpecies(const std::vector& all_species) + template + bool ContainsUniqueObjectsByName(const std::vector& collection) { - for (size_t i = 0; i < all_species.size(); ++i) + for (size_t i = 0; i < collection.size(); ++i) { - for (size_t j = i + 1; j < all_species.size(); ++j) + for (size_t j = i + 1; j < collection.size(); ++j) { - if (all_species[i].name == all_species[j].name) + if (collection[i].name == collection[j].name) { return false; } @@ -155,6 +158,21 @@ namespace open_atmos return true; } + bool PhaseRequiresUnknownSpecies(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 types::Species& existing) { return existing.name == spec; }); + + if (it == existing_species.end()) + { + return true; + } + } + return false; + } + std::pair> ParseSpecies(const json& objects) { ConfigParseStatus status = ConfigParseStatus::Success; @@ -197,12 +215,62 @@ namespace open_atmos all_species.push_back(species); } - if (!ContainsOnlyUniqueSpecies(all_species)) + if (!ContainsUniqueObjectsByName(all_species)) status = ConfigParseStatus::DuplicateSpeciesDetected; return { status, all_species }; } + std::pair> ParsePhases(const json& objects, const std::vector existing_species) + { + 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; + } + + std::string name = object[validation::keys.name].get(); + + std::vector species{}; + for (const auto& spec : object[validation::keys.species]) + { + species.push_back(spec); + } + + auto comments = GetComments(object, validation::phase.required_keys, validation::phase.optional_keys); + + std::unordered_map unknown_properties; + for (const auto& key : comments) + { + std::string val = object[key].dump(); + unknown_properties[key] = val; + } + + phase.name = name; + phase.species = species; + phase.unknown_properties = unknown_properties; + + if (PhaseRequiresUnknownSpecies(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 }; + } + std::pair JsonParser::Parse(const std::string& file_path) { return JsonParser::Parse(std::filesystem::path(file_path)); @@ -262,7 +330,18 @@ namespace open_atmos std::cerr << "[" << msg << "] Failed to parse the species." << std::endl; } + // parse all of the phases at once + auto phases_parsing = ParsePhases(object["phases"], species_parsing.second); + + if (phases_parsing.first != ConfigParseStatus::Success) + { + status = phases_parsing.first; + std::string msg = configParseStatusToString(status); + std::cerr << "[" << msg << "] Failed to parse the phases." << std::endl; + } + mechanism.species = species_parsing.second; + mechanism.phases = phases_parsing.second; return { status, mechanism }; } diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 113c111..03caba6 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -7,6 +7,7 @@ include(test_util) # Tests create_standard_test(NAME parse_species SOURCES test_parse_species.cpp) +create_standard_test(NAME parse_phases SOURCES test_parse_phases.cpp) ################################################################################ # Copy test data diff --git a/test/unit/test_parse_phases.cpp b/test/unit/test_parse_phases.cpp new file mode 100644 index 0000000..2065294 --- /dev/null +++ b/test/unit/test_parse_phases.cpp @@ -0,0 +1,61 @@ +#include + +#include + +using namespace open_atmos::mechanism_configuration; + +TEST(JsonParser, CanParseValidPhases) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/phases/valid_phases.json")); + + EXPECT_EQ(status, ConfigParseStatus::Success); + EXPECT_EQ(mechanism.species.size(), 3); + EXPECT_EQ(mechanism.phases.size(), 2); + + EXPECT_EQ(mechanism.phases[0].name, "gas"); + EXPECT_EQ(mechanism.phases[0].species.size(), 2); + EXPECT_EQ(mechanism.phases[0].species[0], "A"); + EXPECT_EQ(mechanism.phases[0].species[1], "B"); + EXPECT_EQ(mechanism.phases[0].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.phases[0].unknown_properties["__other"], "\"key\""); + + EXPECT_EQ(mechanism.phases[1].name, "aerosols"); + EXPECT_EQ(mechanism.phases[1].species.size(), 1); + EXPECT_EQ(mechanism.phases[1].species[0], "C"); + EXPECT_EQ(mechanism.phases[1].unknown_properties.size(), 2); + EXPECT_EQ(mechanism.phases[1].unknown_properties["__other1"], "\"key1\""); + EXPECT_EQ(mechanism.phases[1].unknown_properties["__other2"], "\"key2\""); +} + +TEST(JsonParser, DetectsDuplicatePhases) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/phases/duplicate_phases.json")); + + EXPECT_EQ(status, ConfigParseStatus::DuplicatePhasesDetected); +} + +TEST(JsonParser, DetectsMissingRequiredKeys) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/phases/missing_required_key.json")); + + EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound); +} + +TEST(JsonParser, DetectsInvalidKeys) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/phases/invalid_key.json")); + + EXPECT_EQ(status, ConfigParseStatus::InvalidKey); +} + +TEST(JsonParser, DetectsPhaseRequestingUnknownSpecies) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/phases/unknown_species.json")); + + EXPECT_EQ(status, ConfigParseStatus::PhaseRequiresUnknownSpecies); +} \ No newline at end of file diff --git a/test/unit/test_parse_species.cpp b/test/unit/test_parse_species.cpp index f9bb628..1fd250c 100644 --- a/test/unit/test_parse_species.cpp +++ b/test/unit/test_parse_species.cpp @@ -7,7 +7,7 @@ using namespace open_atmos::mechanism_configuration; TEST(JsonParser, CanParseValidSpecies) { JsonParser parser; - auto [status, mechanism] = parser.Parse(std::string("unit_configs/valid_species.json")); + auto [status, mechanism] = parser.Parse(std::string("unit_configs/species/valid_species.json")); EXPECT_EQ(status, ConfigParseStatus::Success); EXPECT_EQ(mechanism.species.size(), 3); @@ -38,7 +38,7 @@ TEST(JsonParser, CanParseValidSpecies) TEST(JsonParser, DetectsDuplicateSpecies) { JsonParser parser; - auto [status, mechanism] = parser.Parse(std::string("unit_configs/duplicate_species.json")); + auto [status, mechanism] = parser.Parse(std::string("unit_configs/species/duplicate_species.json")); EXPECT_EQ(status, ConfigParseStatus::DuplicateSpeciesDetected); } @@ -46,7 +46,7 @@ TEST(JsonParser, DetectsDuplicateSpecies) TEST(JsonParser, DetectsMissingRequiredKeys) { JsonParser parser; - auto [status, mechanism] = parser.Parse(std::string("unit_configs/missing_required_key.json")); + auto [status, mechanism] = parser.Parse(std::string("unit_configs/species/missing_required_key.json")); EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound); } @@ -54,7 +54,7 @@ TEST(JsonParser, DetectsMissingRequiredKeys) TEST(JsonParser, DetectsInvalidKeys) { JsonParser parser; - auto [status, mechanism] = parser.Parse(std::string("unit_configs/invalid_key.json")); + auto [status, mechanism] = parser.Parse(std::string("unit_configs/species/invalid_key.json")); EXPECT_EQ(status, ConfigParseStatus::InvalidKey); } \ No newline at end of file diff --git a/test/unit/unit_configs/phases/duplicate_phases.json b/test/unit/unit_configs/phases/duplicate_phases.json new file mode 100644 index 0000000..66da828 --- /dev/null +++ b/test/unit/unit_configs/phases/duplicate_phases.json @@ -0,0 +1,32 @@ +{ + "version": "1.0.0", + "name": "Duplicate phases configuration", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B" + ] + }, + { + "name": "gas", + "species": [ + "A", + "B" + ] + } + ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/phases/invalid_key.json b/test/unit/unit_configs/phases/invalid_key.json new file mode 100644 index 0000000..0b57b70 --- /dev/null +++ b/test/unit/unit_configs/phases/invalid_key.json @@ -0,0 +1,26 @@ +{ + "version": "1.0.0", + "name": "Invalid key configuration", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B" + ], + "other": "key" + } + ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/phases/missing_required_key.json b/test/unit/unit_configs/phases/missing_required_key.json new file mode 100644 index 0000000..5f2c793 --- /dev/null +++ b/test/unit/unit_configs/phases/missing_required_key.json @@ -0,0 +1,12 @@ +{ + "version": "1.0.0", + "name": "Missing required phases key configuration", + "species": [ + ], + "phases": [ + { + "name": "gas" + } + ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/phases/unknown_species.json b/test/unit/unit_configs/phases/unknown_species.json new file mode 100644 index 0000000..4a80e50 --- /dev/null +++ b/test/unit/unit_configs/phases/unknown_species.json @@ -0,0 +1,26 @@ +{ + "version": "1.0.0", + "name": "Unknown species configuration", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "D" + ], + "__other": "key" + } + ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/phases/valid_phases.json b/test/unit/unit_configs/phases/valid_phases.json new file mode 100644 index 0000000..d208cd5 --- /dev/null +++ b/test/unit/unit_configs/phases/valid_phases.json @@ -0,0 +1,34 @@ +{ + "version": "1.0.0", + "name": "Valid phases configuration", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B" + ], + "__other": "key" + }, + { + "name": "aerosols", + "species": [ + "C" + ], + "__other1": "key1", + "__other2": "key2" + } + ], + "reactions": [ ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/duplicate_species.json b/test/unit/unit_configs/species/duplicate_species.json similarity index 100% rename from test/unit/unit_configs/duplicate_species.json rename to test/unit/unit_configs/species/duplicate_species.json diff --git a/test/unit/unit_configs/invalid_key.json b/test/unit/unit_configs/species/invalid_key.json similarity index 100% rename from test/unit/unit_configs/invalid_key.json rename to test/unit/unit_configs/species/invalid_key.json diff --git a/test/unit/unit_configs/missing_required_key.json b/test/unit/unit_configs/species/missing_required_key.json similarity index 100% rename from test/unit/unit_configs/missing_required_key.json rename to test/unit/unit_configs/species/missing_required_key.json diff --git a/test/unit/unit_configs/valid_species.json b/test/unit/unit_configs/species/valid_species.json similarity index 100% rename from test/unit/unit_configs/valid_species.json rename to test/unit/unit_configs/species/valid_species.json From f8c2b54face010cad3a7646c07cf85e9d0c513d1 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 17 Jan 2024 14:02:23 -0600 Subject: [PATCH 24/26] parsing arrhenius --- include/open_atmos/constants.hpp | 14 ++ .../mechanism_configuration/validation.hpp | 32 ++++ include/open_atmos/types.hpp | 42 ++++- src/parser.cpp | 148 +++++++++++++++++- test/integration/test_json_parser.cpp | 5 +- test/unit/CMakeLists.txt | 1 + test/unit/test_parse_arrhenius.cpp | 50 ++++++ .../reactions/arrhenius/valid.json | 81 ++++++++++ 8 files changed, 369 insertions(+), 4 deletions(-) create mode 100644 include/open_atmos/constants.hpp create mode 100644 test/unit/test_parse_arrhenius.cpp create mode 100644 test/unit/unit_configs/reactions/arrhenius/valid.json diff --git a/include/open_atmos/constants.hpp b/include/open_atmos/constants.hpp new file mode 100644 index 0000000..234cd5c --- /dev/null +++ b/include/open_atmos/constants.hpp @@ -0,0 +1,14 @@ +// Copyright (C) 2023-2024 National Center for Atmospheric Research, University of Illinois at Urbana-Champaign +// +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +namespace open_atmos +{ + namespace constants + { + static constexpr double boltzmann = 1.380649e-23; // J K^{-1} + static constexpr double avogadro = 6.02214076e23; // # mol^{-1} + static constexpr double R = boltzmann * avogadro; // J K^{-1} mol^{-1} + } // namespace constants +} // namespace open_atmos \ No newline at end of file diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index b27eea8..ff4224f 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -31,6 +31,26 @@ namespace open_atmos const std::string phase = "phase"; const std::string n_star = "N star"; const std::string density = "density [kg m-3]"; + + // Reactions + const std::string reactants = "reactants"; + const std::string products = "products"; + const std::string type = "type"; + const std::string gas_phase = "gas phase"; + + // Reactant and product + const std::string species_name = "species name"; + const std::string coefficient = "coefficient"; + + // Arrhenius + const std::string Arrhenius_key = "ARRHENIUS"; + const std::string A = "A"; + const std::string B = "B"; + const std::string C = "C"; + const std::string D = "D"; + const std::string E = "E"; + const std::string Ea = "Ea"; + } keys; struct Configuration @@ -57,6 +77,18 @@ namespace open_atmos const std::vector optional_keys{}; } phase; + struct ReactionComponent + { + const std::vector required_keys{ keys.species_name }; + const std::vector optional_keys{ keys.coefficient }; + } reaction_component; + + struct Arrhenius + { + const std::vector required_keys{ keys.products, keys.reactants, keys.type, keys.gas_phase }; + const std::vector optional_keys{ keys.A, keys.B, keys.C, keys.D, keys.E, keys.name }; + } arrhenius; + struct Mechanism { const std::vector required_keys{}; diff --git a/include/open_atmos/types.hpp b/include/open_atmos/types.hpp index a95b9c7..2d3e422 100644 --- a/include/open_atmos/types.hpp +++ b/include/open_atmos/types.hpp @@ -27,11 +27,51 @@ namespace open_atmos std::unordered_map unknown_properties; }; + struct ReactionComponent + { + std::string species_name; + double coefficient; + std::unordered_map unknown_properties; + }; + + struct Arrhenius + { + /// @brief Pre-exponential factor [(mol m−3)^(−(𝑛−1)) s−1] + double A{ 1 }; + /// @brief Unitless exponential factor + double B{ 0 }; + /// @brief Activation threshold, expected to be the negative activation energy divided by the boltzman constant + /// [-E_a / k_b), K] + double C{ 0 }; + /// @brief A factor that determines temperature dependence [K] + double D{ 300 }; + /// @brief A factor that determines pressure dependence [Pa-1] + double E{ 0 }; + + /// @brief A list of reactants + std::vector reactants; + /// @brief A list of products + std::vector products; + /// @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; + + std::unordered_map unknown_properties; + }; + + struct Reactions + { + std::vector arrhenius; + }; + struct Mechanism { - std::string name; // optional + /// @brief An identifier, optional + std::string name; std::vector species; std::vector phases; + Reactions reactions; }; } // namespace types diff --git a/src/parser.cpp b/src/parser.cpp index bb84a92..31478f5 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 +#include #include #include #include @@ -271,6 +272,138 @@ namespace open_atmos return { status, all_phases }; } + 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) + { + double coefficient = object[validation::keys.coefficient].get(); + std::string species_name = object[validation::keys.species_name].get(); + + auto comments = GetComments(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys); + + std::unordered_map unknown_properties; + for (const auto& key : comments) + { + std::string val = object[key].dump(); + unknown_properties[key] = val; + } + + component.species_name = species_name; + component.coefficient = coefficient; + component.unknown_properties = unknown_properties; + } + + return { status, component }; + } + + std::pair ParseArrhenius(const json& object, const std::vector existing_species) + { + 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); + if (product_parse.first != 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); + if (reactant_parse.first != ConfigParseStatus::Success) { + break; + } + reactants.push_back(reactant_parse.second); + } + + 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; + } + + if (object.contains(validation::keys.name)) + { + arrhenius.name = object[validation::keys.name].get(); + } + + auto comments = GetComments(object, validation::arrhenius.required_keys, validation::arrhenius.optional_keys); + + std::unordered_map unknown_properties; + for (const auto& key : comments) + { + std::string val = object[key].dump(); + unknown_properties[key] = val; + } + + arrhenius.gas_phase = object[validation::keys.gas_phase].get(); + arrhenius.products = products; + arrhenius.reactants = reactants; + arrhenius.unknown_properties = unknown_properties; + } + + return { status, arrhenius }; + } + + std::pair ParseReactions(const json& objects, const std::vector existing_species) + { + ConfigParseStatus status = ConfigParseStatus::Success; + types::Reactions reactions; + + 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); + if (arrhenius_parse.first != ConfigParseStatus::Success) + { + break; + } + reactions.arrhenius.push_back(arrhenius_parse.second); + } + } + + return { status, reactions }; + } + std::pair JsonParser::Parse(const std::string& file_path) { return JsonParser::Parse(std::filesystem::path(file_path)); @@ -321,7 +454,7 @@ namespace open_atmos mechanism.name = name; // parse all of the species at once - auto species_parsing = ParseSpecies(object["species"]); + auto species_parsing = ParseSpecies(object[validation::keys.species]); if (species_parsing.first != ConfigParseStatus::Success) { @@ -331,7 +464,7 @@ namespace open_atmos } // parse all of the phases at once - auto phases_parsing = ParsePhases(object["phases"], species_parsing.second); + auto phases_parsing = ParsePhases(object[validation::keys.phases], species_parsing.second); if (phases_parsing.first != ConfigParseStatus::Success) { @@ -340,8 +473,19 @@ namespace open_atmos std::cerr << "[" << msg << "] Failed to parse the phases." << std::endl; } + // parse all of the reactions at once + auto reactions_parsing = ParseReactions(object[validation::keys.reactions], species_parsing.second); + + if (reactions_parsing.first != ConfigParseStatus::Success) + { + status = reactions_parsing.first; + std::string msg = configParseStatusToString(status); + std::cerr << "[" << msg << "] Failed to parse the reactions." << std::endl; + } + mechanism.species = species_parsing.second; mechanism.phases = phases_parsing.second; + mechanism.reactions = reactions_parsing.second; return { status, mechanism }; } diff --git a/test/integration/test_json_parser.cpp b/test/integration/test_json_parser.cpp index c29a40a..1a73d81 100644 --- a/test/integration/test_json_parser.cpp +++ b/test/integration/test_json_parser.cpp @@ -4,9 +4,12 @@ using namespace open_atmos::mechanism_configuration; -TEST(JsonParser, Returns) +TEST(JsonParser, ParsesFullConfiguration) { JsonParser parser; auto [status, mechanism] = parser.Parse(std::string("examples/full_configuration.json")); EXPECT_EQ(status, ConfigParseStatus::Success); + EXPECT_EQ(mechanism.name, "Full Configuration"); + EXPECT_EQ(mechanism.species.size(), 10); + EXPECT_EQ(mechanism.reactions.arrhenius.size(), 1); } \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index 03caba6..b0410c8 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -8,6 +8,7 @@ include(test_util) create_standard_test(NAME parse_species SOURCES test_parse_species.cpp) create_standard_test(NAME parse_phases SOURCES test_parse_phases.cpp) +create_standard_test(NAME parse_arrhenius SOURCES test_parse_arrhenius.cpp) ################################################################################ # Copy test data diff --git a/test/unit/test_parse_arrhenius.cpp b/test/unit/test_parse_arrhenius.cpp new file mode 100644 index 0000000..a219867 --- /dev/null +++ b/test/unit/test_parse_arrhenius.cpp @@ -0,0 +1,50 @@ +#include + +#include + +using namespace open_atmos::mechanism_configuration; + +TEST(JsonParser, CanParseValidArrheniusReaction) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/valid.json")); + + EXPECT_EQ(mechanism.reactions.arrhenius.size(), 2); + + EXPECT_EQ(mechanism.reactions.arrhenius[0].name, "my arrhenius"); + EXPECT_EQ(mechanism.reactions.arrhenius[0].gas_phase, "gas"); + EXPECT_EQ(mechanism.reactions.arrhenius[0].A, 32.1); + EXPECT_EQ(mechanism.reactions.arrhenius[0].B, -2.3); + EXPECT_EQ(mechanism.reactions.arrhenius[0].C, 102.3); + EXPECT_EQ(mechanism.reactions.arrhenius[0].D, 63.4); + EXPECT_EQ(mechanism.reactions.arrhenius[0].E, -1.3); + EXPECT_EQ(mechanism.reactions.arrhenius[0].reactants.size(), 1); + EXPECT_EQ(mechanism.reactions.arrhenius[0].reactants[0].species_name, "A"); + EXPECT_EQ(mechanism.reactions.arrhenius[0].reactants[0].coefficient, 1); + EXPECT_EQ(mechanism.reactions.arrhenius[0].products.size(), 2); + EXPECT_EQ(mechanism.reactions.arrhenius[0].products[0].species_name, "B"); + EXPECT_EQ(mechanism.reactions.arrhenius[0].products[0].coefficient, 1.2); + EXPECT_EQ(mechanism.reactions.arrhenius[0].products[1].species_name, "C"); + EXPECT_EQ(mechanism.reactions.arrhenius[0].products[1].coefficient, 0.3); + EXPECT_EQ(mechanism.reactions.arrhenius[0].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.reactions.arrhenius[0].unknown_properties["__solver_param"], "0.1"); + + EXPECT_EQ(mechanism.reactions.arrhenius[1].name, "my arrhenius2"); + EXPECT_EQ(mechanism.reactions.arrhenius[1].gas_phase, "gas"); + EXPECT_EQ(mechanism.reactions.arrhenius[1].A, 3.1); + EXPECT_EQ(mechanism.reactions.arrhenius[1].B, -0.3); + EXPECT_EQ(mechanism.reactions.arrhenius[1].C, 12.3); + EXPECT_EQ(mechanism.reactions.arrhenius[1].D, 6.4); + EXPECT_EQ(mechanism.reactions.arrhenius[1].E, -0.3); + EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants.size(), 2); + EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[0].species_name, "A"); + EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[0].coefficient, 2); + EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[1].species_name, "B"); + EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[1].coefficient, 0.1); + + EXPECT_EQ(mechanism.reactions.arrhenius[1].products.size(), 1); + EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].species_name, "C"); + EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].coefficient, 0.5); + EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].unknown_properties.size(), 1); + EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].unknown_properties["__optional thing"], "\"hello\""); +} \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/arrhenius/valid.json b/test/unit/unit_configs/reactions/arrhenius/valid.json new file mode 100644 index 0000000..7e50291 --- /dev/null +++ b/test/unit/unit_configs/reactions/arrhenius/valid.json @@ -0,0 +1,81 @@ +{ + "version": "1.0.0", + "name": "Valid arrhenius", + "species": [ + { + "name": "A" + }, + { + "name": "B" + }, + { + "name": "C" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B", + "C" + ] + } + ], + "reactions": [ + { + "type": "ARRHENIUS", + "gas phase": "gas", + "reactants": [ + { + "species name": "A", + "coefficient": 1 + } + ], + "products": [ + { + "species name": "B", + "coefficient": 1.2 + }, + { + "species name": "C", + "coefficient": 0.3 + } + ], + "A": 32.1, + "B": -2.3, + "C": 102.3, + "D": 63.4, + "E": -1.3, + "name": "my arrhenius", + "__solver_param": 0.1 + }, + { + "type": "ARRHENIUS", + "gas phase": "gas", + "reactants": [ + { + "species name": "A", + "coefficient": 2 + }, + { + "species name": "B", + "coefficient": 0.1 + } + ], + "products": [ + { + "species name": "C", + "coefficient": 0.5, + "__optional thing": "hello" + } + ], + "A": 3.1, + "B": -0.3, + "C": 12.3, + "D": 6.4, + "E": -0.3, + "name": "my arrhenius2" + } + ] +} \ No newline at end of file From f9e2eb98126651d484d5f093df42f475c7768d68 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 17 Jan 2024 14:26:31 -0600 Subject: [PATCH 25/26] unknown species detection --- .../mechanism_configuration/parser.hpp | 1 + src/parser.cpp | 26 +++++++++++-- test/unit/test_parse_arrhenius.cpp | 25 ++++++++++++- .../reactions/arrhenius/unknown_species.json | 37 +++++++++++++++++++ .../reactions/arrhenius/valid.json | 14 +++++++ 5 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 test/unit/unit_configs/reactions/arrhenius/unknown_species.json diff --git a/include/open_atmos/mechanism_configuration/parser.hpp b/include/open_atmos/mechanism_configuration/parser.hpp index 3a542f9..6d306ed 100644 --- a/include/open_atmos/mechanism_configuration/parser.hpp +++ b/include/open_atmos/mechanism_configuration/parser.hpp @@ -32,6 +32,7 @@ namespace open_atmos DuplicateSpeciesDetected, DuplicatePhasesDetected, PhaseRequiresUnknownSpecies, + ReactionRequiresUnknownSpecies, }; std::string configParseStatusToString(const ConfigParseStatus &status); diff --git a/src/parser.cpp b/src/parser.cpp index 31478f5..440830f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -28,6 +28,7 @@ namespace open_atmos case ConfigParseStatus::DuplicateSpeciesDetected: return "DuplicateSpeciesDetected"; case ConfigParseStatus::DuplicatePhasesDetected: return "DuplicatePhasesDetected"; case ConfigParseStatus::PhaseRequiresUnknownSpecies: return "PhaseRequiresUnknownSpecies"; + case ConfigParseStatus::ReactionRequiresUnknownSpecies: return "ReactionRequiresUnknownSpecies"; default: return "Unknown"; } } @@ -159,7 +160,7 @@ namespace open_atmos return true; } - bool PhaseRequiresUnknownSpecies(const std::vector requested_species, const std::vector& existing_species) + bool RequiresUnknownSpecies(const std::vector requested_species, const std::vector& existing_species) { for (const auto& spec : requested_species) { @@ -257,7 +258,7 @@ namespace open_atmos phase.species = species; phase.unknown_properties = unknown_properties; - if (PhaseRequiresUnknownSpecies(species, existing_species)) + if (RequiresUnknownSpecies(species, existing_species)) { status = ConfigParseStatus::PhaseRequiresUnknownSpecies; break; @@ -280,8 +281,12 @@ namespace open_atmos status = ValidateSchema(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys); if (status == ConfigParseStatus::Success) { - double coefficient = object[validation::keys.coefficient].get(); 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(); + } auto comments = GetComments(object, validation::reaction_component.required_keys, validation::reaction_component.optional_keys); @@ -373,6 +378,18 @@ namespace open_atmos 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 (RequiresUnknownSpecies(requested_species, existing_species)) { + status = ConfigParseStatus::ReactionRequiresUnknownSpecies; + } + arrhenius.gas_phase = object[validation::keys.gas_phase].get(); arrhenius.products = products; arrhenius.reactants = reactants; @@ -393,7 +410,8 @@ namespace open_atmos if (type == validation::keys.Arrhenius_key) { auto arrhenius_parse = ParseArrhenius(object, existing_species); - if (arrhenius_parse.first != ConfigParseStatus::Success) + status = arrhenius_parse.first; + if (status != ConfigParseStatus::Success) { break; } diff --git a/test/unit/test_parse_arrhenius.cpp b/test/unit/test_parse_arrhenius.cpp index a219867..ebe5a52 100644 --- a/test/unit/test_parse_arrhenius.cpp +++ b/test/unit/test_parse_arrhenius.cpp @@ -8,8 +8,9 @@ TEST(JsonParser, CanParseValidArrheniusReaction) { JsonParser parser; auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/valid.json")); + EXPECT_EQ(status, ConfigParseStatus::Success); - EXPECT_EQ(mechanism.reactions.arrhenius.size(), 2); + EXPECT_EQ(mechanism.reactions.arrhenius.size(), 3); EXPECT_EQ(mechanism.reactions.arrhenius[0].name, "my arrhenius"); EXPECT_EQ(mechanism.reactions.arrhenius[0].gas_phase, "gas"); @@ -41,10 +42,30 @@ TEST(JsonParser, CanParseValidArrheniusReaction) EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[0].coefficient, 2); EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[1].species_name, "B"); EXPECT_EQ(mechanism.reactions.arrhenius[1].reactants[1].coefficient, 0.1); - EXPECT_EQ(mechanism.reactions.arrhenius[1].products.size(), 1); EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].species_name, "C"); EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].coefficient, 0.5); EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].unknown_properties.size(), 1); EXPECT_EQ(mechanism.reactions.arrhenius[1].products[0].unknown_properties["__optional thing"], "\"hello\""); + + EXPECT_EQ(mechanism.reactions.arrhenius[2].name, ""); + EXPECT_EQ(mechanism.reactions.arrhenius[2].gas_phase, "gas"); + EXPECT_EQ(mechanism.reactions.arrhenius[2].A, 1); + EXPECT_EQ(mechanism.reactions.arrhenius[2].B, 0); + EXPECT_EQ(mechanism.reactions.arrhenius[2].C, 0); + EXPECT_EQ(mechanism.reactions.arrhenius[2].D, 300); + EXPECT_EQ(mechanism.reactions.arrhenius[2].E, 0); + EXPECT_EQ(mechanism.reactions.arrhenius[2].reactants.size(), 1); + EXPECT_EQ(mechanism.reactions.arrhenius[2].reactants[0].species_name, "A"); + EXPECT_EQ(mechanism.reactions.arrhenius[2].reactants[0].coefficient, 1); + EXPECT_EQ(mechanism.reactions.arrhenius[2].products.size(), 1); + EXPECT_EQ(mechanism.reactions.arrhenius[2].products[0].species_name, "C"); + EXPECT_EQ(mechanism.reactions.arrhenius[2].products[0].coefficient, 1); +} + +TEST(JsonParser, ArrheniusDetectsUnknownSpecies) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/unknown_species.json")); + EXPECT_EQ(status, ConfigParseStatus::ReactionRequiresUnknownSpecies); } \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/arrhenius/unknown_species.json b/test/unit/unit_configs/reactions/arrhenius/unknown_species.json new file mode 100644 index 0000000..a92afa3 --- /dev/null +++ b/test/unit/unit_configs/reactions/arrhenius/unknown_species.json @@ -0,0 +1,37 @@ +{ + "version": "1.0.0", + "name": "Unknown species", + "species": [ + { + "name": "A" + }, + { + "name": "B" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B" + ] + } + ], + "reactions": [ + { + "type": "ARRHENIUS", + "gas phase": "gas", + "reactants": [ + { + "species name": "A" + } + ], + "products": [ + { + "species name": "C" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/arrhenius/valid.json b/test/unit/unit_configs/reactions/arrhenius/valid.json index 7e50291..2ef1013 100644 --- a/test/unit/unit_configs/reactions/arrhenius/valid.json +++ b/test/unit/unit_configs/reactions/arrhenius/valid.json @@ -76,6 +76,20 @@ "D": 6.4, "E": -0.3, "name": "my arrhenius2" + }, + { + "type": "ARRHENIUS", + "gas phase": "gas", + "reactants": [ + { + "species name": "A" + } + ], + "products": [ + { + "species name": "C" + } + ] } ] } \ No newline at end of file From f3511638b3d4ae7fac4ec5e008a2edc010cd5554 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 17 Jan 2024 14:52:48 -0600 Subject: [PATCH 26/26] error detection --- .../mechanism_configuration/validation.hpp | 2 +- src/parser.cpp | 8 ++-- test/integration/test_json_parser.cpp | 7 ++++ test/unit/test_parse_arrhenius.cpp | 14 +++++++ .../arrhenius/bad_reaction_component.json | 37 ++++++++++++++++++ .../arrhenius/mutually_exclusive.json | 39 +++++++++++++++++++ 6 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 test/unit/unit_configs/reactions/arrhenius/bad_reaction_component.json create mode 100644 test/unit/unit_configs/reactions/arrhenius/mutually_exclusive.json diff --git a/include/open_atmos/mechanism_configuration/validation.hpp b/include/open_atmos/mechanism_configuration/validation.hpp index ff4224f..6f5de43 100644 --- a/include/open_atmos/mechanism_configuration/validation.hpp +++ b/include/open_atmos/mechanism_configuration/validation.hpp @@ -86,7 +86,7 @@ namespace open_atmos struct Arrhenius { const std::vector required_keys{ keys.products, keys.reactants, keys.type, keys.gas_phase }; - const std::vector optional_keys{ keys.A, keys.B, keys.C, keys.D, keys.E, keys.name }; + const std::vector optional_keys{ keys.A, keys.B, keys.C, keys.D, keys.E, keys.Ea, keys.name }; } arrhenius; struct Mechanism diff --git a/src/parser.cpp b/src/parser.cpp index 440830f..6bc9552 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -317,7 +317,8 @@ namespace open_atmos for (const auto& product : object[validation::keys.products]) { auto product_parse = ParseReactionComponent(product); - if (product_parse.first != ConfigParseStatus::Success) { + status = product_parse.first; + if (status != ConfigParseStatus::Success) { break; } products.push_back(product_parse.second); @@ -327,7 +328,8 @@ namespace open_atmos for (const auto& reactant : object[validation::keys.reactants]) { auto reactant_parse = ParseReactionComponent(reactant); - if (reactant_parse.first != ConfigParseStatus::Success) { + status = reactant_parse.first; + if (status != ConfigParseStatus::Success) { break; } reactants.push_back(reactant_parse.second); @@ -386,7 +388,7 @@ namespace open_atmos requested_species.push_back(spec.species_name); } - if (RequiresUnknownSpecies(requested_species, existing_species)) { + if (status == ConfigParseStatus::Success && RequiresUnknownSpecies(requested_species, existing_species)) { status = ConfigParseStatus::ReactionRequiresUnknownSpecies; } diff --git a/test/integration/test_json_parser.cpp b/test/integration/test_json_parser.cpp index 1a73d81..c49b2de 100644 --- a/test/integration/test_json_parser.cpp +++ b/test/integration/test_json_parser.cpp @@ -12,4 +12,11 @@ TEST(JsonParser, ParsesFullConfiguration) EXPECT_EQ(mechanism.name, "Full Configuration"); EXPECT_EQ(mechanism.species.size(), 10); EXPECT_EQ(mechanism.reactions.arrhenius.size(), 1); +} + +TEST(JsonParser, ParserReportsBadFiles) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("examples/_missing_configuration.json")); + EXPECT_EQ(status, ConfigParseStatus::InvalidFilePath); } \ No newline at end of file diff --git a/test/unit/test_parse_arrhenius.cpp b/test/unit/test_parse_arrhenius.cpp index ebe5a52..e26fba1 100644 --- a/test/unit/test_parse_arrhenius.cpp +++ b/test/unit/test_parse_arrhenius.cpp @@ -68,4 +68,18 @@ TEST(JsonParser, ArrheniusDetectsUnknownSpecies) JsonParser parser; auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/unknown_species.json")); EXPECT_EQ(status, ConfigParseStatus::ReactionRequiresUnknownSpecies); +} + +TEST(JsonParser, ArrheniusDetectsMutuallyExclusiveOptions) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/mutually_exclusive.json")); + EXPECT_EQ(status, ConfigParseStatus::MutuallyExclusiveOption); +} + +TEST(JsonParser, ArrheniusDetectsBadReactionComponent) +{ + JsonParser parser; + auto [status, mechanism] = parser.Parse(std::string("unit_configs/reactions/arrhenius/bad_reaction_component.json")); + EXPECT_EQ(status, ConfigParseStatus::RequiredKeyNotFound); } \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/arrhenius/bad_reaction_component.json b/test/unit/unit_configs/reactions/arrhenius/bad_reaction_component.json new file mode 100644 index 0000000..859209d --- /dev/null +++ b/test/unit/unit_configs/reactions/arrhenius/bad_reaction_component.json @@ -0,0 +1,37 @@ +{ + "version": "1.0.0", + "name": "Mutually Exclusive", + "species": [ + { + "name": "A" + }, + { + "name": "B" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B" + ] + } + ], + "reactions": [ + { + "type": "ARRHENIUS", + "gas phase": "gas", + "reactants": [ + { + "Species name": "A" + } + ], + "products": [ + { + "species name": "B" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/reactions/arrhenius/mutually_exclusive.json b/test/unit/unit_configs/reactions/arrhenius/mutually_exclusive.json new file mode 100644 index 0000000..61db970 --- /dev/null +++ b/test/unit/unit_configs/reactions/arrhenius/mutually_exclusive.json @@ -0,0 +1,39 @@ +{ + "version": "1.0.0", + "name": "Mutually Exclusive", + "species": [ + { + "name": "A" + }, + { + "name": "B" + } + ], + "phases": [ + { + "name": "gas", + "species": [ + "A", + "B" + ] + } + ], + "reactions": [ + { + "type": "ARRHENIUS", + "gas phase": "gas", + "reactants": [ + { + "species name": "A" + } + ], + "products": [ + { + "species name": "B" + } + ], + "C": 10, + "Ea": 0.5 + } + ] +} \ No newline at end of file