From e795c10fa7ac1ab04640cec863518fd0c29b72de Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 18 Sep 2023 16:15:24 -0500 Subject: [PATCH 001/318] moved the user guide --- docs/CMakeLists.txt | 10 +++------- docs/source/index.rst | 4 ++-- docs/source/user_guide.rst | 5 ----- docs/source/user_guide/index.rst | 12 ++++++++++++ include/micm/version.hpp | 4 ++-- 5 files changed, 19 insertions(+), 16 deletions(-) delete mode 100644 docs/source/user_guide.rst create mode 100644 docs/source/user_guide/index.rst diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index f7c930fdd..dd0c5b382 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -41,21 +41,17 @@ set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/sphinx) set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html) add_custom_command( - OUTPUT ${SPHINX_INDEX_FILE} + OUTPUT ${SPHINX_INDEX_FILE} __fake_file_to_ensure_this_always_run COMMAND ${SPHINX_EXECUTABLE} -b html # Tell Breathe where to find the Doxygen output -Dbreathe_projects.micm=${DOXYGEN_OUTPUT_DIR}/xml ${SPHINX_SOURCE} ${SPHINX_BUILD} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS - # Other docs files you want to track should go here (or in some variable) - ${CMAKE_CURRENT_SOURCE_DIR}/source/index.rst - ${CMAKE_CURRENT_SOURCE_DIR}/source/getting_started.rst - ${CMAKE_CURRENT_SOURCE_DIR}/source/user_guide.rst - ${DOXYGEN_INDEX_FILE} MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py COMMENT "Generating documentation with Sphinx" + # run this command each time. This way we don't have to keep track of each individual rst file + ALWAYS ) add_custom_target(docs ALL DEPENDS ${SPHINX_INDEX_FILE}) diff --git a/docs/source/index.rst b/docs/source/index.rst index 9e321b597..7c06c60f7 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -22,7 +22,7 @@ Welcome to MICM's documentation! .. grid-item-card:: User guide :img-top: _static/index_user_guide.svg - :link: user_guide + :link: user_guide/index :link-type: doc Learn how to configure micm for your mechanisms here! @@ -48,7 +48,7 @@ Welcome to MICM's documentation! :caption: Contents: getting_started - user_guide + user_guide/index api/index contributing/index citation diff --git a/docs/source/user_guide.rst b/docs/source/user_guide.rst deleted file mode 100644 index 181084693..000000000 --- a/docs/source/user_guide.rst +++ /dev/null @@ -1,5 +0,0 @@ - - -########## -User Guide -########## diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst new file mode 100644 index 000000000..a928dccd8 --- /dev/null +++ b/docs/source/user_guide/index.rst @@ -0,0 +1,12 @@ + + +########## +User Guide +########## + +MICM is quite expansive in its capabilities. We've written examples showing many different use cases. +Hopefully our examples are exhaustive enough to provide you with the tools you need to solve some atmospheric chemistry. + +If you happen to find our examples are lacking for your needs, please, +`fill out an issue `_ and request the kind of example you'd like. + diff --git a/include/micm/version.hpp b/include/micm/version.hpp index eaf20c49b..6a5992c97 100644 --- a/include/micm/version.hpp +++ b/include/micm/version.hpp @@ -8,7 +8,7 @@ extern "C" { const char* getMicmVersion() { - return "3.1.0"; + return "3.2.0"; } unsigned getMicmVersionMajor() { @@ -16,7 +16,7 @@ extern "C" { } unsigned getMicmVersionMinor() { - return 1+0; + return 2+0; } unsigned getMicmVersionPatch() { From 188cab2f3d57a4dfb7cc0be3cd590bf022b928b5 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 18 Sep 2023 19:24:38 -0600 Subject: [PATCH 002/318] Added an example config.json with camp-files list. --- test/unit/unit_configs/camp/config.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/unit/unit_configs/camp/config.json diff --git a/test/unit/unit_configs/camp/config.json b/test/unit/unit_configs/camp/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/unit/unit_configs/camp/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} From 231209a96fe398d3f363cd5e86d6d71251ff270b Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 18 Sep 2023 19:47:29 -0600 Subject: [PATCH 003/318] In test_solver_config, added TEST(SolverConfig, ReadAndParseCAMPFiles). --- test/unit/configure/test_solver_config.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 4ea2080ac..c5b4d3c8b 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -9,6 +9,15 @@ TEST(SolverConfig, DetectsInvalidConfigFile) EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); } +TEST(SolverConfig, ReadAndParseCAMPFiles) +{ + micm::SolverConfig solverConfig{}; + + // Read and parse the CAMP configure file + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/camp"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); +} + TEST(SolverConfig, ReadAndParseSystemObject) { micm::SolverConfig solverConfig; From ce81070084782e2b26ca972afccaee71de864e1b Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 18 Sep 2023 20:22:04 -0600 Subject: [PATCH 004/318] Added CAMP_FILES constant string. --- include/micm/configure/solver_config.hpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index bb8511f41..e7e138b9e 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -102,6 +102,7 @@ namespace micm std::vector processes_; // Constants + // find names from config.json // Configure files static const inline std::string SPECIES_CONFIG = "species.json"; static const inline std::string MECHANISM_CONFIG = "mechanism.json"; @@ -110,6 +111,7 @@ namespace micm // Common JSON static const inline std::string CAMP_DATA = "camp-data"; + static const inline std::string CAMP_FILES = "camp-files"; static const inline std::string TYPE = "type"; // Functions @@ -118,6 +120,8 @@ namespace micm /// @return True for successful parsing ConfigParseStatus Parse(const std::filesystem::path& config_dir) { + // Look for CAMP_FILES here + // Create configure paths std::filesystem::path species_config(config_dir / SPECIES_CONFIG); std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); @@ -784,4 +788,4 @@ namespace micm } }; -} // namespace micm \ No newline at end of file +} // namespace micm From 00550c2ca7d54d332498be2996eed46450d159a2 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Tue, 19 Sep 2023 07:32:05 -0600 Subject: [PATCH 005/318] Read top level CAMP JSON if config.json file exists. --- include/micm/configure/solver_config.hpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index e7e138b9e..62a973596 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -104,6 +104,7 @@ namespace micm // Constants // find names from config.json // Configure files + static const inline std::string CAMP_CONFIG = "config.json"; static const inline std::string SPECIES_CONFIG = "species.json"; static const inline std::string MECHANISM_CONFIG = "mechanism.json"; static const inline std::string REACTIONS_CONFIG = "reactions.json"; @@ -120,14 +121,19 @@ namespace micm /// @return True for successful parsing ConfigParseStatus Parse(const std::filesystem::path& config_dir) { - // Look for CAMP_FILES here - // Create configure paths std::filesystem::path species_config(config_dir / SPECIES_CONFIG); std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); + // Look for CAMP config file + std::filesystem::path camp_config(config_dir / CAMP_CONFIG); + if (std::filesystem::exists(camp_config)) + { + json camp_config = json::parse(std::ifstream(camp_config)); + } + // Current reaction configs should be either mechanism_config or reactions config std::filesystem::path cur_reactions_config; From 0a61b2631c8178bdb8c8b0cf391a01235c089d94 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Tue, 19 Sep 2023 07:52:21 -0600 Subject: [PATCH 006/318] Added CAMPFilesSectionNotFound status. --- include/micm/configure/solver_config.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 62a973596..30a95c131 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -34,6 +34,7 @@ namespace micm InvalidKey, UnknownKey, InvalidSpecies, + CAMPFilesSectionNotFound, CAMPDataSectionNotFound, InvalidMechanism, ObjectTypeNotFound, @@ -51,6 +52,7 @@ namespace micm case ConfigParseStatus::InvalidKey: return "InvalidKey"; case ConfigParseStatus::UnknownKey: return "UnknownKey"; case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; + case ConfigParseStatus::CAMPFilesSectionNotFound: return "CAMPFilesSectionNotFound"; case ConfigParseStatus::CAMPDataSectionNotFound: return "CAMPDataSectionNotFound"; case ConfigParseStatus::InvalidMechanism: return "InvalidMechanism"; case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; @@ -131,7 +133,10 @@ namespace micm std::filesystem::path camp_config(config_dir / CAMP_CONFIG); if (std::filesystem::exists(camp_config)) { + std::cout << "Reading CAMP config " << camp_config << std::endl; json camp_config = json::parse(std::ifstream(camp_config)); + if (!camp_config.contains(CAMP_FILES)) + return ConfigParseStatus::CAMPFilesSectionNotFound; } // Current reaction configs should be either mechanism_config or reactions config From 3df9bf79f4c2a9ee4a866dcc0652f50ccea82a62 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Tue, 19 Sep 2023 09:26:13 -0600 Subject: [PATCH 007/318] Extract vector of CAMP filenames from JSON. --- include/micm/configure/solver_config.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 30a95c131..643acb4a2 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -134,9 +134,16 @@ namespace micm if (std::filesystem::exists(camp_config)) { std::cout << "Reading CAMP config " << camp_config << std::endl; - json camp_config = json::parse(std::ifstream(camp_config)); - if (!camp_config.contains(CAMP_FILES)) + json camp_data = json::parse(std::ifstream(camp_config)); + if (!camp_data.contains(CAMP_FILES)) return ConfigParseStatus::CAMPFilesSectionNotFound; + + std::vector camp_files; + for (const auto& element : camp_data[CAMP_FILES]) + { + std::cout << element.get() << std::endl; + camp_files.push_back(element.get()); + } } // Current reaction configs should be either mechanism_config or reactions config From b2e85975443c12cf3bcd331915b7551e39cd359d Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 19 Sep 2023 11:15:55 -0500 Subject: [PATCH 008/318] added rate constant tutorial file --- docs/source/user_guide/index.rst | 12 ++++++++++++ docs/source/user_guide/rate_constant_tutorial.rst | 10 ++++++++++ 2 files changed, 22 insertions(+) create mode 100644 docs/source/user_guide/rate_constant_tutorial.rst diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index a928dccd8..d65806cc9 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -10,3 +10,15 @@ Hopefully our examples are exhaustive enough to provide you with the tools you n If you happen to find our examples are lacking for your needs, please, `fill out an issue `_ and request the kind of example you'd like. + + +1. :doc:`Rate constants` (except user-defined ones) + +2. User defined rate constants + + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + rate_constant_tutorial \ No newline at end of file diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst new file mode 100644 index 000000000..0feff0a39 --- /dev/null +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -0,0 +1,10 @@ + +.. _Rate constants: + +Rate Constants (except user defined ones) +######################################### + +Some introductory content. +This is the content you want to include in the excerpt. + +More content in the source file. \ No newline at end of file From f68810f151815082b990a7f4d7f6f948bb39db6f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 19 Sep 2023 15:13:58 -0500 Subject: [PATCH 009/318] adding links to generated breathe documentation --- docs/CMakeLists.txt | 2 +- docs/source/user_guide/index.rst | 10 ++++------ .../user_guide/rate_constant_tutorial.rst | 17 +++++++++++++---- .../user_defined_rate_constant_tutorial.rst | 4 ++++ .../test_rate_constants_no_user_defined.cpp | 0 5 files changed, 22 insertions(+), 11 deletions(-) create mode 100644 docs/source/user_guide/user_defined_rate_constant_tutorial.rst create mode 100644 test/tutorial/test_rate_constants_no_user_defined.cpp diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index dd0c5b382..0f9be5aa8 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -51,7 +51,7 @@ add_custom_command( MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py COMMENT "Generating documentation with Sphinx" # run this command each time. This way we don't have to keep track of each individual rst file - ALWAYS + BuildDocs ) add_custom_target(docs ALL DEPENDS ${SPHINX_INDEX_FILE}) diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index d65806cc9..fad6b2bdc 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -1,5 +1,3 @@ - - ########## User Guide ########## @@ -12,13 +10,13 @@ If you happen to find our examples are lacking for your needs, please, -1. :doc:`Rate constants` (except user-defined ones) - -2. User defined rate constants +1. :ref:`Rate constants` +2. :ref:`User defined rate constants` .. toctree:: :maxdepth: 1 :caption: Contents: - rate_constant_tutorial \ No newline at end of file + rate_constant_tutorial + user_defined_rate_constant_tutorial diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 0feff0a39..a176736c2 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -1,10 +1,19 @@ - .. _Rate constants: Rate Constants (except user defined ones) ######################################### -Some introductory content. -This is the content you want to include in the excerpt. +MICM supports a subset of the rate constants defined as part of the +`OpenAtmos Mechanism Configuration `_ +We will be adding more in the future. At present, supported rate constants are: + +- :cpp:class:`micm::ArrheniusRateConstant` +- :cpp:class:`micm::BranchedRateConstant` +- :cpp:class:`micm::SurfaceRateConstant` +- :cpp:class:`micm::TernaryChemicalActivationRateConstant` +- :cpp:class:`micm::TroeRateConstant` +- :cpp:class:`micm::TunnelingRateConstant` +- :cpp:class:`micm::UserDefinedRateConstant` -More content in the source file. \ No newline at end of file +This tutorial covers all but the last one. See the :ref:`User defined rate constants` tutorial for examples and use +cases on that. diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst new file mode 100644 index 000000000..088af3302 --- /dev/null +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -0,0 +1,4 @@ +.. _User defined rate constants: + +User Defined Rate Constants +########################### diff --git a/test/tutorial/test_rate_constants_no_user_defined.cpp b/test/tutorial/test_rate_constants_no_user_defined.cpp new file mode 100644 index 000000000..e69de29bb From 3035433e2861dada944cdfc1dd738535d5a92132 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 20 Sep 2023 16:51:23 -0500 Subject: [PATCH 010/318] added most of an example --- docs/requirements.txt | 4 +- docs/source/_static/custom.css | 30 ++++ .../source/_static/tutorials/.gitkeep | 0 .../rate_constants_no_user_defined.zip | Bin 0 -> 888 bytes .../reactions.json | 3 + .../species.json | 3 + docs/source/conf.py | 2 + docs/source/getting_started.rst | 56 +----- docs/source/index.rst | 1 + .../user_guide/rate_constant_tutorial.rst | 97 ++++++++++- include/micm/process/process.hpp | 1 + include/micm/solver/rosenbrock.inl | 4 +- test/tutorial/CMakeLists.txt | 41 ++++- .../rate_constants_no_user_defined.zip | Bin 0 -> 888 bytes .../reactions.json | 3 + .../species.json | 3 + ...rate_constants_no_user_defined_by_hand.cpp | 161 ++++++++++++++++++ ..._constants_no_user_defined_with_config.cpp | 13 ++ 18 files changed, 355 insertions(+), 67 deletions(-) rename test/tutorial/test_rate_constants_no_user_defined.cpp => docs/source/_static/tutorials/.gitkeep (100%) create mode 100644 docs/source/_static/tutorials/rate_constants_no_user_defined.zip create mode 100644 docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json create mode 100644 docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json create mode 100644 test/tutorial/configs/rate_constants_no_user_defined.zip create mode 100644 test/tutorial/configs/rate_constants_no_user_defined/reactions.json create mode 100644 test/tutorial/configs/rate_constants_no_user_defined/species.json create mode 100644 test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp create mode 100644 test/tutorial/test_rate_constants_no_user_defined_with_config.cpp diff --git a/docs/requirements.txt b/docs/requirements.txt index 5c5432a54..46798caed 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,6 @@ +breathe sphinx sphinx-book-theme +sphinx-copybutton sphinx-design -breathe +sphinx-tabs \ No newline at end of file diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index 9c69a3a3a..655ca3fb4 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -4,4 +4,34 @@ margin-left: auto; margin-right: auto; margin-top: 10px; +} + +.download-div { + display: flex; + flex-direction: row; + justify-content: space-around; + margin-bottom: 1rem; +} + +.download-button { + display: inline-block; + padding: 10px 20px; + background-color: #3498db; + color: white; + border: none; + border-radius: 5px; + text-align: center; + text-decoration: none; + font-size: 16px; + cursor: pointer; + transition: background-color 0.3s; +} + +.download-button:hover { + background-color: #2980b9; +} + +blockquote { + padding: unset; + border-left: unset; } \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined.cpp b/docs/source/_static/tutorials/.gitkeep similarity index 100% rename from test/tutorial/test_rate_constants_no_user_defined.cpp rename to docs/source/_static/tutorials/.gitkeep diff --git a/docs/source/_static/tutorials/rate_constants_no_user_defined.zip b/docs/source/_static/tutorials/rate_constants_no_user_defined.zip new file mode 100644 index 0000000000000000000000000000000000000000..5e13a502774b94a2f6da40b55b1b1f4db67a6055 GIT binary patch literal 888 zcmWIWW@Zs#0D+Tr#^GQFl(1k>V9?XkFG(#f(Jv`2$uG)G%+XKI&r8cpFV-(gEJ=+A za*9h5^Gb^2^YY_Mi&Kl@Q&Q71^HNjvLqm8O*sHj>QtN;iM3+`@GcdCBPi0^L+ZF(} zm4ktUp}P@bD>u+)7Yb}GE=WzzOfA;SD$dUXyI>n1SL${k2GM9PSRM8tD8R!@$M4}P zR)zp?c8+i7Vc0hMkWzv-0=pDLWZ}FAQtUn5#kD1JR%1nC>~*8Nn;rWt{^2=1H2I~0!1Bin1P}W g29`96GLj!Iq(x+aH!B;+US=R%1GIG;5Hm0U0E;;4n*aa+ literal 0 HcmV?d00001 diff --git a/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json b/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json new file mode 100644 index 000000000..772d7048c --- /dev/null +++ b/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json @@ -0,0 +1,3 @@ +{ + asdf +} \ No newline at end of file diff --git a/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json b/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json new file mode 100644 index 000000000..21bdae398 --- /dev/null +++ b/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json @@ -0,0 +1,3 @@ +{ + basd +} \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 303cb8a56..8de8f8d9d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -32,7 +32,9 @@ # ones. extensions = [ 'breathe', + 'sphinx_copybutton', 'sphinx_design', + 'sphinx_tabs.tabs', ] breathe_default_project = "micm" diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 5e72efa61..695731edd 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -78,60 +78,8 @@ See the [MICM documentation](https://ncar.github.io/micm/) for details on the types of reactions available in MICM and how to configure them. To solve this system save the following code in a file named `foo_chem.cpp` -.. code-block:: C++ - - #include - #include - #include - #include - - using namespace micm; - - int main(const int argc, const char *argv[]) - { - auto foo = Species{ "Foo" }; - auto bar = Species{ "Bar" }; - auto baz = Species{ "Baz" }; - - Phase gas_phase{ std::vector{ foo, bar, baz } }; - - System chemical_system{ SystemParameters{ .gas_phase_ = gas_phase } }; - - Process r1 = Process::create() - .reactants({ foo }) - .products({ Yield(bar, 0.8), Yield(baz, 0.2) }) - .rate_constant(ArrheniusRateConstant({ .A_ = 1.0e-3 })) - .phase(gas_phase); - - Process r2 = Process::create() - .reactants({ foo, bar }) - .products({ Yield(baz, 1) }) - .rate_constant(ArrheniusRateConstant({ .A_ = 1.0e-5, .C_ = 110.0 })) - .phase(gas_phase); - - std::vector reactions{ r1, r2 }; - - RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - - State state = solver.GetState(); - - state.conditions_[0].temperature_ = 287.45; // K - state.conditions_[0].pressure_ = 101319.9; // Pa - state.SetConcentration(foo, 20.0); // mol m-3 - - std::cout << "foo, bar, baz" << std::endl; - for (int i = 0; i < 10; ++i) - { - auto result = solver.Solve(500.0, state); - state.variables_ = result.result_; - std::cout << std::fixed << std::setprecision(6) - << state.variables_[0][state.variable_map_["Foo"]] << ", " - << state.variables_[0][state.variable_map_["Bar"]] << ", " - << state.variables_[0][state.variable_map_["Baz"]] << std::endl; - } - - return 0; - } +.. literalinclude:: ../../test/tutorial/test_README_example.cpp + :language: cpp To build and run the example using GNU:: diff --git a/docs/source/index.rst b/docs/source/index.rst index 7c06c60f7..8b7c691f9 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,6 +7,7 @@ .. ~ for subsubsubsections .. " for paragraphs +================================ Welcome to MICM's documentation! ================================ diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index a176736c2..1bbe22a41 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -1,19 +1,98 @@ .. _Rate constants: Rate Constants (except user defined ones) -######################################### +========================================= MICM supports a subset of the rate constants defined as part of the `OpenAtmos Mechanism Configuration `_ -We will be adding more in the future. At present, supported rate constants are: +We will be adding more in the future. +The links to the ``micm`` classes below detail the class and methods. Please check the OpenAtmos standard for +specific on the algorithm implemented for each rate constant type. +At present, supported rate constants are: -- :cpp:class:`micm::ArrheniusRateConstant` -- :cpp:class:`micm::BranchedRateConstant` -- :cpp:class:`micm::SurfaceRateConstant` -- :cpp:class:`micm::TernaryChemicalActivationRateConstant` -- :cpp:class:`micm::TroeRateConstant` -- :cpp:class:`micm::TunnelingRateConstant` -- :cpp:class:`micm::UserDefinedRateConstant` +- :cpp:class:`micm::ArrheniusRateConstant`, `OpenAtmos Arrhenius description `_ +- :cpp:class:`micm::BranchedRateConstant`, `OpenAtmos Branched description `_ +- :cpp:class:`micm::SurfaceRateConstant`, `OpenAtmos Surface description `_ +- :cpp:class:`micm::TernaryChemicalActivationRateConstant`, `OpenAtmos Ternay chemical activiation description `_ +- :cpp:class:`micm::TroeRateConstant`, `OpenAtmos Troe description `_ +- :cpp:class:`micm::TunnelingRateConstant`, `OpenAtmos Tunneling description `_ +- :cpp:class:`micm::UserDefinedRateConstant`, this is a custom type, but this is how photolysis rates are setup `OpenAtmos Photolysis description `_ This tutorial covers all but the last one. See the :ref:`User defined rate constants` tutorial for examples and use cases on that. + +We'll setup and solve a fake chemical system with 4 species and 6 reactions, + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{arrhenius}} \\ + B &\longrightarrow C (\mathrm{alkoxy\ products}) + D (\mathrm{nitrate\ products}), &k_{2, \mathrm{branched}} \\ + 2C &\longrightarrow A, &k_{3, \mathrm{surface}} \\ + D &\longrightarrow 2B, &k_{4, \mathrm{ternary\ chemical\ activation}} \\ + A &\longrightarrow C, &k_{5, \mathrm{troe}} \\ + B &\longrightarrow D, &k_{6, \mathrm{tunneling}} \\ + + +MICM can be configured in two ways. We can either build the mechanism up by hand with the ``micm`` API, +or parse a valid mechanism Configuration +in the OpenAtmos format. In this tutorial, we will do both. If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. + + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + + .. tab:: OpenAtmos Configuration reading + + .. raw:: html + + + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +To get started, we'll need to include each rate constant type and the +rosenbrock solver at the top of the file. + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 1-11 + +After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the +rosenbrock solver. + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 13-20 + +Next we have to define the chemical system we want to solve. + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + To do this by hand, we have to define all of the chemical species in the system. This allows us to set + any properties of the species that may be necessary for rate constanta calculations, like molecular weights + and diffusion coefficients for the surface reaction. + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 25-31 + + Products are created with a :cpp:type:`micm::Yield` :cpp:func:`micm::yields` + + .. tab:: OpenAtmos Configuration reading + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp + :language: cpp \ No newline at end of file diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index 5319d3ee7..f9c7a674e 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -15,6 +15,7 @@ namespace micm { + /// @brief An alias that allows making products easily using Yield = std::pair; Yield yields(micm::Species species, double yield) diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 863ad9e8a..d8f89323f 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -642,7 +642,7 @@ namespace micm result.final_time_ = present_time; result.stats_ = stats_; - result.result_ = std::move(Y); + result.result_ = Y; return result; } @@ -762,4 +762,4 @@ namespace micm return std::max(std::sqrt(error / N_), error_min_); } -} // namespace micm \ No newline at end of file +} // namespace micm diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index 23fc4088d..d1f4cfdaf 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -6,4 +6,43 @@ include(test_util) ################################################################################ # Tests -create_standard_test(NAME README_example SOURCES test_README_example.cpp) \ No newline at end of file +create_standard_test(NAME README_example SOURCES test_README_example.cpp) +create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) +create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) + +################################################################################ +# Copy test data + +add_custom_target(copy_tutorial_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) + +################################################################################ +# Create zip folders of the configurations so that they can be +# placed in the docs _static folder for downloads + +file(GLOB subdirectories LIST_DIRECTORIES true "${CMAKE_CURRENT_SOURCE_DIR}/configs/*") + +# Loop through the list of directories +foreach(subdirectory ${subdirectories}) + # Check if the current item is a directory + if(IS_DIRECTORY ${subdirectory}) + # Extract the directory name from the path + get_filename_component(directory_name ${subdirectory} NAME) + + # set the output file name + # set(output_archive "${CMAKE_SOURCE_DIR}/docs/source/_static/tutorials/${directory_name}.zip") + set(output_archive "${CMAKE_CURRENT_SOURCE_DIR}/configs/${directory_name}.zip") + + message(STATUS "subdirectory: ${subdirectory}") + + # create the archive + file(ARCHIVE_CREATE + OUTPUT "${output_archive}" + PATHS + "${subdirectory}" + FORMAT "zip" + ) + + message(STATUS "Created archive: ${output_archive}") + endif() +endforeach() \ No newline at end of file diff --git a/test/tutorial/configs/rate_constants_no_user_defined.zip b/test/tutorial/configs/rate_constants_no_user_defined.zip new file mode 100644 index 0000000000000000000000000000000000000000..5e13a502774b94a2f6da40b55b1b1f4db67a6055 GIT binary patch literal 888 zcmWIWW@Zs#0D+Tr#^GQFl(1k>V9?XkFG(#f(Jv`2$uG)G%+XKI&r8cpFV-(gEJ=+A za*9h5^Gb^2^YY_Mi&Kl@Q&Q71^HNjvLqm8O*sHj>QtN;iM3+`@GcdCBPi0^L+ZF(} zm4ktUp}P@bD>u+)7Yb}GE=WzzOfA;SD$dUXyI>n1SL${k2GM9PSRM8tD8R!@$M4}P zR)zp?c8+i7Vc0hMkWzv-0=pDLWZ}FAQtUn5#kD1JR%1nC>~*8Nn;rWt{^2=1H2I~0!1Bin1P}W g29`96GLj!Iq(x+aH!B;+US=R%1GIG;5Hm0U0E;;4n*aa+ literal 0 HcmV?d00001 diff --git a/test/tutorial/configs/rate_constants_no_user_defined/reactions.json b/test/tutorial/configs/rate_constants_no_user_defined/reactions.json new file mode 100644 index 000000000..772d7048c --- /dev/null +++ b/test/tutorial/configs/rate_constants_no_user_defined/reactions.json @@ -0,0 +1,3 @@ +{ + asdf +} \ No newline at end of file diff --git a/test/tutorial/configs/rate_constants_no_user_defined/species.json b/test/tutorial/configs/rate_constants_no_user_defined/species.json new file mode 100644 index 000000000..21bdae398 --- /dev/null +++ b/test/tutorial/configs/rate_constants_no_user_defined/species.json @@ -0,0 +1,3 @@ +{ + basd +} \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp new file mode 100644 index 000000000..b8fa00955 --- /dev/null +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -0,0 +1,161 @@ +#include +#include +#include + +// Each rate constant is in its own header file +#include +#include +#include +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + + +void print_header(){ + std::cout + << std::setw(8) << "time" << ", " + << std::setw(22) << "A" << ", " + << std::setw(22) << "B" << ", " + << std::setw(22) << "C" << ", " + << std::setw(22) << "D" + << std::endl; +} + +template class T> +void print_state(double time, State& state) { + std::cout + << std::defaultfloat + << std::setw(8) << time << ", " + << std::setw(20) << std::setprecision(16) + << std::setw(20) << std::scientific + << std::setw(20) << state.variables_[0][state.variable_map_["A"]] << ", " + << std::setw(20) << state.variables_[0][state.variable_map_["B"]] << ", " + << std::setw(20) << state.variables_[0][state.variable_map_["C"]] << ", " + << std::setw(20) << state.variables_[0][state.variable_map_["D"]] + << std::endl; +} + +int main(const int argc, const char *argv[]) +{ + auto a = Species("A"); + auto b = Species("B"); + auto c = Species( + "C", + std::map{ { "molecular weight [kg mol-1]", 0.025 }, + { "diffusion coefficient [m2 s-1]", 2.3e2 } }); + auto d = Species("D"); + + Phase gas_phase{ std::vector{ a, b, c, d } }; + + Process r1 = Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-11, .B_ = 0, .C_ = 110 })) + .phase(gas_phase); + + // a branched reaction has two output pathways + // this is represnted internal to micm as two different reactions + auto branched_params = BranchedRateConstantParameters{ .X_ = 1.2, .Y_ = 204.3, .a0_ = 1.0e-3, .n_ = 2 }; + branched_params.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + + Process r2 = Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(BranchedRateConstant(branched_params)) + .phase(gas_phase); + + branched_params.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + Process r3 = Process::create() + .reactants({ b }) + .products({ yields(d, 1) }) + .rate_constant(BranchedRateConstant(branched_params)) + .phase(gas_phase); + + // to have a stoichiemetric coefficient of more than one for reactants, + // list the reactant that many times + // A surface rate constant also needs to know the effective radius and particle number concentration + // we will set those later + Process r4 = Process::create() + .reactants({ c, c }) + .products({ yields(a, 1) }) + .rate_constant(SurfaceRateConstant({ .label_ = "c", .species_ = c, .reaction_probability_ = 0.74 })) + .phase(gas_phase); + + Process r5 = Process::create() + .reactants({ d }) + .products({ yields(b, 2) }) + .rate_constant(TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2, + .k0_B_ = 2.3, + .k0_C_ = 302.3, + .kinf_A_ = 2.6, + .kinf_B_ = -3.1, + .kinf_C_ = 402.1, + .Fc_ = 0.9, + .N_ = 1.2 })) + .phase(gas_phase); + + Process r6 = Process::create() + .reactants({ d }) + .products({ yields(b, 2) }) + .rate_constant(TunnelingRateConstant({ .A_ = 1.2, .B_ = 2.3, .C_ = 302.3 })) + .phase(gas_phase); + + auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + auto reactions = std::vector{ r1, r2, r3, r4, r5, r6 }; + + RosenbrockSolver solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + State state = solver.GetState(); + + std::cout << r4.rate_constant_->CustomParameters()[0] << std::endl; + + state.conditions_[0].temperature_ = 287.45; // K + state.conditions_[0].pressure_ = 101319.9; // Pa + + state.SetConcentration(a, 1.0); // mol m-3 + state.SetConcentration(b, 0.0); // mol m-3 + state.SetConcentration(c, 0.0); // mol m-3 + state.SetConcentration(d, 0.0); // mol m-3 + + state.SetCustomRateParameter("c.effective radius [m]", 1e-7); + state.SetCustomRateParameter("c.particle number concentration [# m-3]", 2.5e6); + + // choose a timestep a print the initial state + double time_step = 500; + + print_header(); + print_state(0, state); + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // std::cout << "solver state: " << StateToString(result.state_) << std::endl; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step*(i+1), state); + } + + return 0; +} diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp new file mode 100644 index 000000000..08e07215a --- /dev/null +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -0,0 +1,13 @@ +#include +#include +#include +#include + +using namespace micm; + +template +using SparseMatrixPolicy = SparseMatrix; + +int main(const int argc, const char *argv[]) +{ +} \ No newline at end of file From b052bab49179ababad08cc8b10a2ae668204ab73 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 21 Sep 2023 09:42:21 -0500 Subject: [PATCH 011/318] changing the reactions --- .../user_guide/rate_constant_tutorial.rst | 10 +-- ...rate_constants_no_user_defined_by_hand.cpp | 84 ++++++++++++------- 2 files changed, 57 insertions(+), 37 deletions(-) diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 1bbe22a41..3204f00e2 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -21,16 +21,16 @@ At present, supported rate constants are: This tutorial covers all but the last one. See the :ref:`User defined rate constants` tutorial for examples and use cases on that. -We'll setup and solve a fake chemical system with 4 species and 6 reactions, +We'll setup and solve a fake chemical system with 7 species and 6 reactions, .. math:: A &\longrightarrow B, &k_{1, \mathrm{arrhenius}} \\ B &\longrightarrow C (\mathrm{alkoxy\ products}) + D (\mathrm{nitrate\ products}), &k_{2, \mathrm{branched}} \\ - 2C &\longrightarrow A, &k_{3, \mathrm{surface}} \\ - D &\longrightarrow 2B, &k_{4, \mathrm{ternary\ chemical\ activation}} \\ - A &\longrightarrow C, &k_{5, \mathrm{troe}} \\ - B &\longrightarrow D, &k_{6, \mathrm{tunneling}} \\ + 2C &\longrightarrow E, &k_{3, \mathrm{surface}} \\ + D &\longrightarrow 2F, &k_{4, \mathrm{ternary\ chemical\ activation}} \\ + E &\longrightarrow G, &k_{5, \mathrm{troe}} \\ + F &\longrightarrow G, &k_{6, \mathrm{tunneling}} \\ MICM can be configured in two ways. We can either build the mechanism up by hand with the ``micm`` API, diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index b8fa00955..bbd8074d9 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -20,32 +20,35 @@ using namespace micm; template using SparseMatrixPolicy = SparseMatrix; - -void print_header(){ - std::cout - << std::setw(8) << "time" << ", " - << std::setw(22) << "A" << ", " - << std::setw(22) << "B" << ", " - << std::setw(22) << "C" << ", " - << std::setw(22) << "D" - << std::endl; +void print_header() +{ + std::cout << std::setw(4) << "time" + << ", " << std::setw(18) << "A" + << ", " << std::setw(18) << "B" + << ", " << std::setw(18) << "C" + << ", " << std::setw(18) << "D" + << ", " << std::setw(18) << "E" + << ", " << std::setw(18) << "F" + << ", " << std::setw(18) << "G" + << std::endl; } template class T> -void print_state(double time, State& state) { - std::cout - << std::defaultfloat - << std::setw(8) << time << ", " - << std::setw(20) << std::setprecision(16) - << std::setw(20) << std::scientific - << std::setw(20) << state.variables_[0][state.variable_map_["A"]] << ", " - << std::setw(20) << state.variables_[0][state.variable_map_["B"]] << ", " - << std::setw(20) << state.variables_[0][state.variable_map_["C"]] << ", " - << std::setw(20) << state.variables_[0][state.variable_map_["D"]] - << std::endl; +void print_state(double time, State& state) +{ + std::cout << std::defaultfloat << std::setw(4) << time << ", " + << std::scientific << std::setprecision(10) + << std::setw(18) << state.variables_[0][state.variable_map_["A"]] << ", " + << std::setw(18) << state.variables_[0][state.variable_map_["B"]] << ", " + << std::setw(18) << state.variables_[0][state.variable_map_["C"]] << ", " + << std::setw(18) << state.variables_[0][state.variable_map_["D"]] << ", " + << std::setw(18) << state.variables_[0][state.variable_map_["E"]] << ", " + << std::setw(18) << state.variables_[0][state.variable_map_["F"]] << ", " + << std::setw(18) << state.variables_[0][state.variable_map_["G"]] + << std::endl; } -int main(const int argc, const char *argv[]) +int main(const int argc, const char* argv[]) { auto a = Species("A"); auto b = Species("B"); @@ -54,13 +57,16 @@ int main(const int argc, const char *argv[]) std::map{ { "molecular weight [kg mol-1]", 0.025 }, { "diffusion coefficient [m2 s-1]", 2.3e2 } }); auto d = Species("D"); + auto e = Species("E"); + auto f = Species("F"); + auto g = Species("G"); - Phase gas_phase{ std::vector{ a, b, c, d } }; + Phase gas_phase{ std::vector{ a, b, c, d, e, f, g } }; Process r1 = Process::create() .reactants({ a }) .products({ yields(b, 1) }) - .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-11, .B_ = 0, .C_ = 110 })) + .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-1, .B_ = 0, .C_ = 110 })) .phase(gas_phase); // a branched reaction has two output pathways @@ -87,13 +93,13 @@ int main(const int argc, const char *argv[]) // we will set those later Process r4 = Process::create() .reactants({ c, c }) - .products({ yields(a, 1) }) + .products({ yields(e, 1) }) .rate_constant(SurfaceRateConstant({ .label_ = "c", .species_ = c, .reaction_probability_ = 0.74 })) .phase(gas_phase); Process r5 = Process::create() .reactants({ d }) - .products({ yields(b, 2) }) + .products({ yields(f, 2) }) .rate_constant(TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2, .k0_B_ = 2.3, .k0_C_ = 302.3, @@ -105,21 +111,32 @@ int main(const int argc, const char *argv[]) .phase(gas_phase); Process r6 = Process::create() - .reactants({ d }) - .products({ yields(b, 2) }) + .reactants({ e }) + .products({ yields(g, 1) }) + .rate_constant(TroeRateConstant({ .k0_A_ = 1.2e-12, + .k0_B_ = 167.0, + .k0_C_ = 3.0, + .kinf_A_ = 136.0, + .kinf_B_ = 5.0, + .kinf_C_ = 24.0, + .Fc_ = 0.9, + .N_ = 0.8 })) + .phase(gas_phase); + + Process r7 = Process::create() + .reactants({ f }) + .products({ yields(g, 1) }) .rate_constant(TunnelingRateConstant({ .A_ = 1.2, .B_ = 2.3, .C_ = 302.3 })) .phase(gas_phase); auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); - auto reactions = std::vector{ r1, r2, r3, r4, r5, r6 }; + auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7 }; RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); - std::cout << r4.rate_constant_->CustomParameters()[0] << std::endl; - state.conditions_[0].temperature_ = 287.45; // K state.conditions_[0].pressure_ = 101319.9; // Pa @@ -127,12 +144,15 @@ int main(const int argc, const char *argv[]) state.SetConcentration(b, 0.0); // mol m-3 state.SetConcentration(c, 0.0); // mol m-3 state.SetConcentration(d, 0.0); // mol m-3 + state.SetConcentration(e, 0.0); // mol m-3 + state.SetConcentration(f, 0.0); // mol m-3 + state.SetConcentration(g, 0.0); // mol m-3 state.SetCustomRateParameter("c.effective radius [m]", 1e-7); state.SetCustomRateParameter("c.particle number concentration [# m-3]", 2.5e6); // choose a timestep a print the initial state - double time_step = 500; + double time_step = 500; // s print_header(); print_state(0, state); @@ -154,7 +174,7 @@ int main(const int argc, const char *argv[]) state.variables_[0] = result.result_.AsVector(); } - print_state(time_step*(i+1), state); + print_state(time_step * (i + 1), state); } return 0; From 9709cc5f1eab62074ea094ff246517ece91ca150 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 21 Sep 2023 15:55:08 -0500 Subject: [PATCH 012/318] wrote the rate constant tutorial --- docs/CMakeLists.txt | 2 +- .../user_guide/rate_constant_tutorial.rst | 87 +++++++++++-- .../micm/process/surface_rate_constant.hpp | 5 +- ...nary_chemical_activation_rate_constant.hpp | 3 +- include/micm/solver/rosenbrock.hpp | 21 +++- .../rate_constants_no_user_defined.zip | Bin 888 -> 888 bytes .../reactions.json | 100 ++++++++++++++- .../species.json | 35 +++++- ...rate_constants_no_user_defined_by_hand.cpp | 61 ++++----- ..._constants_no_user_defined_with_config.cpp | 118 +++++++++++++++++- 10 files changed, 383 insertions(+), 49 deletions(-) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index 0f9be5aa8..c48767b73 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -54,4 +54,4 @@ add_custom_command( BuildDocs ) -add_custom_target(docs ALL DEPENDS ${SPHINX_INDEX_FILE}) +add_custom_target(docs ALL DEPENDS ${SPHINX_INDEX_FILE} Doxygen) diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 3204f00e2..7bbbb9eda 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -27,9 +27,9 @@ We'll setup and solve a fake chemical system with 7 species and 6 reactions, A &\longrightarrow B, &k_{1, \mathrm{arrhenius}} \\ B &\longrightarrow C (\mathrm{alkoxy\ products}) + D (\mathrm{nitrate\ products}), &k_{2, \mathrm{branched}} \\ - 2C &\longrightarrow E, &k_{3, \mathrm{surface}} \\ + C &\longrightarrow E, &k_{3, \mathrm{surface}} \\ D &\longrightarrow 2F, &k_{4, \mathrm{ternary\ chemical\ activation}} \\ - E &\longrightarrow G, &k_{5, \mathrm{troe}} \\ + 2E &\longrightarrow G, &k_{5, \mathrm{troe}} \\ F &\longrightarrow G, &k_{6, \mathrm{tunneling}} \\ @@ -67,16 +67,17 @@ rosenbrock solver at the top of the file. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 1-11 + :lines: 1-13 After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the rosenbrock solver. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 13-20 + :lines: 15-22 -Next we have to define the chemical system we want to solve. +To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) +and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these. .. tabs:: @@ -84,15 +85,83 @@ Next we have to define the chemical system we want to solve. To do this by hand, we have to define all of the chemical species in the system. This allows us to set any properties of the species that may be necessary for rate constanta calculations, like molecular weights - and diffusion coefficients for the surface reaction. + and diffusion coefficients for the surface reaction. We will also put these species into the gas phase. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 25-31 + :lines: 56-67 - Products are created with a :cpp:type:`micm::Yield` :cpp:func:`micm::yields` + Now that we have a gas phase and our species, we can start building the reactions. Two things to note are that + stoichiemtric coefficients for reactants are represented by repeating that product as many times as you need. + To specify the yield of a product, we've created a typedef :cpp:type:`micm::Yield` + and a function :cpp:func:`micm::yields` that produces these. + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 69-133 + + And finally we define our chemical system and reactions + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 135-136 .. tab:: OpenAtmos Configuration reading + After defining a valid OpenAtmos configuration with reactions that ``micm`` supports, configuring the chemical + system and the processes is as simple as using the :cpp:class:`micm::SolverConfig` class + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp - :language: cpp \ No newline at end of file + :language: cpp + :lines: 60-72 + +Now that we have a chemical system and a list of reactions, we can create the RosenbrockSolver. +There are several ways to configure the solver. Here we are using a three stage solver. More options +can be found in the :cpp:class:`micm::RosenbrockSolverParameters` + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 138-140 + +The rosenbrock solver will provide us a state, which we can use to set the concentrations, +custom rate parameters, and temperature and pressure + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 141-155 + +Finally, we are ready to pick a timestep ans solve the system. + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 157-183 + + +This is the output: + + ++-------+------------+------------+------------+------------+------------+------------+------------+ +| time | A | B | C | D | E | F | G | ++=======+============+============+============+============+============+============+============+ +| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 500 | 3.22e-09 | 3.70e-09 | 9.67e-01 | 3.92e-14 | 1.38e-03 | 2.04e-13 | 7.69e-03 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 1000 | 1.15e-14 | 1.33e-14 | 9.35e-01 | 1.40e-19 | 1.34e-03 | 7.31e-19 | 1.56e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 1500 | 4.14e-20 | 4.76e-20 | 9.06e-01 | 5.04e-25 | 1.29e-03 | 2.62e-24 | 2.30e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 2000 | 1.48e-25 | 1.71e-25 | 8.78e-01 | 1.81e-30 | 1.26e-03 | 9.40e-30 | 3.00e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 2500 | 5.32e-31 | 6.12e-31 | 8.52e-01 | 6.47e-36 | 1.22e-03 | 3.37e-35 | 3.65e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 3000 | 1.91e-36 | 2.19e-36 | 8.27e-01 | 2.32e-41 | 1.18e-03 | 1.21e-40 | 4.27e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 3500 | 6.84e-42 | 7.86e-42 | 8.04e-01 | 8.32e-47 | 1.15e-03 | 4.33e-46 | 4.85e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 4000 | 2.45e-47 | 2.82e-47 | 7.82e-01 | 2.98e-52 | 1.12e-03 | 1.55e-51 | 5.40e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 4500 | 8.80e-53 | 1.01e-52 | 7.61e-01 | 1.07e-57 | 1.09e-03 | 5.57e-57 | 5.92e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 5000 | 3.16e-58 | 3.63e-58 | 7.42e-01 | 3.84e-63 | 1.06e-03 | 2.00e-62 | 6.41e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index 1fc313640..69ae4bb24 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -77,8 +77,9 @@ namespace micm const double mean_free_speed = std::sqrt(mean_free_speed_factor_ * conditions.temperature_); const double radius = *(custom_parameters++); const double number = *(custom_parameters); - return (double)4.0 * number * M_PI * radius * radius / + double val = (double)4.0 * number * M_PI * radius * radius / (radius / diffusion_coefficient_ + 4.0 / (mean_free_speed * parameters_.reaction_probability_)); + return val; } inline std::vector SurfaceRateConstant::CustomParameters() const @@ -91,4 +92,4 @@ namespace micm { return 2; } -} // namespace micm \ No newline at end of file +} // namespace micm diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index 2b2f99fff..715fce2d2 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -79,7 +79,8 @@ namespace micm const Conditions& conditions, std::vector::const_iterator custom_parameters) const { - return calculate(conditions.temperature_, conditions.air_density_); + double val = calculate(conditions.temperature_, conditions.air_density_); + return val; } inline double TernaryChemicalActivationRateConstant::calculate(const double& temperature, const double& air_number_density) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 1e8fbab8d..8ecf7d14a 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -85,19 +85,38 @@ namespace micm // Print RosenbrockSolverParameters to console void print() const; + /// @brief an L-stable method, 2 stages, order 2 + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters two_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); + /// @brief an L-stable method, 3 stages, order 3, 2 function evaluations + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters three_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); + /// @brief L-stable rosenbrock method of order 4, with 4 stages + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters four_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); - + /// @brief A stiffly-stable method, 4 stages, order 3 + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters four_stage_differential_algebraic_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); + /// @brief stiffly-stable rosenbrock method of order 4, with 6 stages + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters six_stage_differential_algebraic_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); diff --git a/test/tutorial/configs/rate_constants_no_user_defined.zip b/test/tutorial/configs/rate_constants_no_user_defined.zip index 5e13a502774b94a2f6da40b55b1b1f4db67a6055..157771106c75c251cbd2d5c636e365927ef1f792 100644 GIT binary patch delta 44 vcmeyt_JeK0L+0pA?uk$4fvC-`j7E&CKt}4e$pOqFlP@q;gM=shF>3(;ef$qP delta 44 xcmeyt_JeK0L+0&#Toa$nW8TEawYinih>;b@Pu(^-fLUbn1*U3{@MJ$`EdYME4=?}# diff --git a/test/tutorial/configs/rate_constants_no_user_defined/reactions.json b/test/tutorial/configs/rate_constants_no_user_defined/reactions.json index 772d7048c..63f1d3a80 100644 --- a/test/tutorial/configs/rate_constants_no_user_defined/reactions.json +++ b/test/tutorial/configs/rate_constants_no_user_defined/reactions.json @@ -1,3 +1,99 @@ -{ - asdf +{ + "camp-data": [ + { + "name": "reaction rates no user defined", + "type": "MECHANISM", + "reactions": [ + { + "type": "ARRHENIUS", + "reactants": { + "A": {} + }, + "products": { + "B": {} + }, + "A": 2.15e-1, + "B": 0, + "C": 110.0 + }, + { + "type": "BRANCHED", + "reactants": { + "B": {} + }, + "alkoxy products": { + "C": {} + }, + "nitrate products": { + "D": {} + }, + "Y": 204.3, + "X": 1.2, + "a0": 1.0e-3, + "n": 2 + }, + { + "type": "SURFACE", + "gas-phase reactant": "C", + "gas-phase products": { + "E": { + "yield": 1.0 + } + }, + "MUSICA name": "C surface", + "reaction probability": 0.90 + }, + { + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": { + "D": { } + }, + "products": { + "F": { + "yield": 2 + } + }, + "k0_A": 1.2, + "k0_B": 2.3, + "k0_C": 302.3, + "kinf_A": 2.6, + "kinf_B": -3.1, + "kinf_C": 402.1, + "Fc": 0.9, + "N": 1.2 + }, + { + "type": "TROE", + "k0_A": 1.2e4, + "k0_B": 167.0, + "k0_C": 3.0, + "kinf_A": 136.0, + "kinf_B": 5.0, + "kinf_C": 24.0, + "Fc": 0.9, + "N": 0.8, + "reactants": { + "E": { + "qty": 2 + } + }, + "products": { + "G": {} + } + }, + { + "type": "TUNNELING", + "reactants": { + "F": {} + }, + "products": { + "G": {} + }, + "A": 1.2, + "B": 2.3, + "C": 302.3 + } + ] + } + ] } \ No newline at end of file diff --git a/test/tutorial/configs/rate_constants_no_user_defined/species.json b/test/tutorial/configs/rate_constants_no_user_defined/species.json index 21bdae398..5b6c4f171 100644 --- a/test/tutorial/configs/rate_constants_no_user_defined/species.json +++ b/test/tutorial/configs/rate_constants_no_user_defined/species.json @@ -1,3 +1,34 @@ -{ - basd +{ + "camp-data": [ + { + "name": "A", + "type": "CHEM_SPEC" + }, + { + "name": "B", + "type": "CHEM_SPEC" + }, + { + "name": "C", + "type": "CHEM_SPEC", + "molecular weight [kg mol-1]" : 0.025, + "diffusion coefficient [m2 s-1]" : 2.3e2 + }, + { + "name": "D", + "type": "CHEM_SPEC" + }, + { + "name": "E", + "type": "CHEM_SPEC" + }, + { + "name": "F", + "type": "CHEM_SPEC" + }, + { + "name": "G", + "type": "CHEM_SPEC" + } + ] } \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index bbd8074d9..b53f2851a 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -22,30 +22,33 @@ using SparseMatrixPolicy = SparseMatrix; void print_header() { - std::cout << std::setw(4) << "time" - << ", " << std::setw(18) << "A" - << ", " << std::setw(18) << "B" - << ", " << std::setw(18) << "C" - << ", " << std::setw(18) << "D" - << ", " << std::setw(18) << "E" - << ", " << std::setw(18) << "F" - << ", " << std::setw(18) << "G" - << std::endl; + std::cout << std::setw(5) << "time" + << "," << std::setw(11) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" + << "," << std::setw(10) << "D" + << "," << std::setw(10) << "E" + << "," << std::setw(10) << "F" + << "," << std::setw(10) << "G" << std::endl; } template class T> void print_state(double time, State& state) { - std::cout << std::defaultfloat << std::setw(4) << time << ", " - << std::scientific << std::setprecision(10) - << std::setw(18) << state.variables_[0][state.variable_map_["A"]] << ", " - << std::setw(18) << state.variables_[0][state.variable_map_["B"]] << ", " - << std::setw(18) << state.variables_[0][state.variable_map_["C"]] << ", " - << std::setw(18) << state.variables_[0][state.variable_map_["D"]] << ", " - << std::setw(18) << state.variables_[0][state.variable_map_["E"]] << ", " - << std::setw(18) << state.variables_[0][state.variable_map_["F"]] << ", " - << std::setw(18) << state.variables_[0][state.variable_map_["G"]] - << std::endl; + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ", " << std::flush; + + std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] + << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," + << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; + + std::cout.copyfmt(oldState); } int main(const int argc, const char* argv[]) @@ -87,14 +90,12 @@ int main(const int argc, const char* argv[]) .rate_constant(BranchedRateConstant(branched_params)) .phase(gas_phase); - // to have a stoichiemetric coefficient of more than one for reactants, - // list the reactant that many times // A surface rate constant also needs to know the effective radius and particle number concentration // we will set those later Process r4 = Process::create() - .reactants({ c, c }) + .reactants({ c }) .products({ yields(e, 1) }) - .rate_constant(SurfaceRateConstant({ .label_ = "c", .species_ = c, .reaction_probability_ = 0.74 })) + .rate_constant(SurfaceRateConstant({ .label_ = "C", .species_ = c, .reaction_probability_ = 0.90 })) .phase(gas_phase); Process r5 = Process::create() @@ -110,10 +111,12 @@ int main(const int argc, const char* argv[]) .N_ = 1.2 })) .phase(gas_phase); + // to have a stoichiemetric coefficient of more than one for reactants, + // list the reactant that many times Process r6 = Process::create() - .reactants({ e }) + .reactants({ e, e }) .products({ yields(g, 1) }) - .rate_constant(TroeRateConstant({ .k0_A_ = 1.2e-12, + .rate_constant(TroeRateConstant({ .k0_A_ = 1.2e4, .k0_B_ = 167.0, .k0_C_ = 3.0, .kinf_A_ = 136.0, @@ -148,11 +151,11 @@ int main(const int argc, const char* argv[]) state.SetConcentration(f, 0.0); // mol m-3 state.SetConcentration(g, 0.0); // mol m-3 - state.SetCustomRateParameter("c.effective radius [m]", 1e-7); - state.SetCustomRateParameter("c.particle number concentration [# m-3]", 2.5e6); + state.SetCustomRateParameter("C.effective radius [m]", 1e-7); + state.SetCustomRateParameter("C.particle number concentration [# m-3]", 2.5e6); - // choose a timestep a print the initial state - double time_step = 500; // s + // choose and timestep a print the initial state + double time_step = 500; // s print_header(); print_state(0, state); diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index 08e07215a..38f0f0089 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -1,13 +1,127 @@ +// #ifdef USE_JSON + +#include #include #include +#include + +// Each rate constant is in its own header file +#include #include +#include +#include +#include +#include +#include #include +// Use our namespace so that this example is easier to read using namespace micm; +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type template using SparseMatrixPolicy = SparseMatrix; -int main(const int argc, const char *argv[]) +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(10) << "A" + << "," << std::setw(9) << "B" + << "," << std::setw(9) << "C" + << "," << std::setw(9) << "D" + << "," << std::setw(9) << "E" + << "," << std::setw(9) << "F" + << "," << std::setw(10) << "G" << std::endl; +} + +template class T> +void print_state(double time, State& state) { -} \ No newline at end of file + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ", " << std::flush; + + std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] + << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," + << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; + + std::cout.copyfmt(oldState); +} + +int main(const int argc, const char* argv[]) +{ + SolverConfig solverConfig; + + std::string config_path = "./configs/rate_constants_no_user_defined"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + RosenbrockSolver solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + + State state = solver.GetState(); + + state.conditions_[0].temperature_ = 287.45; // K + state.conditions_[0].pressure_ = 101319.9; // Pa + + std::unordered_map> intial_concentration = { + { "A", { 1.0 } }, // mol m-3 + { "B", { 0.0 } }, // mol m-3 + { "C", { 0.0 } }, // mol m-3 + { "D", { 0.0 } }, // mol m-3 + { "E", { 0.0 } }, // mol m-3 + { "F", { 0.0 } }, // mol m-3 + { "G", { 0.0 } }, // mol m-3 + }; + + state.SetConcentrations(solver_params.system_, intial_concentration); + + state.SetCustomRateParameter("SURF.C surface.effective radius [m]", 1e-7); + state.SetCustomRateParameter("SURF.C surface.particle number concentration [# m-3]", 2.5e6); + + // choose a timestep a print the initial state + double time_step = 500; // s + + print_header(); + print_state(0, state); + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // std::cout << "solver state: " << StateToString(result.state_) << std::endl; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step * (i + 1), state); + } + + return 0; +} + +// #endif \ No newline at end of file From 9e2d69b22f03019591ab2ad6bb4a36a8569ca5a9 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 21 Sep 2023 15:58:07 -0500 Subject: [PATCH 013/318] showing how to set the state with the config version --- .../user_guide/rate_constant_tutorial.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 7bbbb9eda..adfe60a8a 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -126,9 +126,20 @@ can be found in the :cpp:class:`micm::RosenbrockSolverParameters` The rosenbrock solver will provide us a state, which we can use to set the concentrations, custom rate parameters, and temperature and pressure - .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp - :language: cpp - :lines: 141-155 +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp + :language: cpp + :lines: 141-155 + + .. tab:: OpenAtmos Configuration reading + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp + :language: cpp + :lines: 78-96 + Finally, we are ready to pick a timestep ans solve the system. From 193f4eee3c97bc7304cc92688d163098cb7db8b8 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 21 Sep 2023 16:03:15 -0500 Subject: [PATCH 014/318] correcting zip --- docs/source/_static/tutorials/.gitkeep | 0 ...e_constants_no_user_defined 4.02.42 PM.zip | Bin 0 -> 1359 bytes .../rate_constants_no_user_defined.zip | Bin 888 -> 0 bytes .../reactions.json | 3 -- .../species.json | 3 -- .../user_guide/rate_constant_tutorial.rst | 4 +- test/tutorial/CMakeLists.txt | 51 ++++++++++-------- .../rate_constants_no_user_defined.zip | Bin 888 -> 0 bytes ..._constants_no_user_defined_with_config.cpp | 6 +-- 9 files changed, 32 insertions(+), 35 deletions(-) delete mode 100644 docs/source/_static/tutorials/.gitkeep create mode 100644 docs/source/_static/tutorials/rate_constants_no_user_defined 4.02.42 PM.zip delete mode 100644 docs/source/_static/tutorials/rate_constants_no_user_defined.zip delete mode 100644 docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json delete mode 100644 docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json delete mode 100644 test/tutorial/configs/rate_constants_no_user_defined.zip diff --git a/docs/source/_static/tutorials/.gitkeep b/docs/source/_static/tutorials/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/source/_static/tutorials/rate_constants_no_user_defined 4.02.42 PM.zip b/docs/source/_static/tutorials/rate_constants_no_user_defined 4.02.42 PM.zip new file mode 100644 index 0000000000000000000000000000000000000000..c18fcdf7ec773141d64250ae177ae3305882ba8d GIT binary patch literal 1359 zcmWIWW@Zs#0D+Tr#^GQFl#pjoU?@r~NsUj=&nqrT%quC5&&!W5Elw?pPf1P7%u7wt z4-MgEV6WohO05H85M5fq&A`agKb3(2Y-j-3a1I6zhTIBMxZzw(3=GR z%$Jg?To-S?ytlMwrn}`=+siE|x{rmYrZ2=Hd-2npG`*L_ln=|{B)OL!OP42YXvV%F?)ZIo1Nju zxp_7s7x|A}Ogpn;e%ox0BPOYw9cBsBH%PGLh`Q-y#65j}`LCI^ebEO2!Ezg`_M)yQ zC8wYI2AO@@v(Wj@%O6u}(-s73N$7p9;NW~w&?vj)Ozs}evVB?`qvvZ>L@z5n%j12> zRceaB8Nt&{YB?G+rfBV!Q>u9JWsB>-K<(K5`TM>th|*qnzW-2&&-S(j;r<4~XPa^_ zPrHzQQDU>|bS`57BWF8lPPVsfeaqwI6W!n4*Y@psEA1FMnq~s$i+y%=-OG zNBqwX4i%5=7Iyyj*rGelcZZvA>F!^pZ?iJZUd!rDIHV`@F0DI5JTWm-dKy#WmMEF< z?ZJE;T8GWn7P)NLYB^ix)$ik;9txJzR@%$F-LJp(0l$PV$sd{`@cf`E^V`}N|G)1JTDR#- ztmdwm)Q^+*pO>nyKP&z7_MXBYjGz=|5%uWJb4CURP&x|mW@Hj!#+|caIsL68h(&Qe zhgb&7>BvbPl+$5gNn@0G0gbU;qFB literal 0 HcmV?d00001 diff --git a/docs/source/_static/tutorials/rate_constants_no_user_defined.zip b/docs/source/_static/tutorials/rate_constants_no_user_defined.zip deleted file mode 100644 index 5e13a502774b94a2f6da40b55b1b1f4db67a6055..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 888 zcmWIWW@Zs#0D+Tr#^GQFl(1k>V9?XkFG(#f(Jv`2$uG)G%+XKI&r8cpFV-(gEJ=+A za*9h5^Gb^2^YY_Mi&Kl@Q&Q71^HNjvLqm8O*sHj>QtN;iM3+`@GcdCBPi0^L+ZF(} zm4ktUp}P@bD>u+)7Yb}GE=WzzOfA;SD$dUXyI>n1SL${k2GM9PSRM8tD8R!@$M4}P zR)zp?c8+i7Vc0hMkWzv-0=pDLWZ}FAQtUn5#kD1JR%1nC>~*8Nn;rWt{^2=1H2I~0!1Bin1P}W g29`96GLj!Iq(x+aH!B;+US=R%1GIG;5Hm0U0E;;4n*aa+ diff --git a/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json b/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json deleted file mode 100644 index 772d7048c..000000000 --- a/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/reactions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - asdf -} \ No newline at end of file diff --git a/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json b/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json deleted file mode 100644 index 21bdae398..000000000 --- a/docs/source/_static/tutorials/test/tutorial/configs/rate_constants_no_user_defined/species.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - basd -} \ No newline at end of file diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index adfe60a8a..c09ea83ca 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -113,7 +113,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 60-72 + :lines: 58-70 Now that we have a chemical system and a list of reactions, we can create the RosenbrockSolver. There are several ways to configure the solver. Here we are using a three stage solver. More options @@ -138,7 +138,7 @@ custom rate parameters, and temperature and pressure .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 78-96 + :lines: 76-94 Finally, we are ready to pick a timestep ans solve the system. diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index d1f4cfdaf..430a4e8c5 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -8,7 +8,9 @@ include(test_util) create_standard_test(NAME README_example SOURCES test_README_example.cpp) create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) -create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) +if(ENABLE_JSON) + create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) +endif() ################################################################################ # Copy test data @@ -20,29 +22,34 @@ add_custom_target(copy_tutorial_configs ALL ${CMAKE_COMMAND} -E copy_directory # Create zip folders of the configurations so that they can be # placed in the docs _static folder for downloads -file(GLOB subdirectories LIST_DIRECTORIES true "${CMAKE_CURRENT_SOURCE_DIR}/configs/*") +# ~~~~~~~~~~~~~~~~ +# There appears to be a bug with the archive create: https://gitlab.kitware.com/cmake/cmake/-/issues/25260 +# ~~~~~~~~~~~~~~~~ -# Loop through the list of directories -foreach(subdirectory ${subdirectories}) - # Check if the current item is a directory - if(IS_DIRECTORY ${subdirectory}) - # Extract the directory name from the path - get_filename_component(directory_name ${subdirectory} NAME) - # set the output file name - # set(output_archive "${CMAKE_SOURCE_DIR}/docs/source/_static/tutorials/${directory_name}.zip") - set(output_archive "${CMAKE_CURRENT_SOURCE_DIR}/configs/${directory_name}.zip") +# file(GLOB subdirectories LIST_DIRECTORIES true "${CMAKE_CURRENT_SOURCE_DIR}/configs/*") - message(STATUS "subdirectory: ${subdirectory}") +# # Loop through the list of directories +# foreach(subdirectory ${subdirectories}) +# # Check if the current item is a directory +# if(IS_DIRECTORY ${subdirectory}) +# # Extract the directory name from the path +# get_filename_component(directory_name ${subdirectory} NAME) - # create the archive - file(ARCHIVE_CREATE - OUTPUT "${output_archive}" - PATHS - "${subdirectory}" - FORMAT "zip" - ) +# # set the output file name +# # set(output_archive "${CMAKE_SOURCE_DIR}/docs/source/_static/tutorials/${directory_name}.zip") +# set(output_archive "${CMAKE_CURRENT_SOURCE_DIR}/configs/${directory_name}.zip") - message(STATUS "Created archive: ${output_archive}") - endif() -endforeach() \ No newline at end of file +# message(STATUS "subdirectory: ${subdirectory}") + +# # create the archive +# file(ARCHIVE_CREATE +# OUTPUT "${output_archive}" +# PATHS +# "${subdirectory}" +# FORMAT "zip" +# ) + +# message(STATUS "Created archive: ${output_archive}") +# endif() +# endforeach() \ No newline at end of file diff --git a/test/tutorial/configs/rate_constants_no_user_defined.zip b/test/tutorial/configs/rate_constants_no_user_defined.zip deleted file mode 100644 index 157771106c75c251cbd2d5c636e365927ef1f792..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 888 zcmWIWW@Zs#0D+Tr#^GQFl(1k>V9?XkFG(#f(Jv`2$uG)G%+XKI&r8cpFV-(gEJ=+A za*9h5^Gb^2^YY_Mi&Kl@Q&Q71^HNjvLqm8O*sHj>QtN;iM3+`@GcdCBPi0^L+ZF(} zm4ktUp}P@bD>u+)7Yb}GE=WzzOfA;SD$dUXyI>n1S88-7cPfZRbHVDc2SEWIUOIjc zSFth#c(ZeSJ0Ba#3v>X;5eVmXqB+lv0_PQ_CMK6;0>cd9z)esGf@rJ`)baCt40YhA zLyNf)4rF8!Va6SA&?sbh>j+}eE*2rKfW;$n5Q5?n29`9IQQ!(vVl}`U;UZAfA%__# h>R@0=qbMW!;X+zO26(fwf$U`l!Zko!w*fH&0|1nB>ZJex diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index 38f0f0089..7012ae410 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -1,5 +1,3 @@ -// #ifdef USE_JSON - #include #include #include @@ -122,6 +120,4 @@ int main(const int argc, const char* argv[]) } return 0; -} - -// #endif \ No newline at end of file +} \ No newline at end of file From 3b854e1165e267bde206f2bd58d8437fb029fdcc Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 21 Sep 2023 16:05:00 -0500 Subject: [PATCH 015/318] correcting zip name --- ...42 PM.zip => rate_constants_no_user_defined.zip} | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/_static/tutorials/{rate_constants_no_user_defined 4.02.42 PM.zip => rate_constants_no_user_defined.zip} (100%) diff --git a/docs/source/_static/tutorials/rate_constants_no_user_defined 4.02.42 PM.zip b/docs/source/_static/tutorials/rate_constants_no_user_defined.zip similarity index 100% rename from docs/source/_static/tutorials/rate_constants_no_user_defined 4.02.42 PM.zip rename to docs/source/_static/tutorials/rate_constants_no_user_defined.zip From c021c5be7bea840231deb091b396472e54689b8b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 21 Sep 2023 16:10:54 -0500 Subject: [PATCH 016/318] removing format --- docs/source/user_guide/rate_constant_tutorial.rst | 4 ++-- .../test_rate_constants_no_user_defined_with_config.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index c09ea83ca..4fed39121 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -113,7 +113,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 58-70 + :lines: 57-69 Now that we have a chemical system and a list of reactions, we can create the RosenbrockSolver. There are several ways to configure the solver. Here we are using a three stage solver. More options @@ -138,7 +138,7 @@ custom rate parameters, and temperature and pressure .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 76-94 + :lines: 75-93 Finally, we are ready to pick a timestep ans solve the system. diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index 7012ae410..a43b9a69b 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -1,4 +1,3 @@ -#include #include #include #include From 05d72f283bff13ae9ea497f5c1f287ee14aff83f Mon Sep 17 00:00:00 2001 From: dwfncar Date: Fri, 22 Sep 2023 07:55:57 -0600 Subject: [PATCH 017/318] Temporary assumption that camp file list is ordered. --- include/micm/configure/solver_config.hpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 643acb4a2..29096098a 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -104,7 +104,6 @@ namespace micm std::vector processes_; // Constants - // find names from config.json // Configure files static const inline std::string CAMP_CONFIG = "config.json"; static const inline std::string SPECIES_CONFIG = "species.json"; @@ -144,6 +143,9 @@ namespace micm std::cout << element.get() << std::endl; camp_files.push_back(element.get()); } + // Temporary, for development purposes, assume camp files are ordered + species_config = camp_files[0]; + reactions_config = camp_files[1]; } // Current reaction configs should be either mechanism_config or reactions config From da2cab5a80cd8fe5642fd680c2df520212561d9f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 22 Sep 2023 13:25:36 -0500 Subject: [PATCH 018/318] adding user-defined tutorial --- .../tutorials/rate_constants_user_defined.zip | Bin 0 -> 1426 bytes .../user_guide/rate_constant_tutorial.rst | 4 +- .../user_defined_rate_constant_tutorial.rst | 236 +++++++++++++++++- test/tutorial/CMakeLists.txt | 3 + .../reactions.json | 119 +++++++++ .../rate_constants_user_defined/species.json | 34 +++ ...st_rate_constants_user_defined_by_hand.cpp | 211 ++++++++++++++++ ...ate_constants_user_defined_with_config.cpp | 131 ++++++++++ 8 files changed, 736 insertions(+), 2 deletions(-) create mode 100644 docs/source/_static/tutorials/rate_constants_user_defined.zip create mode 100644 test/tutorial/configs/rate_constants_user_defined/reactions.json create mode 100644 test/tutorial/configs/rate_constants_user_defined/species.json create mode 100644 test/tutorial/test_rate_constants_user_defined_by_hand.cpp create mode 100644 test/tutorial/test_rate_constants_user_defined_with_config.cpp diff --git a/docs/source/_static/tutorials/rate_constants_user_defined.zip b/docs/source/_static/tutorials/rate_constants_user_defined.zip new file mode 100644 index 0000000000000000000000000000000000000000..cc844e733b564a648a5b1b5f0353e8ee20467a9d GIT binary patch literal 1426 zcmWIWW@Zs#0D;t4vv4p2O2{xMFcc-0q{b)b=M|SE=9Lu3mlmfM#iyjEW#*-(=!b^z zGO)`X;Z2nXVh~+g!Og(P(m$1f0c>Ib*kld{4iuBQm>3u|2$)=4keZyCTCA5JEMES*HgzcWK&QF8<&PgNVDgHkhr8gt$>h|fu8JYAwiK0VnyEy zzV`C-6_kcCoxbua;rdILZGP!BRu6-A{YLV zbt$h)l%tnhC(d;+nzmYuA;6oRBP6h+X#p@)L4HSsibfV9yeDxoFlZ47m7>(dzqS4TI_A`E1_o7^_QwWJmO=X(<3$)Ato^fe8H=oe>r|e_2K|>= zQZ$aUsAyUU&S~U$|A@u?)bdv~aWbbrYhR9VIQ}i%JlNH7UEn+A!h07!i!Dpe|Fimg zFQcku@{aF|)lMwraw|@uG7TFYi>{c*cbAbk-wBLJx{7#LgZqp?^QM?wZy5$ z{8q;f&G8n$Ia&MG{$=&t7vff5d^`V$!i4Q-r3i7P-+qBs2_QG#TXW3j1Djoa%Vv==wR{p81$G6#7 zt}013%-JN%wK3sU&i)L$^Div#9j`p7vaD|E^~<-u$h^p9UbRw`cPGz+joa!EY<+8W zN$B;=g6i#S*5&o8RDWH?8n8f&>-;HOnd^zmUTnN(aO9-kj+}Q}G7~}$Z(L=r+mq{b zvtqJn9NWRhbE>{SZ&u{_F5e#Y(IsT9-QP7?hpJu|gs!!l*X7gm#xW%!WwGbkOHpf= z)lC$gSWx_LO4GDZr`;Fiq`9WbA39ceX6aiSul3DeFP3Jc-P{rON=w}H&#L^FQv3Dq zY~6k5*S0@*Zx-v;>^O02_Iro*%ch(7e`oFV{3*N8BRuuyy#0JJ`FT|bFMZA4I#HP~ z+2@pX=u6MU`IG+h`rTi)?|wz+J=WG*$B&9?OMX@D;{NmgWoE$NYN!2AcY6_hKH j{SL~NFtDVtfDym{NXw%E-mGjOvzdV~0%)=d3y22*Z?q0; literal 0 HcmV?d00001 diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 4fed39121..c8a0d2999 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -35,7 +35,9 @@ We'll setup and solve a fake chemical system with 7 species and 6 reactions, MICM can be configured in two ways. We can either build the mechanism up by hand with the ``micm`` API, or parse a valid mechanism Configuration -in the OpenAtmos format. In this tutorial, we will do both. If you're looking for a copy and paste, choose +in the OpenAtmos format. In this tutorial, we will do both. + +If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index 088af3302..c970afac2 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -1,4 +1,238 @@ .. _User defined rate constants: -User Defined Rate Constants +User-Defined Rate Constants ########################### + +This tutorial extends the :ref:`Rate constants` tutorial. That one showed how to use every type of rate constant +except the user-defined ones. The difference is that a user-defined rate constant must be updated by the user, +whereas the other rate constants update in response to a changing state. + +Internal to MICM, user-defined rate constants determine provide the ability to represent +emissions, loss, and photolysis rates. These can be static and the same for each time step, or dynamically updated +during the simulation. + +To show how this works, we'll add one photolysis reaction, one first-order loss reaction, and one emission + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{arrhenius}} \\ + B &\longrightarrow C (\mathrm{alkoxy\ products}) + D (\mathrm{nitrate\ products}), &k_{2, \mathrm{branched}} \\ + C &\longrightarrow E, &k_{3, \mathrm{surface}} \\ + D &\longrightarrow 2F, &k_{4, \mathrm{ternary\ chemical\ activation}} \\ + 2E &\longrightarrow G, &k_{5, \mathrm{troe}} \\ + F &\longrightarrow G, &k_{6, \mathrm{tunneling}} \\ + C &\longrightarrow G, &k_{7, \mathrm{photolysis}} \\ + &\longrightarrow A, &k_{8, \mathrm{emission}} \\ + B &\longrightarrow, &k_{9, \mathrm{loss}} \\ + + +If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_user_defined_by_hand.cpp + :language: cpp + + .. tab:: OpenAtmos Configuration reading + + .. raw:: html + + + + .. literalinclude:: ../../../test/tutorial/test_rate_constants_user_defined_with_config.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +Adding the custom rate constant is quite simple. Include the header file: + +.. code-block:: diff + + #include + #include + #include + #include + #include + #include + +#include + #include + + +Then setup the reaction which will use this rate constant: + + + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. code-block:: diff + + Process r7 = Process::create() + .reactants({ f }) + .products({ yields(g, 1) }) + .rate_constant(TunnelingRateConstant({ .A_ = 1.2, .B_ = 2.3, .C_ = 302.3 })) + .phase(gas_phase); + + + Process r8 = Process::create() + + .reactants({ c }) + + .products({ yields(g, 1) }) + + .rate_constant(UserDefinedRateConstant({.label_="my rate"})) + + .phase(gas_phase); + + + Process r9 = Process::create() + + .products({ yields(a, 1) }) + + .rate_constant(UserDefinedRateConstant({.label_="my emission rate"})) + + .phase(gas_phase); + + + Process r10 = Process::create() + + .reactants({ b }) + + .rate_constant(UserDefinedRateConstant({.label_="my loss rate"})) + + .phase(gas_phase); + + auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + - auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7 }; + + auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; + + + + .. tab:: OpenAtmos Configuration reading + + In this case, you only need to add the configuration to the reactions.json file in the configuration directory. + + + .. code-block:: diff + + + { + + "type": "PHOTOLYSIS", + + "reactants": { + + "C": {} + + }, + + "products": { + + "G": {} + + }, + + "MUSICA name": "my photolysis rate" + + }, + + { + + "type": "FIRST_ORDER_LOSS", + + "species": "B", + + "MUSICA name": "my loss rate" + + }, + + { + + "type": "EMISSION", + + "species": "A", + + "MUSICA name": "my emission rate" + + } + + +Finally, set and upate the rate constants as needed: + + + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. code-block:: diff + + +double photo_rate = 1e-10; + +double emission_rate = 1e-20; + +double loss = emission_rate * 1e-3; + +// these rates are constant through the simulation + +state.SetCustomRateParameter("my emission rate", emission_rate); + +state.SetCustomRateParameter("my loss rate", loss); + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + state.SetCustomRateParameter("my photolysis rate", photo_rate); + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // std::cout << "solver state: " << StateToString(result.state_) << std::endl; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step * (i + 1), state); + + photo_rate *= 10; + } + + .. tab:: OpenAtmos Configuration reading + + In this case, you only need to add the configuration to the reactions.json file in the configuration directory. + When reading in from a configuration file, the loss, emissions, and photolysis rates are prefixed with + ``LOSS.``, ``EMIS.``, and ``PHOTO.``. This differs slightly from defining the API by hand. + + + .. code-block:: diff + + +double photo_rate = 1e-10; + +double emission_rate = 1e-20; + +double loss = emission_rate * 1e-3; + +// these rates are constant through the simulation + +state.SetCustomRateParameter("EMIS.my emission rate", emission_rate); + +state.SetCustomRateParameter("LOSS.my loss rate", loss); + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + state.SetCustomRateParameter("PHOTO.my photolysis rate", photo_rate); + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // std::cout << "solver state: " << StateToString(result.state_) << std::endl; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step * (i + 1), state); + + photo_rate *= 10; + } + +And this is final output. Notice that the concentration of G ends up much higher than in +the :ref:`Rate constants` tutorial's result. + ++-------+------------+------------+------------+------------+------------+------------+------------+ +| time | A | B | C | D | E | F | G | ++=======+============+============+============+============+============+============+============+ +| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 1500 | 7.27e-20 | 6.40e-20 | 9.49e-01 | 6.53e-25 | 1.39e-03 | 3.19e-24 | 2.48e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 2000 | 3.17e-20 | 1.70e-20 | 9.33e-01 | 1.55e-25 | 1.38e-03 | 5.92e-25 | 3.30e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 2500 | 3.17e-20 | 1.70e-20 | 9.16e-01 | 1.55e-25 | 1.37e-03 | 5.92e-25 | 4.16e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 3000 | 3.17e-20 | 1.70e-20 | 8.96e-01 | 1.55e-25 | 1.35e-03 | 5.92e-25 | 5.40e-02 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 3500 | 3.17e-20 | 1.70e-20 | 8.37e-01 | 1.55e-25 | 1.31e-03 | 5.92e-25 | 1.05e-01 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 4000 | 3.17e-20 | 1.70e-20 | 4.99e-01 | 1.55e-25 | 1.02e-03 | 5.92e-25 | 4.38e-01 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 4500 | 3.17e-20 | 1.70e-20 | 3.07e-03 | 1.55e-25 | 1.88e-04 | 5.92e-25 | 9.33e-01 | ++-------+------------+------------+------------+------------+------------+------------+------------+ +| 5000 | 3.17e-20 | 1.70e-20 | -3.55e-07 | 1.55e-25 | 7.14e-05 | 5.92e-25 | 9.36e-01 | ++-------+------------+------------+------------+------------+------------+------------+------------+ diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index 430a4e8c5..db4007f65 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -7,9 +7,12 @@ include(test_util) # Tests create_standard_test(NAME README_example SOURCES test_README_example.cpp) + create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) +create_standard_test(NAME rate_constants_user_defined_example_by_hand SOURCES test_rate_constants_user_defined_by_hand.cpp) if(ENABLE_JSON) create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) + create_standard_test(NAME rate_constants_user_defined_example_with_config SOURCES test_rate_constants_user_defined_with_config.cpp) endif() ################################################################################ diff --git a/test/tutorial/configs/rate_constants_user_defined/reactions.json b/test/tutorial/configs/rate_constants_user_defined/reactions.json new file mode 100644 index 000000000..efb0f4316 --- /dev/null +++ b/test/tutorial/configs/rate_constants_user_defined/reactions.json @@ -0,0 +1,119 @@ +{ + "camp-data": [ + { + "name": "reaction rates no user defined", + "type": "MECHANISM", + "reactions": [ + { + "type": "ARRHENIUS", + "reactants": { + "A": {} + }, + "products": { + "B": {} + }, + "A": 2.15e-1, + "B": 0, + "C": 110.0 + }, + { + "type": "BRANCHED", + "reactants": { + "B": {} + }, + "alkoxy products": { + "C": {} + }, + "nitrate products": { + "D": {} + }, + "Y": 204.3, + "X": 1.2, + "a0": 1.0e-3, + "n": 2 + }, + { + "type": "SURFACE", + "gas-phase reactant": "C", + "gas-phase products": { + "E": { + "yield": 1.0 + } + }, + "MUSICA name": "C surface", + "reaction probability": 0.90 + }, + { + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": { + "D": {} + }, + "products": { + "F": { + "yield": 2 + } + }, + "k0_A": 1.2, + "k0_B": 2.3, + "k0_C": 302.3, + "kinf_A": 2.6, + "kinf_B": -3.1, + "kinf_C": 402.1, + "Fc": 0.9, + "N": 1.2 + }, + { + "type": "TROE", + "k0_A": 1.2e4, + "k0_B": 167.0, + "k0_C": 3.0, + "kinf_A": 136.0, + "kinf_B": 5.0, + "kinf_C": 24.0, + "Fc": 0.9, + "N": 0.8, + "reactants": { + "E": { + "qty": 2 + } + }, + "products": { + "G": {} + } + }, + { + "type": "TUNNELING", + "reactants": { + "F": {} + }, + "products": { + "G": {} + }, + "A": 1.2, + "B": 2.3, + "C": 302.3 + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "C": {} + }, + "products": { + "G": {} + }, + "MUSICA name": "my photolysis rate" + }, + { + "type": "FIRST_ORDER_LOSS", + "species": "B", + "MUSICA name": "my loss rate" + }, + { + "type": "EMISSION", + "species": "A", + "MUSICA name": "my emission rate" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/tutorial/configs/rate_constants_user_defined/species.json b/test/tutorial/configs/rate_constants_user_defined/species.json new file mode 100644 index 000000000..5b6c4f171 --- /dev/null +++ b/test/tutorial/configs/rate_constants_user_defined/species.json @@ -0,0 +1,34 @@ +{ + "camp-data": [ + { + "name": "A", + "type": "CHEM_SPEC" + }, + { + "name": "B", + "type": "CHEM_SPEC" + }, + { + "name": "C", + "type": "CHEM_SPEC", + "molecular weight [kg mol-1]" : 0.025, + "diffusion coefficient [m2 s-1]" : 2.3e2 + }, + { + "name": "D", + "type": "CHEM_SPEC" + }, + { + "name": "E", + "type": "CHEM_SPEC" + }, + { + "name": "F", + "type": "CHEM_SPEC" + }, + { + "name": "G", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp new file mode 100644 index 000000000..9bd45dd0b --- /dev/null +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -0,0 +1,211 @@ +#include +#include +#include + +// Each rate constant is in its own header file +#include +#include +#include +#include +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(11) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" + << "," << std::setw(10) << "D" + << "," << std::setw(10) << "E" + << "," << std::setw(10) << "F" + << "," << std::setw(10) << "G" << std::endl; +} + +template class T> +void print_state(double time, State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ", " << std::flush; + + std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] + << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," + << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; + + std::cout.copyfmt(oldState); +} + +int main(const int argc, const char* argv[]) +{ + auto a = Species("A"); + auto b = Species("B"); + auto c = Species( + "C", + std::map{ { "molecular weight [kg mol-1]", 0.025 }, + { "diffusion coefficient [m2 s-1]", 2.3e2 } }); + auto d = Species("D"); + auto e = Species("E"); + auto f = Species("F"); + auto g = Species("G"); + + Phase gas_phase{ std::vector{ a, b, c, d, e, f, g } }; + + Process r1 = Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-1, .B_ = 0, .C_ = 110 })) + .phase(gas_phase); + + // a branched reaction has two output pathways + // this is represnted internal to micm as two different reactions + auto branched_params = BranchedRateConstantParameters{ .X_ = 1.2, .Y_ = 204.3, .a0_ = 1.0e-3, .n_ = 2 }; + branched_params.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + + Process r2 = Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(BranchedRateConstant(branched_params)) + .phase(gas_phase); + + branched_params.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + Process r3 = Process::create() + .reactants({ b }) + .products({ yields(d, 1) }) + .rate_constant(BranchedRateConstant(branched_params)) + .phase(gas_phase); + + // A surface rate constant also needs to know the effective radius and particle number concentration + // we will set those later + Process r4 = Process::create() + .reactants({ c }) + .products({ yields(e, 1) }) + .rate_constant(SurfaceRateConstant({ .label_ = "C", .species_ = c, .reaction_probability_ = 0.90 })) + .phase(gas_phase); + + Process r5 = Process::create() + .reactants({ d }) + .products({ yields(f, 2) }) + .rate_constant(TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2, + .k0_B_ = 2.3, + .k0_C_ = 302.3, + .kinf_A_ = 2.6, + .kinf_B_ = -3.1, + .kinf_C_ = 402.1, + .Fc_ = 0.9, + .N_ = 1.2 })) + .phase(gas_phase); + + // to have a stoichiemetric coefficient of more than one for reactants, + // list the reactant that many times + Process r6 = Process::create() + .reactants({ e, e }) + .products({ yields(g, 1) }) + .rate_constant(TroeRateConstant({ .k0_A_ = 1.2e4, + .k0_B_ = 167.0, + .k0_C_ = 3.0, + .kinf_A_ = 136.0, + .kinf_B_ = 5.0, + .kinf_C_ = 24.0, + .Fc_ = 0.9, + .N_ = 0.8 })) + .phase(gas_phase); + + Process r7 = Process::create() + .reactants({ f }) + .products({ yields(g, 1) }) + .rate_constant(TunnelingRateConstant({ .A_ = 1.2, .B_ = 2.3, .C_ = 302.3 })) + .phase(gas_phase); + + Process r8 = Process::create() + .reactants({ c }) + .products({ yields(g, 1) }) + .rate_constant(UserDefinedRateConstant({.label_="my photolysis rate"})) + .phase(gas_phase); + + Process r9 = Process::create() + .products({ yields(a, 1) }) + .rate_constant(UserDefinedRateConstant({.label_="my emission rate"})) + .phase(gas_phase); + + Process r10 = Process::create() + .reactants({ b }) + .rate_constant(UserDefinedRateConstant({.label_="my loss rate"})) + .phase(gas_phase); + + auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; + + RosenbrockSolver solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + State state = solver.GetState(); + + state.conditions_[0].temperature_ = 287.45; // K + state.conditions_[0].pressure_ = 101319.9; // Pa + + state.SetConcentration(a, 1.0); // mol m-3 + state.SetConcentration(b, 0.0); // mol m-3 + state.SetConcentration(c, 0.0); // mol m-3 + state.SetConcentration(d, 0.0); // mol m-3 + state.SetConcentration(e, 0.0); // mol m-3 + state.SetConcentration(f, 0.0); // mol m-3 + state.SetConcentration(g, 0.0); // mol m-3 + + state.SetCustomRateParameter("C.effective radius [m]", 1e-7); + state.SetCustomRateParameter("C.particle number concentration [# m-3]", 2.5e6); + + + // choose and timestep a print the initial state + double time_step = 500; // s + + print_header(); + print_state(0, state); + + double photo_rate = 1e-10; + double emission_rate = 1e-20; + double loss = emission_rate * 1e-3; + // these rates are constant through the simulation + state.SetCustomRateParameter("my emission rate", emission_rate); + state.SetCustomRateParameter("my loss rate", loss); + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + state.SetCustomRateParameter("my photolysis rate", photo_rate); + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // std::cout << "solver state: " << StateToString(result.state_) << std::endl; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step * (i + 1), state); + photo_rate *= 10; + } + + return 0; +} diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp new file mode 100644 index 000000000..a14f7432d --- /dev/null +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -0,0 +1,131 @@ +#include +#include +#include + +// Each rate constant is in its own header file +#include +#include +#include +#include +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(10) << "A" + << "," << std::setw(9) << "B" + << "," << std::setw(9) << "C" + << "," << std::setw(9) << "D" + << "," << std::setw(9) << "E" + << "," << std::setw(9) << "F" + << "," << std::setw(10) << "G" << std::endl; +} + +template class T> +void print_state(double time, State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ", " << std::flush; + + std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] + << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," + << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) + << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; + + std::cout.copyfmt(oldState); +} + +int main(const int argc, const char* argv[]) +{ + SolverConfig solverConfig; + + std::string config_path = "./configs/rate_constants_user_defined"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + RosenbrockSolver solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + + State state = solver.GetState(); + + state.conditions_[0].temperature_ = 287.45; // K + state.conditions_[0].pressure_ = 101319.9; // Pa + + std::unordered_map> intial_concentration = { + { "A", { 1.0 } }, // mol m-3 + { "B", { 0.0 } }, // mol m-3 + { "C", { 0.0 } }, // mol m-3 + { "D", { 0.0 } }, // mol m-3 + { "E", { 0.0 } }, // mol m-3 + { "F", { 0.0 } }, // mol m-3 + { "G", { 0.0 } }, // mol m-3 + }; + + state.SetConcentrations(solver_params.system_, intial_concentration); + + state.SetCustomRateParameter("SURF.C surface.effective radius [m]", 1e-7); + state.SetCustomRateParameter("SURF.C surface.particle number concentration [# m-3]", 2.5e6); + + // choose a timestep a print the initial state + double time_step = 500; // s + + print_header(); + print_state(0, state); + + double photo_rate = 1e-10; + double emission_rate = 1e-20; + double loss = emission_rate * 1e-3; + // these rates are constant through the simulation + state.SetCustomRateParameter("EMIS.my emission rate", emission_rate); + state.SetCustomRateParameter("LOSS.my loss rate", loss); + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + state.SetCustomRateParameter("PHOTO.my photolysis rate", photo_rate); + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // std::cout << "solver state: " << StateToString(result.state_) << std::endl; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step * (i + 1), state); + photo_rate *= 10; + } + + return 0; +} \ No newline at end of file From 876883fdc228e7b5b28d0ec10cdcc9e4cb7e5331 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Sat, 23 Sep 2023 08:45:25 -0600 Subject: [PATCH 019/318] Added unit test json for CAMP config parsing. --- .../unit_configs/camp/camp_mechanism.json | 134 ++++++++++++++++++ test/unit/unit_configs/camp/camp_species.json | 53 +++++++ test/unit/unit_configs/camp/config.json | 2 +- 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 test/unit/unit_configs/camp/camp_mechanism.json create mode 100644 test/unit/unit_configs/camp/camp_species.json diff --git a/test/unit/unit_configs/camp/camp_mechanism.json b/test/unit/unit_configs/camp/camp_mechanism.json new file mode 100644 index 000000000..a0da29deb --- /dev/null +++ b/test/unit/unit_configs/camp/camp_mechanism.json @@ -0,0 +1,134 @@ +{ + "camp-data" : [ + { + "name" : "Chapman", + "type" : "MECHANISM", + "reactions" : [ + { + "type" : "PHOTOLYSIS", + "reactants" : { + "O2" : { } + }, + "products" : { + "O" : { "yield" : 2.0 } + }, + "MUSICA name" : "O2_1" + }, + { + "type" : "PHOTOLYSIS", + "reactants" : { + "O3" : { } + }, + "products" : { + "O1D" : { }, + "O2" : { } + }, + "MUSICA name" : "O3_1" + }, + { + "type" : "PHOTOLYSIS", + "reactants" : { + "O3" : { } + }, + "products" : { + "O" : { }, + "O2" : { } + }, + "MUSICA name" : "O3_2" + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O1D" : { }, + "N2" : { } + }, + "products" : { + "O" : { }, + "N2" : { } + }, + "A" : 2.15e-11, + "C" : 110.0 + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O1D" : { }, + "O2" : { } + }, + "products" : { + "O" : { }, + "O2" : { } + }, + "A" : 3.3e-11, + "C" : 55.0 + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O" : { }, + "O3" : { } + }, + "products" : { + "O2" : { "yield" : 2.0 } + }, + "A" : 8.0e-12, + "C" : -2060.00 + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O" : { }, + "O2" : { }, + "M" : { } + }, + "products" : { + "O3" : { }, + "M" : { } + }, + "A" : 6.0e-34, + "B" : 2.4 + }, + { + "type" : "EMISSION", + "species" : "O1D", + "MUSICA name" : "O1D" + }, + { + "type" : "EMISSION", + "species" : "O", + "MUSICA name" : "O" + }, + { + "type" : "EMISSION", + "species" : "O3", + "MUSICA name" : "O3" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "N2", + "MUSICA name" : "N2" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "O2", + "MUSICA name" : "O2" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "CO2", + "MUSICA name" : "CO2" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "Ar", + "MUSICA name" : "Ar" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "H2O", + "MUSICA name" : "H2O" + } + ] + } + ] +} diff --git a/test/unit/unit_configs/camp/camp_species.json b/test/unit/unit_configs/camp/camp_species.json new file mode 100644 index 000000000..75fc517ac --- /dev/null +++ b/test/unit/unit_configs/camp/camp_species.json @@ -0,0 +1,53 @@ +{ + "camp-data" : [ + { + "type" : "RELATIVE_TOLERANCE", + "value" : 1.0e-4 + }, + { + "name" : "M", + "type" : "CHEM_SPEC", + "tracer type" : "CONSTANT" + }, + { + "name" : "Ar", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "CO2", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "H2O", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "N2", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O1D", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O2", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O3", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/camp/config.json b/test/unit/unit_configs/camp/config.json index 61165b00d..352880268 100644 --- a/test/unit/unit_configs/camp/config.json +++ b/test/unit/unit_configs/camp/config.json @@ -1 +1 @@ -{"camp-files": ["species.json", "reactions.json"]} +{"camp-files": ["camp_species.json", "camp_reactions.json"]} From 6a31f588e34e819d76450cc61872199f2b11b02d Mon Sep 17 00:00:00 2001 From: dwfncar Date: Sat, 23 Sep 2023 08:56:35 -0600 Subject: [PATCH 020/318] Use camp_mechanism.json for reactions. --- test/unit/unit_configs/camp/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/unit_configs/camp/config.json b/test/unit/unit_configs/camp/config.json index 352880268..95836f060 100644 --- a/test/unit/unit_configs/camp/config.json +++ b/test/unit/unit_configs/camp/config.json @@ -1 +1 @@ -{"camp-files": ["camp_species.json", "camp_reactions.json"]} +{"camp-files": ["camp_species.json", "camp_mechanism.json"]} From 19496a6c2d373c82b9d8d8436add66a21d97b905 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Sat, 23 Sep 2023 08:57:09 -0600 Subject: [PATCH 021/318] Prepend config dir to files in CAMP list. --- include/micm/configure/solver_config.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 29096098a..06d6b9e1d 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -144,11 +144,11 @@ namespace micm camp_files.push_back(element.get()); } // Temporary, for development purposes, assume camp files are ordered - species_config = camp_files[0]; - reactions_config = camp_files[1]; + species_config = config_dir / camp_files[0]; + reactions_config = config_dir / camp_files[1]; } - // Current reaction configs should be either mechanism_config or reactions config + // Current reaction configs should be either mechanism_config or reactions_config std::filesystem::path cur_reactions_config; // Check if species config exists From 9b1d4554b94559f7cbbbd97945d52d951fee7223 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Sun, 24 Sep 2023 13:31:24 -0600 Subject: [PATCH 022/318] Return ConfigParseStatus::InvalidCAMPFileCount if camp_files size is not 2. --- include/micm/configure/solver_config.hpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 06d6b9e1d..52cfea9d2 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -35,6 +35,7 @@ namespace micm UnknownKey, InvalidSpecies, CAMPFilesSectionNotFound, + InvalidCAMPFileCount, CAMPDataSectionNotFound, InvalidMechanism, ObjectTypeNotFound, @@ -53,6 +54,7 @@ namespace micm case ConfigParseStatus::UnknownKey: return "UnknownKey"; case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; case ConfigParseStatus::CAMPFilesSectionNotFound: return "CAMPFilesSectionNotFound"; + case ConfigParseStatus::InvalidCAMPFileCount: return "InvalidCAMPFileCount"; case ConfigParseStatus::CAMPDataSectionNotFound: return "CAMPDataSectionNotFound"; case ConfigParseStatus::InvalidMechanism: return "InvalidMechanism"; case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; @@ -126,6 +128,7 @@ namespace micm std::filesystem::path species_config(config_dir / SPECIES_CONFIG); std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); + // Note tolerance_config is defined here but not used std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); // Look for CAMP config file @@ -143,6 +146,10 @@ namespace micm std::cout << element.get() << std::endl; camp_files.push_back(element.get()); } + if (camp_files.size() != 2) { + return ConfigParseStatus::InvalidCAMPFileCount; + } + // Temporary, for development purposes, assume camp files are ordered species_config = config_dir / camp_files[0]; reactions_config = config_dir / camp_files[1]; From e1d9aec6171b8091d1a8bca01e993f4a9b48f5c9 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Sun, 24 Sep 2023 13:51:27 -0600 Subject: [PATCH 023/318] Added unit test for InvalidCAMPFileCount. --- include/micm/configure/solver_config.hpp | 7 ++++--- test/unit/configure/test_solver_config.cpp | 9 +++++++++ test/unit/unit_configs/camp_invalid/config.json | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 test/unit/unit_configs/camp_invalid/config.json diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 52cfea9d2..65482794e 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -147,12 +147,13 @@ namespace micm camp_files.push_back(element.get()); } if (camp_files.size() != 2) { + std::string err_msg = "CAMP file list should contain two files [species.json, mechanism.json]"; + std::cerr << err_msg << std::endl; return ConfigParseStatus::InvalidCAMPFileCount; } - - // Temporary, for development purposes, assume camp files are ordered + // As a temporary implementation, assume camp files are ordered species_config = config_dir / camp_files[0]; - reactions_config = config_dir / camp_files[1]; + mechanism_config = config_dir / camp_files[1]; } // Current reaction configs should be either mechanism_config or reactions_config diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index c5b4d3c8b..ca07312f0 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -18,6 +18,15 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) EXPECT_EQ(micm::ConfigParseStatus::Success, status); } +TEST(SolverConfig, DetectsInvalideCAMPFileCount) +{ + micm::SolverConfig solverConfig{}; + + // Read and parse the CAMP configure file + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/camp_invalid"); + EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFileCount, status); +} + TEST(SolverConfig, ReadAndParseSystemObject) { micm::SolverConfig solverConfig; diff --git a/test/unit/unit_configs/camp_invalid/config.json b/test/unit/unit_configs/camp_invalid/config.json new file mode 100644 index 000000000..d8250971c --- /dev/null +++ b/test/unit/unit_configs/camp_invalid/config.json @@ -0,0 +1 @@ +{"camp-files": ["camp_species.json", "camp_mechanism.json", "other.json"]} From 89ebafe002d603fbdafb42cc10b730ff350dba85 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 25 Sep 2023 13:53:30 -0700 Subject: [PATCH 024/318] README updates (#252) Update README.md Adds a clarification of sphinx dependencies, and instructions for building the latest commit on main with Docker --- README.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fbd0abddb..869659c10 100644 --- a/README.md +++ b/README.md @@ -12,14 +12,14 @@ Copyright (C) 2018-2023 National Center for Atmospheric Research # Getting Started ## Installing MICM locally -To build and install MICM locally, you must have the following libraries installed: +To build and install MICM locally, you must have CMake installed on your machine. +If you plan to build the documentation, you must also have: - [sphinx](https://github.com/sphinx-doc/sphinx) - [sphinx-book-theme](https://github.com/executablebooks/sphinx-book-theme) - [sphinx-design](https://github.com/executablebooks/sphinx-design) - [breathe](https://github.com/breathe-doc/breathe) -You must also have CMake installed on your machine. Open a terminal window, navigate to a folder where you would like the MICM files to exist, and run the following commands: @@ -44,12 +44,23 @@ If you would later like to uninstall MICM, you can run ## Running a MICM Docker container -You must have [Docker Desktop](https://www.docker.com/get-started) installed and running. With Docker Desktop running, open a terminal window and run the following command to start the MICM container: +You must have [Docker Desktop](https://www.docker.com/get-started) installed and running. +With Docker Desktop running, open a terminal window. +To build the latest MICM release, run the following command to start the MICM container: ``` docker run -it ghcr.io/ncar/micm:release bash ``` +To build the latest pre-release version of MICM, instead run: + +``` +git clone https://github.com/NCAR/micm.git +cd micm +docker build -t micm . +docker run -it micm bash +``` + Inside the container, you can run the MICM tests from the `/build/` folder: ``` From 375a5ab57a59a2c8e7538323ae5ace6365f36bf9 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 25 Sep 2023 15:20:08 -0600 Subject: [PATCH 025/318] Added unit_configs/CAMP subdir. --- test/unit/unit_configs/{ => CAMP}/camp_invalid/config.json | 0 .../unit_configs/{camp => CAMP/camp_valid}/camp_mechanism.json | 0 .../unit/unit_configs/{camp => CAMP/camp_valid}/camp_species.json | 0 test/unit/unit_configs/{camp => CAMP/camp_valid}/config.json | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename test/unit/unit_configs/{ => CAMP}/camp_invalid/config.json (100%) rename test/unit/unit_configs/{camp => CAMP/camp_valid}/camp_mechanism.json (100%) rename test/unit/unit_configs/{camp => CAMP/camp_valid}/camp_species.json (100%) rename test/unit/unit_configs/{camp => CAMP/camp_valid}/config.json (100%) diff --git a/test/unit/unit_configs/camp_invalid/config.json b/test/unit/unit_configs/CAMP/camp_invalid/config.json similarity index 100% rename from test/unit/unit_configs/camp_invalid/config.json rename to test/unit/unit_configs/CAMP/camp_invalid/config.json diff --git a/test/unit/unit_configs/camp/camp_mechanism.json b/test/unit/unit_configs/CAMP/camp_valid/camp_mechanism.json similarity index 100% rename from test/unit/unit_configs/camp/camp_mechanism.json rename to test/unit/unit_configs/CAMP/camp_valid/camp_mechanism.json diff --git a/test/unit/unit_configs/camp/camp_species.json b/test/unit/unit_configs/CAMP/camp_valid/camp_species.json similarity index 100% rename from test/unit/unit_configs/camp/camp_species.json rename to test/unit/unit_configs/CAMP/camp_valid/camp_species.json diff --git a/test/unit/unit_configs/camp/config.json b/test/unit/unit_configs/CAMP/camp_valid/config.json similarity index 100% rename from test/unit/unit_configs/camp/config.json rename to test/unit/unit_configs/CAMP/camp_valid/config.json From e2b2ce6cc1d3d8297d8ac2f00d32c9c9224a69db Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 25 Sep 2023 15:59:15 -0600 Subject: [PATCH 026/318] Created CAMP unit_config subdir. --- test/unit/configure/test_solver_config.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index ca07312f0..663b3eddd 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -14,7 +14,7 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) micm::SolverConfig solverConfig{}; // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/camp"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); } @@ -23,7 +23,7 @@ TEST(SolverConfig, DetectsInvalideCAMPFileCount) micm::SolverConfig solverConfig{}; // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/camp_invalid"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid"); EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFileCount, status); } From be6bb53a9dd7b6a8a439f897f922d8ddb4c9be74 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 25 Sep 2023 17:12:15 -0600 Subject: [PATCH 027/318] Added unit test for NoCAMPFilesSectionFound. --- test/unit/configure/test_solver_config.cpp | 8 ++++++-- test/unit/unit_configs/CAMP/camp_no_files_key/config.json | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 test/unit/unit_configs/CAMP/camp_no_files_key/config.json diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 663b3eddd..c9e5b7e4d 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -18,13 +18,17 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) EXPECT_EQ(micm::ConfigParseStatus::Success, status); } -TEST(SolverConfig, DetectsInvalideCAMPFileCount) +TEST(SolverConfig, DetectsInvalidCAMPConfigFile) { micm::SolverConfig solverConfig{}; // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid"); + micm::ConfigParseStatus status; + status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid"); EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFileCount, status); + + status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_no_files_key"); + EXPECT_EQ(micm::ConfigParseStatus::CAMPFilesSectionNotFound, status); } TEST(SolverConfig, ReadAndParseSystemObject) diff --git a/test/unit/unit_configs/CAMP/camp_no_files_key/config.json b/test/unit/unit_configs/CAMP/camp_no_files_key/config.json new file mode 100644 index 000000000..134d5a852 --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_no_files_key/config.json @@ -0,0 +1 @@ +{"wrong-key": ["camp_species.json", "camp_mechanism.json"]} From 0acf911d31f2cfe5a6bed521bc37dc8b3f7d4a2f Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 26 Sep 2023 15:31:45 +0000 Subject: [PATCH 028/318] Commented out some stdout prints in solver_config.hpp. --- include/micm/configure/solver_config.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 65482794e..3882f91ee 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -135,7 +135,7 @@ namespace micm std::filesystem::path camp_config(config_dir / CAMP_CONFIG); if (std::filesystem::exists(camp_config)) { - std::cout << "Reading CAMP config " << camp_config << std::endl; + // std::cout << "Reading CAMP config " << camp_config << std::endl; json camp_data = json::parse(std::ifstream(camp_config)); if (!camp_data.contains(CAMP_FILES)) return ConfigParseStatus::CAMPFilesSectionNotFound; @@ -143,7 +143,7 @@ namespace micm std::vector camp_files; for (const auto& element : camp_data[CAMP_FILES]) { - std::cout << element.get() << std::endl; + // std::cout << element.get() << std::endl; camp_files.push_back(element.get()); } if (camp_files.size() != 2) { From 20dccce68af01f978ee17795d14d41fdab938329 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 10:38:05 -0500 Subject: [PATCH 029/318] removing template alias, correcting table output? --- .../user_guide/rate_constant_tutorial.rst | 42 +++++++++---------- ...rate_constants_no_user_defined_by_hand.cpp | 10 +---- ..._constants_no_user_defined_with_config.cpp | 10 +---- 3 files changed, 23 insertions(+), 39 deletions(-) diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 4fed39121..716d7ad94 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -67,14 +67,14 @@ rosenbrock solver at the top of the file. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 1-13 + :lines: 1-12 After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the rosenbrock solver. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 15-22 + :lines: 14-15 To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these. @@ -89,7 +89,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 56-67 + :lines: 50-61 Now that we have a gas phase and our species, we can start building the reactions. Two things to note are that stoichiemtric coefficients for reactants are represented by repeating that product as many times as you need. @@ -98,13 +98,13 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 69-133 + :lines: 63-127 And finally we define our chemical system and reactions .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 135-136 + :lines: 129-130 .. tab:: OpenAtmos Configuration reading @@ -113,7 +113,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 57-69 + :lines: 51-60 Now that we have a chemical system and a list of reactions, we can create the RosenbrockSolver. There are several ways to configure the solver. Here we are using a three stage solver. More options @@ -121,7 +121,7 @@ can be found in the :cpp:class:`micm::RosenbrockSolverParameters` .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 138-140 + :lines: 132-132 The rosenbrock solver will provide us a state, which we can use to set the concentrations, custom rate parameters, and temperature and pressure @@ -132,20 +132,20 @@ custom rate parameters, and temperature and pressure .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 141-155 + :lines: 133-147 .. tab:: OpenAtmos Configuration reading .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 75-93 + :lines: 67-85 -Finally, we are ready to pick a timestep ans solve the system. +Finally, we are ready to pick a timestep and solve the system. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 157-183 + :lines: 149-173 This is the output: @@ -156,23 +156,23 @@ This is the output: +=======+============+============+============+============+============+============+============+ | 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 500 | 3.22e-09 | 3.70e-09 | 9.67e-01 | 3.92e-14 | 1.38e-03 | 2.04e-13 | 7.69e-03 | +| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 1000 | 1.15e-14 | 1.33e-14 | 9.35e-01 | 1.40e-19 | 1.34e-03 | 7.31e-19 | 1.56e-02 | +| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 1500 | 4.14e-20 | 4.76e-20 | 9.06e-01 | 5.04e-25 | 1.29e-03 | 2.62e-24 | 2.30e-02 | +| 1500 | 4.09e-20 | 4.71e-20 | 9.49e-01 | 4.98e-25 | 1.39e-03 | 2.59e-24 | 2.48e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 2000 | 1.48e-25 | 1.71e-25 | 8.78e-01 | 1.81e-30 | 1.26e-03 | 9.40e-30 | 3.00e-02 | +| 2000 | 1.47e-25 | 1.69e-25 | 9.33e-01 | 1.79e-30 | 1.38e-03 | 9.30e-30 | 3.30e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 2500 | 5.32e-31 | 6.12e-31 | 8.52e-01 | 6.47e-36 | 1.22e-03 | 3.37e-35 | 3.65e-02 | +| 2500 | 5.26e-31 | 6.05e-31 | 9.17e-01 | 6.40e-36 | 1.37e-03 | 3.33e-35 | 4.11e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 3000 | 1.91e-36 | 2.19e-36 | 8.27e-01 | 2.32e-41 | 1.18e-03 | 1.21e-40 | 4.27e-02 | +| 3000 | 1.89e-36 | 2.17e-36 | 9.01e-01 | 2.30e-41 | 1.36e-03 | 1.20e-40 | 4.90e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 3500 | 6.84e-42 | 7.86e-42 | 8.04e-01 | 8.32e-47 | 1.15e-03 | 4.33e-46 | 4.85e-02 | +| 3500 | 6.77e-42 | 7.78e-42 | 8.85e-01 | 8.23e-47 | 1.34e-03 | 4.29e-46 | 5.68e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 4000 | 2.45e-47 | 2.82e-47 | 7.82e-01 | 2.98e-52 | 1.12e-03 | 1.55e-51 | 5.40e-02 | +| 4000 | 2.43e-47 | 2.79e-47 | 8.70e-01 | 2.95e-52 | 1.33e-03 | 1.54e-51 | 6.44e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 4500 | 8.80e-53 | 1.01e-52 | 7.61e-01 | 1.07e-57 | 1.09e-03 | 5.57e-57 | 5.92e-02 | +| 4500 | 8.70e-53 | 1.00e-52 | 8.55e-01 | 1.06e-57 | 1.32e-03 | 5.51e-57 | 7.20e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ -| 5000 | 3.16e-58 | 3.63e-58 | 7.42e-01 | 3.84e-63 | 1.06e-03 | 2.00e-62 | 6.41e-02 | +| 5000 | 3.12e-58 | 3.59e-58 | 8.40e-01 | 3.80e-63 | 1.31e-03 | 1.98e-62 | 7.94e-02 | +-------+------------+------------+------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index b53f2851a..e4b058ec9 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -14,12 +14,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -135,9 +129,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7 }; - RosenbrockSolver solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); state.conditions_[0].temperature_ = 287.45; // K diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index a43b9a69b..c5c3c3660 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -15,12 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -68,9 +62,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); From 2202a2a1fa1859f6c921300eeb0800677fca9764 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 11:20:25 -0500 Subject: [PATCH 030/318] correcting table --- .../user_guide/rate_constant_tutorial.rst | 72 +++++++++---------- ...rate_constants_no_user_defined_by_hand.cpp | 10 ++- ..._constants_no_user_defined_with_config.cpp | 10 ++- 3 files changed, 54 insertions(+), 38 deletions(-) diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 716d7ad94..a4ae3cf0d 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -67,14 +67,14 @@ rosenbrock solver at the top of the file. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 1-12 + :lines: 1-13 After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the rosenbrock solver. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 14-15 + :lines: 15-22 To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these. @@ -89,7 +89,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 50-61 + :lines: 56-67 Now that we have a gas phase and our species, we can start building the reactions. Two things to note are that stoichiemtric coefficients for reactants are represented by repeating that product as many times as you need. @@ -98,13 +98,13 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 63-127 + :lines: 69-133 And finally we define our chemical system and reactions .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 129-130 + :lines: 135-136 .. tab:: OpenAtmos Configuration reading @@ -113,7 +113,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 51-60 + :lines: 57-69 Now that we have a chemical system and a list of reactions, we can create the RosenbrockSolver. There are several ways to configure the solver. Here we are using a three stage solver. More options @@ -121,7 +121,7 @@ can be found in the :cpp:class:`micm::RosenbrockSolverParameters` .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 132-132 + :lines: 138-140 The rosenbrock solver will provide us a state, which we can use to set the concentrations, custom rate parameters, and temperature and pressure @@ -132,47 +132,47 @@ custom rate parameters, and temperature and pressure .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 133-147 + :lines: 141-155 .. tab:: OpenAtmos Configuration reading .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 67-85 + :lines: 75-93 -Finally, we are ready to pick a timestep and solve the system. +Finally, we are ready to pick a timestep ans solve the system. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 149-173 + :lines: 157-183 This is the output: -+-------+------------+------------+------------+------------+------------+------------+------------+ -| time | A | B | C | D | E | F | G | -+=======+============+============+============+============+============+============+============+ -| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 1500 | 4.09e-20 | 4.71e-20 | 9.49e-01 | 4.98e-25 | 1.39e-03 | 2.59e-24 | 2.48e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 2000 | 1.47e-25 | 1.69e-25 | 9.33e-01 | 1.79e-30 | 1.38e-03 | 9.30e-30 | 3.30e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 2500 | 5.26e-31 | 6.05e-31 | 9.17e-01 | 6.40e-36 | 1.37e-03 | 3.33e-35 | 4.11e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 3000 | 1.89e-36 | 2.17e-36 | 9.01e-01 | 2.30e-41 | 1.36e-03 | 1.20e-40 | 4.90e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 3500 | 6.77e-42 | 7.78e-42 | 8.85e-01 | 8.23e-47 | 1.34e-03 | 4.29e-46 | 5.68e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 4000 | 2.43e-47 | 2.79e-47 | 8.70e-01 | 2.95e-52 | 1.33e-03 | 1.54e-51 | 6.44e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 4500 | 8.70e-53 | 1.00e-52 | 8.55e-01 | 1.06e-57 | 1.32e-03 | 5.51e-57 | 7.20e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 5000 | 3.12e-58 | 3.59e-58 | 8.40e-01 | 3.80e-63 | 1.31e-03 | 1.98e-62 | 7.94e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ \ No newline at end of file ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| time | A | B | C | D | E | F | G | ++=======+=============+=============+=============+=============+=============+=============+=============+ +| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1500 | 4.09e-20 | 4.71e-20 | 9.49e-01 | 4.98e-25 | 1.39e-03 | 2.59e-24 | 2.48e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2000 | 1.47e-25 | 1.69e-25 | 9.33e-01 | 1.79e-30 | 1.38e-03 | 9.30e-30 | 3.30e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2500 | 5.26e-31 | 6.05e-31 | 9.17e-01 | 6.40e-36 | 1.37e-03 | 3.33e-35 | 4.11e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 3000 | 1.89e-36 | 2.17e-36 | 9.01e-01 | 2.30e-41 | 1.36e-03 | 1.20e-40 | 4.90e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 3500 | 6.77e-42 | 7.78e-42 | 8.85e-01 | 8.23e-47 | 1.34e-03 | 4.29e-46 | 5.68e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 4000 | 2.43e-47 | 2.79e-47 | 8.70e-01 | 2.95e-52 | 1.33e-03 | 1.54e-51 | 6.44e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 4500 | 8.70e-53 | 1.00e-52 | 8.55e-01 | 1.06e-57 | 1.32e-03 | 5.51e-57 | 7.20e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 5000 | 3.12e-58 | 3.59e-58 | 8.40e-01 | 3.80e-63 | 1.31e-03 | 1.98e-62 | 7.94e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index e4b058ec9..b53f2851a 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -14,6 +14,12 @@ // Use our namespace so that this example is easier to read using namespace micm; +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + void print_header() { std::cout << std::setw(5) << "time" @@ -129,7 +135,9 @@ int main(const int argc, const char* argv[]) auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7 }; - RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); state.conditions_[0].temperature_ = 287.45; // K diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index c5c3c3660..a43b9a69b 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -15,6 +15,12 @@ // Use our namespace so that this example is easier to read using namespace micm; +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + void print_header() { std::cout << std::setw(5) << "time" @@ -62,7 +68,9 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); From 254561e858b191a24eb9c4d4e1ca134741cc38fc Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 12:35:33 -0500 Subject: [PATCH 031/318] decreased the photolysis rate constant increases --- .../user_defined_rate_constant_tutorial.rst | 51 ++++++++++--------- ...st_rate_constants_user_defined_by_hand.cpp | 2 +- ...ate_constants_user_defined_with_config.cpp | 2 +- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index c970afac2..2e4d6b63e 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -211,28 +211,29 @@ Finally, set and upate the rate constants as needed: And this is final output. Notice that the concentration of G ends up much higher than in the :ref:`Rate constants` tutorial's result. -+-------+------------+------------+------------+------------+------------+------------+------------+ -| time | A | B | C | D | E | F | G | -+=======+============+============+============+============+============+============+============+ -| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 1500 | 7.27e-20 | 6.40e-20 | 9.49e-01 | 6.53e-25 | 1.39e-03 | 3.19e-24 | 2.48e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 2000 | 3.17e-20 | 1.70e-20 | 9.33e-01 | 1.55e-25 | 1.38e-03 | 5.92e-25 | 3.30e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 2500 | 3.17e-20 | 1.70e-20 | 9.16e-01 | 1.55e-25 | 1.37e-03 | 5.92e-25 | 4.16e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 3000 | 3.17e-20 | 1.70e-20 | 8.96e-01 | 1.55e-25 | 1.35e-03 | 5.92e-25 | 5.40e-02 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 3500 | 3.17e-20 | 1.70e-20 | 8.37e-01 | 1.55e-25 | 1.31e-03 | 5.92e-25 | 1.05e-01 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 4000 | 3.17e-20 | 1.70e-20 | 4.99e-01 | 1.55e-25 | 1.02e-03 | 5.92e-25 | 4.38e-01 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 4500 | 3.17e-20 | 1.70e-20 | 3.07e-03 | 1.55e-25 | 1.88e-04 | 5.92e-25 | 9.33e-01 | -+-------+------------+------------+------------+------------+------------+------------+------------+ -| 5000 | 3.17e-20 | 1.70e-20 | -3.55e-07 | 1.55e-25 | 7.14e-05 | 5.92e-25 | 9.36e-01 | -+-------+------------+------------+------------+------------+------------+------------+------------+ ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| time | A | B | C | D | E | F | G | ++=======+=============+=============+=============+=============+=============+=============+=============+ +| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1500 | 7.27e-20 | 6.40e-20 | 9.49e-01 | 6.53e-25 | 1.39e-03 | 3.19e-24 | 2.48e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2000 | 3.17e-20 | 1.70e-20 | 9.33e-01 | 1.55e-25 | 1.38e-03 | 5.92e-25 | 3.30e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2500 | 3.17e-20 | 1.70e-20 | 9.17e-01 | 1.55e-25 | 1.37e-03 | 5.92e-25 | 4.11e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 3000 | 3.17e-20 | 1.70e-20 | 9.01e-01 | 1.55e-25 | 1.36e-03 | 5.92e-25 | 4.90e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 3500 | 3.17e-20 | 1.70e-20 | 8.85e-01 | 1.55e-25 | 1.34e-03 | 5.92e-25 | 5.68e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 4000 | 3.17e-20 | 1.70e-20 | 8.70e-01 | 1.55e-25 | 1.33e-03 | 5.92e-25 | 6.44e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 4500 | 3.17e-20 | 1.70e-20 | 8.55e-01 | 1.55e-25 | 1.32e-03 | 5.92e-25 | 7.20e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 5000 | 3.17e-20 | 1.70e-20 | 8.40e-01 | 1.55e-25 | 1.31e-03 | 5.92e-25 | 7.94e-02 | ++-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ + diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index 9bd45dd0b..874f3196c 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -204,7 +204,7 @@ int main(const int argc, const char* argv[]) } print_state(time_step * (i + 1), state); - photo_rate *= 10; + photo_rate *= 1.5; } return 0; diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index a14f7432d..117b6b738 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -124,7 +124,7 @@ int main(const int argc, const char* argv[]) } print_state(time_step * (i + 1), state); - photo_rate *= 10; + photo_rate *= 1.5; } return 0; From 77041da7c7a704f340b3b9fa01b91e6150c6e824 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 12:41:04 -0500 Subject: [PATCH 032/318] Update docs/source/user_guide/user_defined_rate_constant_tutorial.rst Co-authored-by: Matt Dawson --- .../source/user_guide/user_defined_rate_constant_tutorial.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index 2e4d6b63e..4b6445119 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -7,8 +7,8 @@ This tutorial extends the :ref:`Rate constants` tutorial. That one showed how to except the user-defined ones. The difference is that a user-defined rate constant must be updated by the user, whereas the other rate constants update in response to a changing state. -Internal to MICM, user-defined rate constants determine provide the ability to represent -emissions, loss, and photolysis rates. These can be static and the same for each time step, or dynamically updated +Internal to MICM, user-defined rate constants provide the ability to represent +processes like emissions, loss, and photolysis that have rate constants that are calculated externally from MICM. These can be static and the same for each time step, or dynamically updated during the simulation. To show how this works, we'll add one photolysis reaction, one first-order loss reaction, and one emission From 2ba61c498665eeedf75bd20f59774e36adce7c34 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 12:41:14 -0500 Subject: [PATCH 033/318] Update test/tutorial/test_rate_constants_user_defined_by_hand.cpp Co-authored-by: Matt Dawson --- test/tutorial/test_rate_constants_user_defined_by_hand.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index 874f3196c..19fb16eb7 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -193,6 +193,7 @@ int main(const int argc, const char* argv[]) // so we need to track how much time the solver was able to integrate for and continue // solving until we finish double elapsed_solve_time = 0; + // this rate is updated at each time step and would typically vary with time state.SetCustomRateParameter("my photolysis rate", photo_rate); while (elapsed_solve_time < time_step) From 434303a24cc8ffe33da1525c51a36dde4c0c7029 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 12:43:11 -0500 Subject: [PATCH 034/318] Update test/tutorial/test_rate_constants_user_defined_with_config.cpp Co-authored-by: Matt Dawson --- test/tutorial/test_rate_constants_user_defined_with_config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index 117b6b738..d2e710071 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -113,6 +113,7 @@ int main(const int argc, const char* argv[]) // so we need to track how much time the solver was able to integrate for and continue // solving until we finish double elapsed_solve_time = 0; + // This rate is updated at each time step and would typically vary with time state.SetCustomRateParameter("PHOTO.my photolysis rate", photo_rate); while (elapsed_solve_time < time_step) From 12552c66f83e8c94457ecac27adad74c9acd9238 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 12:44:53 -0500 Subject: [PATCH 035/318] address pr comments --- .../source/user_guide/user_defined_rate_constant_tutorial.rst | 4 ++-- test/tutorial/test_rate_constants_user_defined_by_hand.cpp | 1 - .../tutorial/test_rate_constants_user_defined_with_config.cpp | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index 2e4d6b63e..58f0c7c5a 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -168,7 +168,7 @@ Finally, set and upate the rate constants as needed: } print_state(time_step * (i + 1), state); - + photo_rate *= 10; + + photo_rate *= 1.5; } .. tab:: OpenAtmos Configuration reading @@ -205,7 +205,7 @@ Finally, set and upate the rate constants as needed: } print_state(time_step * (i + 1), state); - + photo_rate *= 10; + + photo_rate *= 1.5; } And this is final output. Notice that the concentration of G ends up much higher than in diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index 874f3196c..ce6c6c8a1 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -199,7 +199,6 @@ int main(const int argc, const char* argv[]) { auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; - // std::cout << "solver state: " << StateToString(result.state_) << std::endl; state.variables_[0] = result.result_.AsVector(); } diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index 117b6b738..4e41e80ce 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -119,7 +119,6 @@ int main(const int argc, const char* argv[]) { auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; - // std::cout << "solver state: " << StateToString(result.state_) << std::endl; state.variables_[0] = result.result_.AsVector(); } From 5154fb552bc76e8e93da5ad73da3ef380468d14a Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 26 Sep 2023 17:46:01 +0000 Subject: [PATCH 036/318] Removed some commented out lines. --- include/micm/configure/solver_config.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 3882f91ee..97bcb387c 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -135,7 +135,6 @@ namespace micm std::filesystem::path camp_config(config_dir / CAMP_CONFIG); if (std::filesystem::exists(camp_config)) { - // std::cout << "Reading CAMP config " << camp_config << std::endl; json camp_data = json::parse(std::ifstream(camp_config)); if (!camp_data.contains(CAMP_FILES)) return ConfigParseStatus::CAMPFilesSectionNotFound; @@ -143,7 +142,6 @@ namespace micm std::vector camp_files; for (const auto& element : camp_data[CAMP_FILES]) { - // std::cout << element.get() << std::endl; camp_files.push_back(element.get()); } if (camp_files.size() != 2) { From dd116338d004d50127854610bbb11b4e398ff2ef Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 26 Sep 2023 17:18:38 -0500 Subject: [PATCH 037/318] started multiple grid cell example, need to figure out why 2 of the 3 aren't changing --- docs/source/user_guide/index.rst | 1 + .../source/user_guide/multiple_grid_cells.rst | 23 ++ test/tutorial/CMakeLists.txt | 1 + test/tutorial/test_multiple_grid_cells.cpp | 205 ++++++++++++++++++ ...rate_constants_no_user_defined_by_hand.cpp | 2 +- ..._constants_no_user_defined_with_config.cpp | 2 +- 6 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 docs/source/user_guide/multiple_grid_cells.rst create mode 100644 test/tutorial/test_multiple_grid_cells.cpp diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index fad6b2bdc..30071eab6 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -20,3 +20,4 @@ If you happen to find our examples are lacking for your needs, please, rate_constant_tutorial user_defined_rate_constant_tutorial + multiple_grid_cells diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst new file mode 100644 index 000000000..0afaead0c --- /dev/null +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -0,0 +1,23 @@ +.. _Multiple grid cells: + +Multiple Grid Cells +=================== + +This tutorial will focus on running multiple grid cells. Because the +:ref:`Rate constants` and :ref:`User defined rate constants` showed both configuring +the mechanism by hand and building with a configuration file, we will only show building +up the mechanism by hand for this tutorial. That mechanism will be a simple chapman mechanism. + + +.. math:: + + O_{1}^{d} + N_2 &\longrightarrow O + N_2, &k_{1, \mathrm{arrhenius}} \\ + O_{1}^{d} + O_2 &\longrightarrow O + O_2, &k_{2, \mathrm{arrhenius}} \\ + O + O_3 &\longrightarrow 2O_2, &k_{3, \mathrm{arrhenius}} \\ + O + O_2 + M &\longrightarrow O_3 + M, &k_{4, \mathrm{arrhenius}} \\ + O_2 &\longrightarrow 2O, &k_{5, \mathrm{photolysis}} \\ + O_3 &\longrightarrow O_1^d +O_2,\qquad &k_{6, \mathrm{photolysis}} \\ + O_3 &\longrightarrow O +O_2, &k_{7, \mathrm{photolysis}} \\ + +We will use three grid cells. The second grid cells will have concentrations twice as large as the first grid cell. +The third grid cell will have concentrations half as large as the first grid cell. \ No newline at end of file diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index 430a4e8c5..a1c6203ff 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -8,6 +8,7 @@ include(test_util) create_standard_test(NAME README_example SOURCES test_README_example.cpp) create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) +create_standard_test(NAME multiple_grid_cells SOURCES test_multiple_grid_cells.cpp) if(ENABLE_JSON) create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) endif() diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp new file mode 100644 index 000000000..282d1778a --- /dev/null +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -0,0 +1,205 @@ +#include +#include +#include +#include + +// Each rate constant is in its own header file +#include +#include +#include +#include +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(5) << "grid" + << "," << std::setw(10) << "O" + << "," << std::setw(10) << "O1D" + << "," << std::setw(10) << "O2" + << "," << std::setw(10) << "O3" + << "," << std::setw(10) << "M" + << "," << std::setw(10) << "Ar" + << "," << std::setw(10) << "N2" + << "," << std::setw(10) << "H2O" + << "," << std::setw(10) << "CO2" + << std::endl; +} + +template class T> +void print_state(double time, State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) + << std::setw(6) << "1," + << std::setw(10) << state.variables_[0][state.variable_map_["O"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["O1D"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["O2"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["O3"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["M"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["Ar"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["N2"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["H2O"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["CO2"]] + << std::endl; + + std::cout.copyfmt(oldState); + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) + << std::setw(6) << "2," + << std::setw(10) << state.variables_[1][state.variable_map_["O"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["O1D"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["O2"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["O3"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["M"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["Ar"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["N2"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["H2O"]] << "," + << std::setw(10) << state.variables_[1][state.variable_map_["CO2"]] + << std::endl; + + std::cout.copyfmt(oldState); + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) + << std::setw(6) << "3," + << std::setw(10) << state.variables_[2][state.variable_map_["O"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["O1D"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["O2"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["O3"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["M"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["Ar"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["N2"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["H2O"]] << "," + << std::setw(10) << state.variables_[2][state.variable_map_["CO2"]] + << std::endl; + + std::cout.copyfmt(oldState); +} + +int main(){ + auto o = Species("O"); + auto o1d = Species("O1D"); + auto o2 = Species("O2"); + auto o3 = Species("O3"); + auto m = Species("M"); + auto ar = Species("Ar"); + auto n2 = Species("N2"); + auto h2o = Species("H2O"); + auto co2 = Species("CO2"); + + Phase gas_phase{ std::vector{ o, o1d, o2, o3, m, ar, n2, h2o, co2 } }; + + Process r1 = Process::create() + .reactants({ o1d, n2 }) + .products({ yields(o, 1), yields(n2, 1) }) + .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-11, .B_ = 0, .C_ = 110 })) + .phase(gas_phase); + + Process r2 = Process::create() + .reactants({ o1d, o2 }) + .products({ yields(o, 1), yields(o2, 1) }) + .rate_constant(ArrheniusRateConstant( + ArrheniusRateConstantParameters{ .A_ = 3.3e-11, .B_ = 0, .C_ = 55 })) + .phase(gas_phase); + + Process r3 = Process::create() + .reactants({ o, o3 }) + .products({ yields(o2, 2) }) + .rate_constant(ArrheniusRateConstant( + ArrheniusRateConstantParameters{ .A_ = 8e-12, .B_ = 0, .C_ = -2060 })) + .phase(gas_phase); + + Process r4 = Process::create() + .reactants({ o, o2, m }) + .products({ yields(o3, 1), yields(m, 1) }) + .rate_constant(ArrheniusRateConstant( + ArrheniusRateConstantParameters{ .A_ = 6.0e-34, .B_ = 0, .C_ = 2.4 })) + .phase(gas_phase); + + Process photo_1 = Process::create() + .reactants({ o2 }) + .products({ yields(o, 2) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "jO2" })) + .phase(gas_phase); + + Process photo_2 = Process::create() + .reactants({ o3 }) + .products({ yields(o1d, 1), yields(o2, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "jO3a" })) + .phase(gas_phase); + + Process photo_3 = Process::create() + .reactants({ o3 }) + .products({ yields(o, 1), yields(o2, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "jO3b" })) + .phase(gas_phase); + + RosenbrockSolver solver{ + System(SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, photo_1, photo_2, photo_3 }, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) + }; + + State state = solver.GetState(); + + std::vector concentrations{ 1, 1, 1, 2, 2, 2, 3, 3, 3 }; + state.variables_[0] = concentrations; + + std::transform(concentrations.begin(), concentrations.end(), concentrations.begin(), [](auto& c){return c*2;}); + state.variables_[1] = concentrations; + + std::transform(concentrations.begin(), concentrations.end(), concentrations.begin(), [](auto& c){return c/4;}); + state.variables_[2] = concentrations; + + state.SetCustomRateParameter("jO2", {0.01, 0.01, 0.01}); + state.SetCustomRateParameter("jO3a", {0.02, 0.02, 0.02}); + state.SetCustomRateParameter("jO3b", {0.03, 0.03, 0.03}); + + state.conditions_[0].temperature_ = 284.19; // [K] + state.conditions_[0].pressure_ = 101245.0; // [Pa] + state.conditions_[1].temperature_ = 215.02; // [K] + state.conditions_[1].pressure_ = 100789.2; // [Pa] + state.conditions_[2].temperature_ = 299.31; // [K] + state.conditions_[2].pressure_ = 101398.0; // [Pa] + + // choose a timestep and print the initial state + double time_step = 500; // s + + print_header(); + print_state(0, state); + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_[0] = result.result_.AsVector(); + } + + print_state(time_step * (i + 1), state); + } +} \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index b53f2851a..eb77d5cef 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -154,7 +154,7 @@ int main(const int argc, const char* argv[]) state.SetCustomRateParameter("C.effective radius [m]", 1e-7); state.SetCustomRateParameter("C.particle number concentration [# m-3]", 2.5e6); - // choose and timestep a print the initial state + // choose a timestep and print the initial state double time_step = 500; // s print_header(); diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index a43b9a69b..f11fab469 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -92,7 +92,7 @@ int main(const int argc, const char* argv[]) state.SetCustomRateParameter("SURF.C surface.effective radius [m]", 1e-7); state.SetCustomRateParameter("SURF.C surface.particle number concentration [# m-3]", 2.5e6); - // choose a timestep a print the initial state + // choose a timestep and print the initial state double time_step = 500; // s print_header(); From 40dd6a92e869241059db39232aca2a2e942d48cd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 12:53:07 -0700 Subject: [PATCH 038/318] Auto-format code changes (#262) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/configure/solver_config.hpp | 3 +- .../micm/process/surface_rate_constant.hpp | 2 +- include/micm/solver/rosenbrock.hpp | 30 +++++++++---------- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 920e64c8e..ea004b40e 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -144,7 +144,8 @@ namespace micm { camp_files.push_back(element.get()); } - if (camp_files.size() != 2) { + if (camp_files.size() != 2) + { std::string err_msg = "CAMP file list should contain two files [species.json, mechanism.json]"; std::cerr << err_msg << std::endl; return ConfigParseStatus::InvalidCAMPFileCount; diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index 69ae4bb24..1781f51f3 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -78,7 +78,7 @@ namespace micm const double radius = *(custom_parameters++); const double number = *(custom_parameters); double val = (double)4.0 * number * M_PI * radius * radius / - (radius / diffusion_coefficient_ + 4.0 / (mean_free_speed * parameters_.reaction_probability_)); + (radius / diffusion_coefficient_ + 4.0 / (mean_free_speed * parameters_.reaction_probability_)); return val; } diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index fc57d2f5e..f7bef36be 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -86,37 +86,37 @@ namespace micm void print() const; /// @brief an L-stable method, 2 stages, order 2 - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters two_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief an L-stable method, 3 stages, order 3, 2 function evaluations - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters three_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief L-stable rosenbrock method of order 4, with 4 stages - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters four_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief A stiffly-stable method, 4 stages, order 3 - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters four_stage_differential_algebraic_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief stiffly-stable rosenbrock method of order 4, with 6 stages - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters six_stage_differential_algebraic_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); From 363ec5e3b4b38850bad6ee65d9db508faf62fe6d Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 27 Sep 2023 15:08:44 -0500 Subject: [PATCH 039/318] adding better conditions and rates, setting concentrations for all grid cells --- .../source/user_guide/multiple_grid_cells.rst | 77 ++++++- test/tutorial/test_multiple_grid_cells.cpp | 212 +++++++++--------- 2 files changed, 186 insertions(+), 103 deletions(-) diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index 0afaead0c..1a559ee96 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -8,7 +8,6 @@ This tutorial will focus on running multiple grid cells. Because the the mechanism by hand and building with a configuration file, we will only show building up the mechanism by hand for this tutorial. That mechanism will be a simple chapman mechanism. - .. math:: O_{1}^{d} + N_2 &\longrightarrow O + N_2, &k_{1, \mathrm{arrhenius}} \\ @@ -20,4 +19,78 @@ up the mechanism by hand for this tutorial. That mechanism will be a simple chap O_3 &\longrightarrow O +O_2, &k_{7, \mathrm{photolysis}} \\ We will use three grid cells. The second grid cells will have concentrations twice as large as the first grid cell. -The third grid cell will have concentrations half as large as the first grid cell. \ No newline at end of file +The third grid cell will have concentrations half as large as the first grid cell. Initial conditions are things +that might be found in the atmosphere and the photolysis values are typical photolysis rates at noon. + + +And these are the results. + ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| time | grid | O | O1D | O2 | O3 | M | Ar | N2 | H2O | CO2 | ++=======+======+=============+=============+=============+=============+=============+=============+=============+=============+=============+ +| 0 | 1 | 0.00e+00 | 0.00e+00 | 7.50e-01 | 8.10e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 0 | 2 | 0.00e+00 | 0.00e+00 | 1.50e+00 | 1.62e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 0 | 3 | 0.00e+00 | 0.00e+00 | 3.75e-01 | 4.05e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 200 | 1 | 7.62e-07 | 3.19e-08 | 7.50e-01 | 7.31e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 200 | 2 | 1.52e-06 | 6.37e-08 | 1.50e+00 | 1.46e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 200 | 3 | 3.81e-07 | 1.59e-08 | 3.75e-01 | 3.65e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 400 | 1 | 1.45e-06 | 6.06e-08 | 7.50e-01 | 6.59e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 400 | 2 | 2.90e-06 | 1.21e-07 | 1.50e+00 | 1.32e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 400 | 3 | 7.25e-07 | 3.03e-08 | 3.75e-01 | 3.30e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 600 | 1 | 2.07e-06 | 8.65e-08 | 7.50e-01 | 5.94e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 600 | 2 | 4.14e-06 | 1.73e-07 | 1.50e+00 | 1.19e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 600 | 3 | 1.03e-06 | 4.33e-08 | 3.75e-01 | 2.97e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 800 | 1 | 2.63e-06 | 1.10e-07 | 7.50e-01 | 5.36e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 800 | 2 | 5.26e-06 | 2.20e-07 | 1.50e+00 | 1.07e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 800 | 3 | 1.31e-06 | 5.50e-08 | 3.75e-01 | 2.68e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1000 | 1 | 3.13e-06 | 1.31e-07 | 7.50e-01 | 4.84e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1000 | 2 | 6.27e-06 | 2.62e-07 | 1.50e+00 | 9.67e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1000 | 3 | 1.57e-06 | 6.55e-08 | 3.75e-01 | 2.42e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1200 | 1 | 3.59e-06 | 1.50e-07 | 7.50e-01 | 4.36e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1200 | 2 | 7.18e-06 | 3.00e-07 | 1.50e+00 | 8.72e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1200 | 3 | 1.79e-06 | 7.50e-08 | 3.75e-01 | 2.18e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1400 | 1 | 4.00e-06 | 1.67e-07 | 7.50e-01 | 3.93e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1400 | 2 | 8.00e-06 | 3.34e-07 | 1.50e+00 | 7.87e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1400 | 3 | 2.00e-06 | 8.36e-08 | 3.75e-01 | 1.97e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1600 | 1 | 4.37e-06 | 1.83e-07 | 7.50e-01 | 3.55e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1600 | 2 | 8.74e-06 | 3.65e-07 | 1.50e+00 | 7.10e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1600 | 3 | 2.18e-06 | 9.13e-08 | 3.75e-01 | 1.77e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1800 | 1 | 4.70e-06 | 1.97e-07 | 7.50e-01 | 3.20e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1800 | 2 | 9.40e-06 | 3.93e-07 | 1.50e+00 | 6.40e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 1800 | 3 | 2.35e-06 | 9.83e-08 | 3.75e-01 | 1.60e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2000 | 1 | 5.00e-06 | 2.09e-07 | 7.50e-01 | 2.89e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2000 | 2 | 1.00e-05 | 4.18e-07 | 1.50e+00 | 5.78e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +| 2000 | 3 | 2.50e-06 | 1.05e-07 | 3.75e-01 | 1.44e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | ++-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index 282d1778a..361f7db11 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -1,7 +1,7 @@ +#include #include #include #include -#include // Each rate constant is in its own header file #include @@ -32,10 +32,9 @@ void print_header() << "," << std::setw(10) << "O3" << "," << std::setw(10) << "M" << "," << std::setw(10) << "Ar" - << "," << std::setw(10) << "N2" - << "," << std::setw(10) << "H2O" - << "," << std::setw(10) << "CO2" - << std::endl; + << "," << std::setw(10) << "N2" + << "," << std::setw(10) << "H2O" + << "," << std::setw(10) << "CO2" << std::endl; } template class T> @@ -44,132 +43,143 @@ void print_state(double time, State& state) std::ios oldState(nullptr); oldState.copyfmt(std::cout); - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) - << std::setw(6) << "1," - << std::setw(10) << state.variables_[0][state.variable_map_["O"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["O1D"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["O2"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["O3"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["M"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["Ar"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["N2"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["H2O"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["CO2"]] - << std::endl; + std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "1," << std::setw(10) + << state.variables_[0][state.variable_map_["O"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["O1D"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["O2"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["O3"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["M"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["Ar"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["N2"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["H2O"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["CO2"]] << std::endl; std::cout.copyfmt(oldState); std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) - << std::setw(6) << "2," - << std::setw(10) << state.variables_[1][state.variable_map_["O"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["O1D"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["O2"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["O3"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["M"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["Ar"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["N2"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["H2O"]] << "," - << std::setw(10) << state.variables_[1][state.variable_map_["CO2"]] - << std::endl; + std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "2," << std::setw(10) + << state.variables_[1][state.variable_map_["O"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["O1D"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["O2"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["O3"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["M"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["Ar"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["N2"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["H2O"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["CO2"]] << std::endl; std::cout.copyfmt(oldState); std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) - << std::setw(6) << "3," - << std::setw(10) << state.variables_[2][state.variable_map_["O"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["O1D"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["O2"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["O3"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["M"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["Ar"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["N2"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["H2O"]] << "," - << std::setw(10) << state.variables_[2][state.variable_map_["CO2"]] - << std::endl; + std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "3," << std::setw(10) + << state.variables_[2][state.variable_map_["O"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["O1D"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["O2"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["O3"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["M"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["Ar"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["N2"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["H2O"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["CO2"]] << std::endl; std::cout.copyfmt(oldState); } -int main(){ - auto o = Species("O"); +int main() +{ + auto o = Species("O"); auto o1d = Species("O1D"); - auto o2 = Species("O2"); - auto o3 = Species("O3"); - auto m = Species("M"); - auto ar = Species("Ar"); - auto n2 = Species("N2"); + auto o2 = Species("O2"); + auto o3 = Species("O3"); + auto m = Species("M"); + auto ar = Species("Ar"); + auto n2 = Species("N2"); auto h2o = Species("H2O"); auto co2 = Species("CO2"); Phase gas_phase{ std::vector{ o, o1d, o2, o3, m, ar, n2, h2o, co2 } }; Process r1 = Process::create() - .reactants({ o1d, n2 }) - .products({ yields(o, 1), yields(n2, 1) }) - .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-11, .B_ = 0, .C_ = 110 })) - .phase(gas_phase); + .reactants({ o1d, n2 }) + .products({ yields(o, 1), yields(n2, 1) }) + .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-11, .B_ = 0, .C_ = 110 })) + .phase(gas_phase); Process r2 = Process::create() - .reactants({ o1d, o2 }) - .products({ yields(o, 1), yields(o2, 1) }) - .rate_constant(ArrheniusRateConstant( - ArrheniusRateConstantParameters{ .A_ = 3.3e-11, .B_ = 0, .C_ = 55 })) - .phase(gas_phase); - - Process r3 = Process::create() - .reactants({ o, o3 }) - .products({ yields(o2, 2) }) - .rate_constant(ArrheniusRateConstant( - ArrheniusRateConstantParameters{ .A_ = 8e-12, .B_ = 0, .C_ = -2060 })) - .phase(gas_phase); - - Process r4 = Process::create() - .reactants({ o, o2, m }) - .products({ yields(o3, 1), yields(m, 1) }) - .rate_constant(ArrheniusRateConstant( - ArrheniusRateConstantParameters{ .A_ = 6.0e-34, .B_ = 0, .C_ = 2.4 })) - .phase(gas_phase); + .reactants({ o1d, o2 }) + .products({ yields(o, 1), yields(o2, 1) }) + .rate_constant(ArrheniusRateConstant(ArrheniusRateConstantParameters{ .A_ = 3.3e-11, .B_ = 0, .C_ = 55 })) + .phase(gas_phase); + + Process r3 = + Process::create() + .reactants({ o, o3 }) + .products({ yields(o2, 2) }) + .rate_constant(ArrheniusRateConstant(ArrheniusRateConstantParameters{ .A_ = 8e-12, .B_ = 0, .C_ = -2060 })) + .phase(gas_phase); + + Process r4 = + Process::create() + .reactants({ o, o2, m }) + .products({ yields(o3, 1), yields(m, 1) }) + .rate_constant(ArrheniusRateConstant(ArrheniusRateConstantParameters{ .A_ = 6.0e-34, .B_ = 0, .C_ = 2.4 })) + .phase(gas_phase); Process photo_1 = Process::create() - .reactants({ o2 }) - .products({ yields(o, 2) }) - .rate_constant(UserDefinedRateConstant({ .label_ = "jO2" })) - .phase(gas_phase); + .reactants({ o2 }) + .products({ yields(o, 2) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "jO2" })) + .phase(gas_phase); Process photo_2 = Process::create() - .reactants({ o3 }) - .products({ yields(o1d, 1), yields(o2, 1) }) - .rate_constant(UserDefinedRateConstant({ .label_ = "jO3a" })) - .phase(gas_phase); + .reactants({ o3 }) + .products({ yields(o1d, 1), yields(o2, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "jO3a" })) + .phase(gas_phase); Process photo_3 = Process::create() - .reactants({ o3 }) - .products({ yields(o, 1), yields(o2, 1) }) - .rate_constant(UserDefinedRateConstant({ .label_ = "jO3b" })) - .phase(gas_phase); + .reactants({ o3 }) + .products({ yields(o, 1), yields(o2, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "jO3b" })) + .phase(gas_phase); - RosenbrockSolver solver{ - System(SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, photo_1, photo_2, photo_3 }, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) - }; + auto params = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false); + params.relative_tolerance_ = 1e-3; + params.absolute_tolerance_ = params.relative_tolerance_ * 1e-4; + + RosenbrockSolver solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, photo_1, photo_2, photo_3 }, + params }; State state = solver.GetState(); std::vector concentrations{ 1, 1, 1, 2, 2, 2, 3, 3, 3 }; - state.variables_[0] = concentrations; - - std::transform(concentrations.begin(), concentrations.end(), concentrations.begin(), [](auto& c){return c*2;}); - state.variables_[1] = concentrations; - - std::transform(concentrations.begin(), concentrations.end(), concentrations.begin(), [](auto& c){return c/4;}); - state.variables_[2] = concentrations; - state.SetCustomRateParameter("jO2", {0.01, 0.01, 0.01}); - state.SetCustomRateParameter("jO3a", {0.02, 0.02, 0.02}); - state.SetCustomRateParameter("jO3b", {0.03, 0.03, 0.03}); + // mol m-3 + double ar_concentration = 0.0334; + double co2_concentration = 0.00146; + double h2o_concentration = 1.19E-05; + double o2_concentration = 0.75; + double o3_concentration = 8.1E-06; + + // Set concentrations for each element with the same scaling factor + state.SetConcentration(ar, std::vector{ ar_concentration, ar_concentration * 2, ar_concentration * 0.5 }); + state.SetConcentration(co2, std::vector{ co2_concentration, co2_concentration * 2, co2_concentration * 0.5 }); + state.SetConcentration(h2o, std::vector{ h2o_concentration, h2o_concentration * 2, h2o_concentration * 0.5 }); + state.SetConcentration(o2, std::vector{ o2_concentration, o2_concentration * 2, o2_concentration * 0.5 }); + state.SetConcentration(o3, std::vector{ o3_concentration, o3_concentration * 2, o3_concentration * 0.5 }); + + state.SetConcentration(o1d, std::vector(3, 0)); + state.SetConcentration(o, std::vector(3, 0)); + state.SetConcentration(n2, std::vector(3, 1e-10)); + + state.SetConcentration(m, std::vector(3, 2.7e25)); + + double jo2 = 1.2e-14; + double jo3a = 2.07e-5; + double jo3b = 4.95e-4; + state.SetCustomRateParameter("jO2", std::vector(3, jo2)); + state.SetCustomRateParameter("jO3a", std::vector(3, jo3a)); + state.SetCustomRateParameter("jO3b", std::vector(3, jo3b)); state.conditions_[0].temperature_ = 284.19; // [K] state.conditions_[0].pressure_ = 101245.0; // [Pa] @@ -179,7 +189,7 @@ int main(){ state.conditions_[2].pressure_ = 101398.0; // [Pa] // choose a timestep and print the initial state - double time_step = 500; // s + double time_step = 200; // s print_header(); print_state(0, state); @@ -197,7 +207,7 @@ int main(){ { auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; - state.variables_[0] = result.result_.AsVector(); + state.variables_ = result.result_; } print_state(time_step * (i + 1), state); From 76054477e638dfdb65592454203873811bb8dcc7 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 27 Sep 2023 15:10:43 -0500 Subject: [PATCH 040/318] removing dumb way of setting state --- test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp | 2 +- .../test_rate_constants_no_user_defined_with_config.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index eb77d5cef..8aedec01c 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -174,7 +174,7 @@ int main(const int argc, const char* argv[]) auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; // std::cout << "solver state: " << StateToString(result.state_) << std::endl; - state.variables_[0] = result.result_.AsVector(); + state.variables_ = result.result_; } print_state(time_step * (i + 1), state); diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index f11fab469..889603dd2 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -112,7 +112,7 @@ int main(const int argc, const char* argv[]) auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; // std::cout << "solver state: " << StateToString(result.state_) << std::endl; - state.variables_[0] = result.result_.AsVector(); + state.variables_ = result.result_; } print_state(time_step * (i + 1), state); From d568e84624974c9ee981e006e8d18c5e28ce324d Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 27 Sep 2023 15:35:28 -0500 Subject: [PATCH 041/318] finished the tutorial --- .../source/user_guide/multiple_grid_cells.rst | 81 +++++++++++++++++++ .../user_guide/rate_constant_tutorial.rst | 7 +- test/tutorial/test_multiple_grid_cells.cpp | 14 +--- 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index 1a559ee96..c15912f9d 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -22,6 +22,87 @@ We will use three grid cells. The second grid cells will have concentrations twi The third grid cell will have concentrations half as large as the first grid cell. Initial conditions are things that might be found in the atmosphere and the photolysis values are typical photolysis rates at noon. +If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +This mechanism only needs the arrhenius rate constant, the user defined rate constant (for photolysis rates) +and the rosenbrock solver. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 1-9 + +After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the +rosenbrock solver. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 11-18 + +To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) +and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these as +well as a :cpp:class:`micm::Phase`. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 84-94 + + +With the species and gas phase, we can define all of our reactions + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 96-138 + + +Now we can define our RosenbrockSolver. This time we'll form the reactions and chemical system in place. +Also, notice the ``false`` in our :cpp:class:`micm::RosenbrockSolverParameters`. This tells the solver +not to reorder the state variables. The reordering is an optimization that can minizie fill-in for some +of the linear algebra operations. For this example, it's turned off so that the order of the state matches +the order the species are added to the gas phase. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 140-143 + + +Now we need to get a state and set the concentations of each of the species. In the +":ref:`Rate constants set concentations`" section of the rate constants tutorial, +we used a ``std::unordered_map>`` +to set the concentrations. Here we will set the concentations by providing the :cpp:class:`micm::Species` objects. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 145-165 + +Then we set the photolysis rates by creating a vector that is 3 elements long, one for each grid cell. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 167-172 + +And lastly set the temperature and pressure for each grid cell. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 174-179 + +Now we are ready to run the simulation. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 181-204 + And these are the results. diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index a4ae3cf0d..92b2e8874 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -126,6 +126,11 @@ can be found in the :cpp:class:`micm::RosenbrockSolverParameters` The rosenbrock solver will provide us a state, which we can use to set the concentrations, custom rate parameters, and temperature and pressure +.. _Rate constants set concentations: + +Initializing the state +^^^^^^^^^^^^^^^^^^^^^^ + .. tabs:: .. tab:: Build the Mechanism with the API @@ -141,7 +146,7 @@ custom rate parameters, and temperature and pressure :lines: 75-93 -Finally, we are ready to pick a timestep ans solve the system. +Finally, we are ready to pick a timestep and solve the system. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index 361f7db11..86097f706 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -5,11 +5,6 @@ // Each rate constant is in its own header file #include -#include -#include -#include -#include -#include #include #include @@ -142,18 +137,13 @@ int main() .rate_constant(UserDefinedRateConstant({ .label_ = "jO3b" })) .phase(gas_phase); - auto params = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false); - params.relative_tolerance_ = 1e-3; - params.absolute_tolerance_ = params.relative_tolerance_ * 1e-4; - RosenbrockSolver solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, photo_1, photo_2, photo_3 }, - params }; + RosenbrockSolverParameters::three_stage_rosenbrock_parameters( + 3, false) }; State state = solver.GetState(); - std::vector concentrations{ 1, 1, 1, 2, 2, 2, 3, 3, 3 }; - // mol m-3 double ar_concentration = 0.0334; double co2_concentration = 0.00146; From fd3b24527066090a53ca19721ca828a770fb1ae5 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 27 Sep 2023 15:44:21 -0500 Subject: [PATCH 042/318] removing useless list --- docs/source/user_guide/index.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 30071eab6..e579327ff 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -10,10 +10,6 @@ If you happen to find our examples are lacking for your needs, please, -1. :ref:`Rate constants` -2. :ref:`User defined rate constants` - - .. toctree:: :maxdepth: 1 :caption: Contents: From 9a5e0c107d431a0e90fbb8f7d3ee72e44f730713 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 27 Sep 2023 14:11:35 -0700 Subject: [PATCH 043/318] add linear solver policy to RosenbrockSolver --- include/micm/solver/rosenbrock.hpp | 6 ++- include/micm/solver/rosenbrock.inl | 54 +++++++++---------- test/integration/e5.hpp | 8 +-- test/integration/hires.hpp | 6 +-- test/integration/oregonator.hpp | 8 +-- .../regression_test_dforce_dy.cpp | 14 ++--- .../regression_test_p_force.cpp | 28 +++++----- .../regression_test_solve.cpp | 22 ++++---- test/regression/RosenbrockChapman/util.hpp | 30 +++++------ test/unit/solver/test_rosenbrock.cpp | 26 ++++----- 10 files changed, 102 insertions(+), 100 deletions(-) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index fc57d2f5e..1a40b9ae0 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -142,7 +142,9 @@ namespace micm /// @brief An implementation of the Rosenbrock ODE solver /// /// The template parameter is the type of matrix to use - template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = SparseMatrix> + template class MatrixPolicy = Matrix, + template class SparseMatrixPolicy = SparseMatrix, + class LinearSolverPolicy = LinearSolver> class RosenbrockSolver { public: @@ -180,7 +182,7 @@ namespace micm ProcessSet process_set_; SolverStats stats_; SparseMatrixPolicy jacobian_; - LinearSolver linear_solver_; + LinearSolverPolicy linear_solver_; std::vector jacobian_diagonal_elements_; size_t N_{}; diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 57d347cb5..f8621d526 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -366,8 +366,8 @@ namespace micm // // RosenbrockSolver // - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::SolverStats::Reset() + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::SolverStats::Reset() { function_calls = 0; jacobian_updates = 0; @@ -396,8 +396,8 @@ namespace micm return ""; } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline RosenbrockSolver::RosenbrockSolver() + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline RosenbrockSolver::RosenbrockSolver() : system_(), processes_(), parameters_(RosenbrockSolverParameters::three_stage_rosenbrock_parameters()), @@ -411,8 +411,8 @@ namespace micm { } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline RosenbrockSolver::RosenbrockSolver( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline RosenbrockSolver::RosenbrockSolver( const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters) @@ -451,14 +451,14 @@ namespace micm builder = builder.with_element(i, i); jacobian_ = builder; - linear_solver_ = LinearSolver(jacobian_, 1.0e-30); + linear_solver_ = LinearSolverPolicy(jacobian_, 1.0e-30); process_set_.SetJacobianFlatIds(jacobian_); for (std::size_t i = 0; i < jacobian_[0].size(); ++i) jacobian_diagonal_elements_.push_back(jacobian_.VectorIndex(0, i, i)); } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline State RosenbrockSolver::GetState() const + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline State RosenbrockSolver::GetState() const { std::vector param_labels{}; for (const auto& process : processes_) @@ -471,11 +471,11 @@ namespace micm .number_of_rate_constants_ = processes_.size() } }; } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline typename RosenbrockSolver::SolverResult - RosenbrockSolver::Solve(double time_step, State& state) noexcept + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline typename RosenbrockSolver::SolverResult + RosenbrockSolver::Solve(double time_step, State& state) noexcept { - typename RosenbrockSolver::SolverResult result{}; + typename RosenbrockSolver::SolverResult result{}; result.state_ = SolverState::Running; MatrixPolicy Y(state.variables_); MatrixPolicy Ynew(Y.size(), Y[0].size(), 0.0); @@ -646,8 +646,8 @@ namespace micm return result; } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::CalculateForcing( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::CalculateForcing( const MatrixPolicy& rate_constants, const MatrixPolicy& number_densities, MatrixPolicy& forcing) @@ -657,8 +657,8 @@ namespace micm stats_.function_calls += 1; } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::AlphaMinusJacobian( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::AlphaMinusJacobian( SparseMatrixPolicy& jacobian, const double& alpha) const requires(!VectorizableSparse>) @@ -673,8 +673,8 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::AlphaMinusJacobian( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::AlphaMinusJacobian( SparseMatrixPolicy& jacobian, const double& alpha) const requires(VectorizableSparse>) @@ -691,8 +691,8 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::CalculateJacobian( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::CalculateJacobian( const MatrixPolicy& rate_constants, const MatrixPolicy& number_densities, SparseMatrixPolicy& jacobian) @@ -702,14 +702,14 @@ namespace micm stats_.jacobian_updates += 1; } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::UpdateState(State& state) + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::UpdateState(State& state) { Process::UpdateState(processes_, state); } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline void RosenbrockSolver::LinearFactor( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline void RosenbrockSolver::LinearFactor( double& H, const double gamma, bool& singular, @@ -736,8 +736,8 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy> - inline double RosenbrockSolver::NormalizedError( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline double RosenbrockSolver::NormalizedError( const MatrixPolicy& Y, const MatrixPolicy& Ynew, const MatrixPolicy& errors) diff --git a/test/integration/e5.hpp b/test/integration/e5.hpp index abb6a6524..5b921cf8b 100644 --- a/test/integration/e5.hpp +++ b/test/integration/e5.hpp @@ -2,8 +2,8 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix> -class E5 : public micm::RosenbrockSolver +template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +class E5 : public micm::RosenbrockSolver { public: /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters @@ -12,7 +12,7 @@ class E5 : public micm::RosenbrockSolver E5(const micm::System& system, const std::vector& processes, const micm::RosenbrockSolverParameters& parameters) - : micm::RosenbrockSolver() + : micm::RosenbrockSolver() { this->system_ = system; this->processes_ = processes; @@ -29,7 +29,7 @@ class E5 : public micm::RosenbrockSolver this->jacobian_ = builder; for (std::size_t i = 0; i < this->jacobian_[0].size(); ++i) this->jacobian_diagonal_elements_.push_back(this->jacobian_.VectorIndex(0, i, i)); - this->linear_solver_ = micm::LinearSolver(this->jacobian_, 1.0e-30); + this->linear_solver_ = LinearSolverPolicy(this->jacobian_, 1.0e-30); } ~E5() diff --git a/test/integration/hires.hpp b/test/integration/hires.hpp index ef3ccc296..3ad2d47dd 100644 --- a/test/integration/hires.hpp +++ b/test/integration/hires.hpp @@ -2,8 +2,8 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix> -class HIRES : public micm::RosenbrockSolver +template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +class HIRES : public micm::RosenbrockSolver { public: /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters @@ -30,7 +30,7 @@ class HIRES : public micm::RosenbrockSolver this->jacobian_ = builder; for (std::size_t i = 0; i < this->jacobian_[0].size(); ++i) this->jacobian_diagonal_elements_.push_back(this->jacobian_.VectorIndex(0, i, i)); - this->linear_solver_ = micm::LinearSolver(this->jacobian_, 1.0e-30); + this->linear_solver_ = LinearSolverPolicy(this->jacobian_, 1.0e-30); } ~HIRES() diff --git a/test/integration/oregonator.hpp b/test/integration/oregonator.hpp index 9a4d8e072..cd2f0bae2 100644 --- a/test/integration/oregonator.hpp +++ b/test/integration/oregonator.hpp @@ -2,8 +2,8 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix> -class Oregonator : public micm::RosenbrockSolver +template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +class Oregonator : public micm::RosenbrockSolver { public: /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters @@ -13,7 +13,7 @@ class Oregonator : public micm::RosenbrockSolver& processes, const micm::RosenbrockSolverParameters& parameters) - : micm::RosenbrockSolver() + : micm::RosenbrockSolver() { this->system_ = system; this->processes_ = processes; @@ -30,7 +30,7 @@ class Oregonator : public micm::RosenbrockSolverjacobian_ = builder; for (std::size_t i = 0; i < this->jacobian_[0].size(); ++i) this->jacobian_diagonal_elements_.push_back(this->jacobian_.VectorIndex(0, i, i)); - this->linear_solver_ = micm::LinearSolver(this->jacobian_, 1.0e-30); + this->linear_solver_ = LinearSolverPolicy(this->jacobian_, 1.0e-30); } ~Oregonator() diff --git a/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp b/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp index d87161908..49335c002 100644 --- a/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp +++ b/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp @@ -10,7 +10,7 @@ #include "chapman_ode_solver.hpp" #include "util.hpp" -template class MatrixPolicy, template class SparseMatrixPolicy> +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> void testJacobian() { std::random_device rnd_device; @@ -18,7 +18,7 @@ void testJacobian() std::lognormal_distribution dist{ -2.0, 4.0 }; micm::ChapmanODESolver fixed_solver{}; - auto solver = getThreeStageMultiCellChapmanSolver(3); + auto solver = getThreeStageMultiCellChapmanSolver(3); auto state = solver.GetState(); @@ -75,13 +75,13 @@ using Group4SparseVectorMatrix = micm::SparseMatrix(); + testJacobian>(); } TEST(RegressionRosenbrock, VectorJacobian) { - testJacobian(); - testJacobian(); - testJacobian(); - testJacobian(); + testJacobian>(); + testJacobian>(); + testJacobian>(); + testJacobian>(); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_p_force.cpp b/test/regression/RosenbrockChapman/regression_test_p_force.cpp index e19e724ee..08a0122ce 100644 --- a/test/regression/RosenbrockChapman/regression_test_p_force.cpp +++ b/test/regression/RosenbrockChapman/regression_test_p_force.cpp @@ -10,11 +10,11 @@ #include "chapman_ode_solver.hpp" #include "util.hpp" -template class MatrixPolicy, template class SparseMatrixPolicy> +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> void testRateConstants() { micm::ChapmanODESolver fixed_solver{}; - auto solver = getThreeStageMultiCellChapmanSolver(3); + auto solver = getThreeStageMultiCellChapmanSolver(3); auto state = solver.GetState(); auto fixed_state = fixed_solver.GetState(); @@ -46,7 +46,7 @@ void testRateConstants() } } -template class MatrixPolicy, template class SparseMatrixPolicy> +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> void testForcing() { std::random_device rnd_device; @@ -54,7 +54,7 @@ void testForcing() std::lognormal_distribution dist(-2.0, 2.0); micm::ChapmanODESolver fixed_solver{}; - auto solver = getThreeStageMultiCellChapmanSolver(3); + auto solver = getThreeStageMultiCellChapmanSolver(3); auto state = solver.GetState(); @@ -110,26 +110,26 @@ using Group4SparseVectorMatrix = micm::SparseMatrix(); + testRateConstants>(); } TEST(RegressionRosenbrock, VectorRateConstants) { - testRateConstants(); - testRateConstants(); - testRateConstants(); - testRateConstants(); + testRateConstants>(); + testRateConstants>(); + testRateConstants>(); + testRateConstants>(); } TEST(RegressionRosenbrock, Forcing) { - testForcing(); + testForcing>(); } TEST(RegressionRosenbrock, VectorForcing) { - testForcing(); - testForcing(); - testForcing(); - testForcing(); + testForcing>(); + testForcing>(); + testForcing>(); + testForcing>(); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_solve.cpp b/test/regression/RosenbrockChapman/regression_test_solve.cpp index bed2a90f6..a98c6d1da 100644 --- a/test/regression/RosenbrockChapman/regression_test_solve.cpp +++ b/test/regression/RosenbrockChapman/regression_test_solve.cpp @@ -10,8 +10,8 @@ #include "chapman_ode_solver.hpp" #include "util.hpp" -template class MatrixPolicy, template class SparseMatrixPolicy> -void testSolve(micm::RosenbrockSolver solver, double relative_tolerance = 1.0e-8) +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +void testSolve(micm::RosenbrockSolver solver, double relative_tolerance = 1.0e-8) { auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); micm::ChapmanODESolver fixed_solver{}; @@ -91,45 +91,45 @@ using Group4SparseVectorMatrix = micm::SparseMatrix(3); + auto solver = getTwoStageMultiCellChapmanSolver>(3); testSolve(solver, 1.0e-2); } TEST(RegressionRosenbrock, ThreeStageSolve) { - auto solver = getThreeStageMultiCellChapmanSolver(3); + auto solver = getThreeStageMultiCellChapmanSolver>(3); testSolve(solver); } TEST(RegressionRosenbrock, FourStageSolve) { - auto solver = getFourStageMultiCellChapmanSolver(3); + auto solver = getFourStageMultiCellChapmanSolver>(3); testSolve(solver, 1.0e-4); } TEST(RegressionRosenbrock, FourStageDASolve) { - auto solver = getFourStageDAMultiCellChapmanSolver(3); + auto solver = getFourStageDAMultiCellChapmanSolver>(3); testSolve(solver, 1.0e-4); } TEST(RegressionRosenbrock, SixStageDASolve) { - auto solver = getSixStageDAMultiCellChapmanSolver(3); + auto solver = getSixStageDAMultiCellChapmanSolver>(3); testSolve(solver, 1.0e-4); } TEST(RegressionRosenbrock, VectorSolve) { - auto solver1 = getThreeStageMultiCellChapmanSolver(3); + auto solver1 = getThreeStageMultiCellChapmanSolver>(3); testSolve(solver1); - auto solver2 = getThreeStageMultiCellChapmanSolver(3); + auto solver2 = getThreeStageMultiCellChapmanSolver>(3); testSolve(solver2); - auto solver3 = getThreeStageMultiCellChapmanSolver(3); + auto solver3 = getThreeStageMultiCellChapmanSolver>(3); testSolve(solver3); - auto solver4 = getThreeStageMultiCellChapmanSolver(3); + auto solver4 = getThreeStageMultiCellChapmanSolver>(3); testSolve(solver4); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/util.hpp b/test/regression/RosenbrockChapman/util.hpp index 4aa974243..ec99671a7 100644 --- a/test/regression/RosenbrockChapman/util.hpp +++ b/test/regression/RosenbrockChapman/util.hpp @@ -79,65 +79,65 @@ std::vector createProcesses(const micm::Phase& gas_phase) return { photo_1, photo_2, photo_3, r1, r2, r3, r4 }; } -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::RosenbrockSolver getTwoStageMultiCellChapmanSolver(const size_t number_of_grid_cells) +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::RosenbrockSolver getTwoStageMultiCellChapmanSolver(const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - return micm::RosenbrockSolver( + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::RosenbrockSolver getThreeStageMultiCellChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::RosenbrockSolver getThreeStageMultiCellChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - return micm::RosenbrockSolver( + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::RosenbrockSolver getFourStageMultiCellChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::RosenbrockSolver getFourStageMultiCellChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - return micm::RosenbrockSolver( + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::RosenbrockSolver getFourStageDAMultiCellChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::RosenbrockSolver getFourStageDAMultiCellChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - return micm::RosenbrockSolver( + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::RosenbrockSolver getSixStageDAMultiCellChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::RosenbrockSolver getSixStageDAMultiCellChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - return micm::RosenbrockSolver( + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); diff --git a/test/unit/solver/test_rosenbrock.cpp b/test/unit/solver/test_rosenbrock.cpp index 0c8ad8b80..6132edc07 100644 --- a/test/unit/solver/test_rosenbrock.cpp +++ b/test/unit/solver/test_rosenbrock.cpp @@ -7,8 +7,8 @@ #include #include -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::RosenbrockSolver getSolver(std::size_t number_of_grid_cells) +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::RosenbrockSolver getSolver(std::size_t number_of_grid_cells) { // ---- foo bar baz quz quuz // foo 0 1 2 - - @@ -40,7 +40,7 @@ micm::RosenbrockSolver getSolver(std::size_t n micm::Process r3 = micm::Process::create().reactants({ quz }).products({}).phase(gas_phase).rate_constant( micm::ArrheniusRateConstant({ .A_ = 3.5e-6 })); - return micm::RosenbrockSolver( + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, false)); @@ -49,10 +49,10 @@ micm::RosenbrockSolver getSolver(std::size_t n template using SparseMatrix = micm::SparseMatrix; -template class MatrixPolicy, template class SparseMatrixPolicy> +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> void testAlphaMinusJacobian(std::size_t number_of_grid_cells) { - auto solver = getSolver(number_of_grid_cells); + auto solver = getSolver(number_of_grid_cells); auto jacobian = solver.jacobian_; EXPECT_EQ(jacobian.size(), number_of_grid_cells); @@ -98,10 +98,10 @@ void testAlphaMinusJacobian(std::size_t number_of_grid_cells) TEST(RosenbrockSolver, StandardAlphaMinusJacobian) { - testAlphaMinusJacobian(1); - testAlphaMinusJacobian(2); - testAlphaMinusJacobian(3); - testAlphaMinusJacobian(4); + testAlphaMinusJacobian>(1); + testAlphaMinusJacobian>(2); + testAlphaMinusJacobian>(3); + testAlphaMinusJacobian>(4); } template @@ -124,8 +124,8 @@ using Group4SparseVectorMatrix = micm::SparseMatrix(1); - testAlphaMinusJacobian(4); - testAlphaMinusJacobian(3); - testAlphaMinusJacobian(2); + testAlphaMinusJacobian>(1); + testAlphaMinusJacobian>(4); + testAlphaMinusJacobian>(3); + testAlphaMinusJacobian>(2); } \ No newline at end of file From 9dab4d25649b1e7a023c60f01ca56e169594168b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 27 Sep 2023 16:13:16 -0500 Subject: [PATCH 044/318] used a simpler mechanism for the reaction rates --- .../source/user_guide/multiple_grid_cells.rst | 146 +++++-------- test/tutorial/test_multiple_grid_cells.cpp | 193 ++++++------------ 2 files changed, 115 insertions(+), 224 deletions(-) diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index c15912f9d..de01b7e04 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -6,21 +6,16 @@ Multiple Grid Cells This tutorial will focus on running multiple grid cells. Because the :ref:`Rate constants` and :ref:`User defined rate constants` showed both configuring the mechanism by hand and building with a configuration file, we will only show building -up the mechanism by hand for this tutorial. That mechanism will be a simple chapman mechanism. +up the mechanism by hand for this tutorial. We will use a simple 3-reaction 3-species mechanism. .. math:: - O_{1}^{d} + N_2 &\longrightarrow O + N_2, &k_{1, \mathrm{arrhenius}} \\ - O_{1}^{d} + O_2 &\longrightarrow O + O_2, &k_{2, \mathrm{arrhenius}} \\ - O + O_3 &\longrightarrow 2O_2, &k_{3, \mathrm{arrhenius}} \\ - O + O_2 + M &\longrightarrow O_3 + M, &k_{4, \mathrm{arrhenius}} \\ - O_2 &\longrightarrow 2O, &k_{5, \mathrm{photolysis}} \\ - O_3 &\longrightarrow O_1^d +O_2,\qquad &k_{6, \mathrm{photolysis}} \\ - O_3 &\longrightarrow O +O_2, &k_{7, \mathrm{photolysis}} \\ + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ We will use three grid cells. The second grid cells will have concentrations twice as large as the first grid cell. -The third grid cell will have concentrations half as large as the first grid cell. Initial conditions are things -that might be found in the atmosphere and the photolysis values are typical photolysis rates at noon. +The third grid cell will have concentrations half as large as the first grid cell. If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. @@ -35,19 +30,18 @@ the appropriate tab below and be on your way! Otherwise, stick around for a line Line-by-line explanation ------------------------ -This mechanism only needs the arrhenius rate constant, the user defined rate constant (for photolysis rates) -and the rosenbrock solver. +This mechanism only needs the user defined rate constant and the rosenbrock solver. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 1-9 + :lines: 1-5 After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the rosenbrock solver. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 11-18 + :lines: 7-14 To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these as @@ -55,14 +49,14 @@ well as a :cpp:class:`micm::Phase`. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 84-94 + :lines: 56-60 With the species and gas phase, we can define all of our reactions .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 96-138 + :lines: 62-78 Now we can define our RosenbrockSolver. This time we'll form the reactions and chemical system in place. @@ -73,7 +67,7 @@ the order the species are added to the gas phase. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 140-143 + :lines: 80-84 Now we need to get a state and set the concentations of each of the species. In the @@ -83,95 +77,63 @@ to set the concentrations. Here we will set the concentations by providing the : .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 145-165 + :lines: 86-91 -Then we set the photolysis rates by creating a vector that is 3 elements long, one for each grid cell. +Then we set the reaction rates by creating a vector that is 3 elements long, one for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 167-172 + :lines: 93-98 -And lastly set the temperature and pressure for each grid cell. +And lastly set the temperature, pressure, and air density for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 174-179 + :lines: 100-108 Now we are ready to run the simulation. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 181-204 + :lines: 110-133 And these are the results. -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| time | grid | O | O1D | O2 | O3 | M | Ar | N2 | H2O | CO2 | -+=======+======+=============+=============+=============+=============+=============+=============+=============+=============+=============+ -| 0 | 1 | 0.00e+00 | 0.00e+00 | 7.50e-01 | 8.10e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 0 | 2 | 0.00e+00 | 0.00e+00 | 1.50e+00 | 1.62e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 0 | 3 | 0.00e+00 | 0.00e+00 | 3.75e-01 | 4.05e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 200 | 1 | 7.62e-07 | 3.19e-08 | 7.50e-01 | 7.31e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 200 | 2 | 1.52e-06 | 6.37e-08 | 1.50e+00 | 1.46e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 200 | 3 | 3.81e-07 | 1.59e-08 | 3.75e-01 | 3.65e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 400 | 1 | 1.45e-06 | 6.06e-08 | 7.50e-01 | 6.59e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 400 | 2 | 2.90e-06 | 1.21e-07 | 1.50e+00 | 1.32e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 400 | 3 | 7.25e-07 | 3.03e-08 | 3.75e-01 | 3.30e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 600 | 1 | 2.07e-06 | 8.65e-08 | 7.50e-01 | 5.94e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 600 | 2 | 4.14e-06 | 1.73e-07 | 1.50e+00 | 1.19e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 600 | 3 | 1.03e-06 | 4.33e-08 | 3.75e-01 | 2.97e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 800 | 1 | 2.63e-06 | 1.10e-07 | 7.50e-01 | 5.36e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 800 | 2 | 5.26e-06 | 2.20e-07 | 1.50e+00 | 1.07e-05 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 800 | 3 | 1.31e-06 | 5.50e-08 | 3.75e-01 | 2.68e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1000 | 1 | 3.13e-06 | 1.31e-07 | 7.50e-01 | 4.84e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1000 | 2 | 6.27e-06 | 2.62e-07 | 1.50e+00 | 9.67e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1000 | 3 | 1.57e-06 | 6.55e-08 | 3.75e-01 | 2.42e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1200 | 1 | 3.59e-06 | 1.50e-07 | 7.50e-01 | 4.36e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1200 | 2 | 7.18e-06 | 3.00e-07 | 1.50e+00 | 8.72e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1200 | 3 | 1.79e-06 | 7.50e-08 | 3.75e-01 | 2.18e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1400 | 1 | 4.00e-06 | 1.67e-07 | 7.50e-01 | 3.93e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1400 | 2 | 8.00e-06 | 3.34e-07 | 1.50e+00 | 7.87e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1400 | 3 | 2.00e-06 | 8.36e-08 | 3.75e-01 | 1.97e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1600 | 1 | 4.37e-06 | 1.83e-07 | 7.50e-01 | 3.55e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1600 | 2 | 8.74e-06 | 3.65e-07 | 1.50e+00 | 7.10e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1600 | 3 | 2.18e-06 | 9.13e-08 | 3.75e-01 | 1.77e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1800 | 1 | 4.70e-06 | 1.97e-07 | 7.50e-01 | 3.20e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1800 | 2 | 9.40e-06 | 3.93e-07 | 1.50e+00 | 6.40e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1800 | 3 | 2.35e-06 | 9.83e-08 | 3.75e-01 | 1.60e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2000 | 1 | 5.00e-06 | 2.09e-07 | 7.50e-01 | 2.89e-06 | 2.70e+25 | 3.34e-02 | 1.00e-10 | 1.19e-05 | 1.46e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2000 | 2 | 1.00e-05 | 4.18e-07 | 1.50e+00 | 5.78e-06 | 2.70e+25 | 6.68e-02 | 1.00e-10 | 2.38e-05 | 2.92e-03 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2000 | 3 | 2.50e-06 | 1.05e-07 | 3.75e-01 | 1.44e-06 | 2.70e+25 | 1.67e-02 | 1.00e-10 | 5.95e-06 | 7.30e-04 | -+-------+------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +.. csv-table:: + :header: "time", "grid", "A", "B", "C" + :widths: 6, 6, 10, 10, 10 + + 0, 1, 1.00e+00, 0.00e+00, 0.00e+00 + 0, 2, 2.00e+00, 0.00e+00, 0.00e+00 + 0, 3, 5.00e-01, 0.00e+00, 0.00e+00 + 200, 1, 5.35e-01, 4.49e-06, 4.65e-01 + 200, 2, 1.21e+00, 6.01e-06, 7.89e-01 + 200, 3, 2.30e-01, 3.31e-06, 2.70e-01 + 400, 1, 4.50e-01, 3.23e-06, 5.50e-01 + 400, 2, 1.05e+00, 4.42e-06, 9.45e-01 + 400, 3, 1.85e-01, 2.32e-06, 3.15e-01 + 600, 1, 4.00e-01, 2.63e-06, 6.00e-01 + 600, 2, 9.59e-01, 3.65e-06, 1.04e+00 + 600, 3, 1.60e-01, 1.85e-06, 3.40e-01 + 800, 1, 3.64e-01, 2.27e-06, 6.36e-01 + 800, 2, 8.90e-01, 3.18e-06, 1.11e+00 + 800, 3, 1.42e-01, 1.57e-06, 3.58e-01 + 1000, 1, 3.37e-01, 2.01e-06, 6.63e-01 + 1000, 2, 8.35e-01, 2.85e-06, 1.16e+00 + 1000, 3, 1.29e-01, 1.38e-06, 3.71e-01 + 1200, 1, 3.15e-01, 1.82e-06, 6.85e-01 + 1200, 2, 7.91e-01, 2.60e-06, 1.21e+00 + 1200, 3, 1.19e-01, 1.24e-06, 3.81e-01 + 1400, 1, 2.96e-01, 1.67e-06, 7.04e-01 + 1400, 2, 7.54e-01, 2.40e-06, 1.25e+00 + 1400, 3, 1.11e-01, 1.13e-06, 3.89e-01 + 1600, 1, 2.81e-01, 1.55e-06, 7.19e-01 + 1600, 2, 7.21e-01, 2.24e-06, 1.28e+00 + 1600, 3, 1.04e-01, 1.04e-06, 3.96e-01 + 1800, 1, 2.67e-01, 1.45e-06, 7.33e-01 + 1800, 2, 6.93e-01, 2.11e-06, 1.31e+00 + 1800, 3, 9.77e-02, 9.65e-07, 4.02e-01 + 2000, 1, 2.55e-01, 1.37e-06, 7.45e-01 + 2000, 2, 6.68e-01, 2.00e-06, 1.33e+00 + 2000, 3, 9.25e-02, 9.02e-07, 4.07e-01 \ No newline at end of file diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index 86097f706..851e268dd 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -1,10 +1,6 @@ -#include #include #include -#include -// Each rate constant is in its own header file -#include #include #include @@ -21,15 +17,9 @@ void print_header() { std::cout << std::setw(5) << "time" << "," << std::setw(5) << "grid" - << "," << std::setw(10) << "O" - << "," << std::setw(10) << "O1D" - << "," << std::setw(10) << "O2" - << "," << std::setw(10) << "O3" - << "," << std::setw(10) << "M" - << "," << std::setw(10) << "Ar" - << "," << std::setw(10) << "N2" - << "," << std::setw(10) << "H2O" - << "," << std::setw(10) << "CO2" << std::endl; + << "," << std::setw(10) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" << std::endl; } template class T> @@ -40,143 +30,82 @@ void print_state(double time, State& state) std::cout << std::setw(5) << time << ","; std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "1," << std::setw(10) - << state.variables_[0][state.variable_map_["O"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["O1D"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["O2"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["O3"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["M"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["Ar"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["N2"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["H2O"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["CO2"]] << std::endl; + << state.variables_[0][state.variable_map_["A"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["C"]] << std::endl; std::cout.copyfmt(oldState); std::cout << std::setw(5) << time << ","; std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "2," << std::setw(10) - << state.variables_[1][state.variable_map_["O"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["O1D"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["O2"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["O3"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["M"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["Ar"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["N2"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["H2O"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["CO2"]] << std::endl; + << state.variables_[1][state.variable_map_["A"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["C"]] << std::endl; std::cout.copyfmt(oldState); std::cout << std::setw(5) << time << ","; std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "3," << std::setw(10) - << state.variables_[2][state.variable_map_["O"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["O1D"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["O2"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["O3"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["M"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["Ar"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["N2"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["H2O"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["CO2"]] << std::endl; + << state.variables_[2][state.variable_map_["A"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["C"]] << std::endl; std::cout.copyfmt(oldState); } int main() { - auto o = Species("O"); - auto o1d = Species("O1D"); - auto o2 = Species("O2"); - auto o3 = Species("O3"); - auto m = Species("M"); - auto ar = Species("Ar"); - auto n2 = Species("N2"); - auto h2o = Species("H2O"); - auto co2 = Species("CO2"); - - Phase gas_phase{ std::vector{ o, o1d, o2, o3, m, ar, n2, h2o, co2 } }; - - Process r1 = Process::create() - .reactants({ o1d, n2 }) - .products({ yields(o, 1), yields(n2, 1) }) - .rate_constant(ArrheniusRateConstant({ .A_ = 2.15e-11, .B_ = 0, .C_ = 110 })) - .phase(gas_phase); - - Process r2 = Process::create() - .reactants({ o1d, o2 }) - .products({ yields(o, 1), yields(o2, 1) }) - .rate_constant(ArrheniusRateConstant(ArrheniusRateConstantParameters{ .A_ = 3.3e-11, .B_ = 0, .C_ = 55 })) - .phase(gas_phase); - - Process r3 = - Process::create() - .reactants({ o, o3 }) - .products({ yields(o2, 2) }) - .rate_constant(ArrheniusRateConstant(ArrheniusRateConstantParameters{ .A_ = 8e-12, .B_ = 0, .C_ = -2060 })) - .phase(gas_phase); - - Process r4 = - Process::create() - .reactants({ o, o2, m }) - .products({ yields(o3, 1), yields(m, 1) }) - .rate_constant(ArrheniusRateConstant(ArrheniusRateConstantParameters{ .A_ = 6.0e-34, .B_ = 0, .C_ = 2.4 })) - .phase(gas_phase); - - Process photo_1 = Process::create() - .reactants({ o2 }) - .products({ yields(o, 2) }) - .rate_constant(UserDefinedRateConstant({ .label_ = "jO2" })) - .phase(gas_phase); - - Process photo_2 = Process::create() - .reactants({ o3 }) - .products({ yields(o1d, 1), yields(o2, 1) }) - .rate_constant(UserDefinedRateConstant({ .label_ = "jO3a" })) - .phase(gas_phase); - - Process photo_3 = Process::create() - .reactants({ o3 }) - .products({ yields(o, 1), yields(o2, 1) }) - .rate_constant(UserDefinedRateConstant({ .label_ = "jO3b" })) - .phase(gas_phase); - - RosenbrockSolver solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, photo_1, photo_2, photo_3 }, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters( - 3, false) }; + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b, b }) + .products({ yields(b, 1), yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b, c }) + .products({ yields(a, 1), yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) + }; State state = solver.GetState(); // mol m-3 - double ar_concentration = 0.0334; - double co2_concentration = 0.00146; - double h2o_concentration = 1.19E-05; - double o2_concentration = 0.75; - double o3_concentration = 8.1E-06; - - // Set concentrations for each element with the same scaling factor - state.SetConcentration(ar, std::vector{ ar_concentration, ar_concentration * 2, ar_concentration * 0.5 }); - state.SetConcentration(co2, std::vector{ co2_concentration, co2_concentration * 2, co2_concentration * 0.5 }); - state.SetConcentration(h2o, std::vector{ h2o_concentration, h2o_concentration * 2, h2o_concentration * 0.5 }); - state.SetConcentration(o2, std::vector{ o2_concentration, o2_concentration * 2, o2_concentration * 0.5 }); - state.SetConcentration(o3, std::vector{ o3_concentration, o3_concentration * 2, o3_concentration * 0.5 }); - - state.SetConcentration(o1d, std::vector(3, 0)); - state.SetConcentration(o, std::vector(3, 0)); - state.SetConcentration(n2, std::vector(3, 1e-10)); - - state.SetConcentration(m, std::vector(3, 2.7e25)); - - double jo2 = 1.2e-14; - double jo3a = 2.07e-5; - double jo3b = 4.95e-4; - state.SetCustomRateParameter("jO2", std::vector(3, jo2)); - state.SetCustomRateParameter("jO3a", std::vector(3, jo3a)); - state.SetCustomRateParameter("jO3b", std::vector(3, jo3b)); - - state.conditions_[0].temperature_ = 284.19; // [K] - state.conditions_[0].pressure_ = 101245.0; // [Pa] - state.conditions_[1].temperature_ = 215.02; // [K] - state.conditions_[1].pressure_ = 100789.2; // [Pa] - state.conditions_[2].temperature_ = 299.31; // [K] - state.conditions_[2].pressure_ = 101398.0; // [Pa] + state.SetConcentration(a, std::vector{1, 2, 0.5 }); + state.SetConcentration(b, std::vector(3, 0)); + state.SetConcentration(c, std::vector(3, 0)); + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("r1", std::vector(3, k1)); + state.SetCustomRateParameter("r2", std::vector(3, k2)); + state.SetCustomRateParameter("r3", std::vector(3, k3)); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + for(size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) { + state.conditions_[cell].temperature_ = temperature; + state.conditions_[cell].pressure_ = pressure; + state.conditions_[cell].air_density_ = air_density; + } // choose a timestep and print the initial state double time_step = 200; // s From 7d834c7364436ba556cbd9a7b53b5c34083da9b6 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 27 Sep 2023 18:34:55 -0700 Subject: [PATCH 045/318] update jit rosenbrock for linear solver policy --- include/micm/solver/jit_rosenbrock.hpp | 10 +++++++--- include/micm/solver/rosenbrock.hpp | 2 +- test/integration/e5.hpp | 2 +- test/integration/hires.hpp | 4 ++-- test/integration/oregonator.hpp | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 7c51fc7b2..933bf5f8d 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -19,13 +19,17 @@ #include #include #include +#include #include namespace micm { - template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = SparseMatrix> - class JitRosenbrockSolver : public RosenbrockSolver + template< + template class MatrixPolicy = Matrix, + template class SparseMatrixPolicy = SparseMatrix, + class LinearSolverPolicy = LinearSolver> + class JitRosenbrockSolver : public RosenbrockSolver { std::shared_ptr compiler_; llvm::orc::ResourceTrackerSP function_resource_tracker_; @@ -41,7 +45,7 @@ namespace micm const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters) - : RosenbrockSolver(system, processes, parameters), + : RosenbrockSolver(system, processes, parameters), compiler_(compiler) { this->GenerateAlphaMinusJacobian(); diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 1a40b9ae0..dd4854e68 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -144,7 +144,7 @@ namespace micm /// The template parameter is the type of matrix to use template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = SparseMatrix, - class LinearSolverPolicy = LinearSolver> + class LinearSolverPolicy = LinearSolver> class RosenbrockSolver { public: diff --git a/test/integration/e5.hpp b/test/integration/e5.hpp index 5b921cf8b..098895121 100644 --- a/test/integration/e5.hpp +++ b/test/integration/e5.hpp @@ -2,7 +2,7 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> class E5 : public micm::RosenbrockSolver { public: diff --git a/test/integration/hires.hpp b/test/integration/hires.hpp index 3ad2d47dd..59db5712f 100644 --- a/test/integration/hires.hpp +++ b/test/integration/hires.hpp @@ -2,7 +2,7 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> class HIRES : public micm::RosenbrockSolver { public: @@ -13,7 +13,7 @@ class HIRES : public micm::RosenbrockSolver& processes, const micm::RosenbrockSolverParameters& parameters) - : micm::RosenbrockSolver() + : micm::RosenbrockSolver() { this->system_ = system; this->processes_ = processes; diff --git a/test/integration/oregonator.hpp b/test/integration/oregonator.hpp index cd2f0bae2..be6619d57 100644 --- a/test/integration/oregonator.hpp +++ b/test/integration/oregonator.hpp @@ -2,7 +2,7 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> class Oregonator : public micm::RosenbrockSolver { public: From c342e427fcdc27a5029f17a8fe2db514e2b57c35 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 29 Sep 2023 10:25:33 -0500 Subject: [PATCH 046/318] saving initial files --- docs/source/user_guide/but_how_fast_is_it.rst | 137 ++++++++++++++++++ docs/source/user_guide/index.rst | 1 + test/tutorial/CMakeLists.txt | 1 + test/tutorial/test_but_how_fast_is_it.cpp | 134 +++++++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 docs/source/user_guide/but_how_fast_is_it.rst create mode 100644 test/tutorial/test_but_how_fast_is_it.cpp diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst new file mode 100644 index 000000000..a285d702c --- /dev/null +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -0,0 +1,137 @@ +.. _But how fast is it: + +But how fast is it? +=================== + +This tutorial will focus on timing the solver to show how you can measure performance. +We will use a simple 3-reaction 3-species mechanism. The setup here is the same in +:ref:`Multiple grid cells`, so to understand the full setup, read that tutorial. Otherwise, +we assume that configuring a rosenbrock solver is understood and instead we will focus on timing +the solver. + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ + +If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +This mechanism only needs the user defined rate constant and the rosenbrock solver. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 1-5 + +After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the +rosenbrock solver. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 7-14 + +To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) +and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these as +well as a :cpp:class:`micm::Phase`. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 56-60 + + +With the species and gas phase, we can define all of our reactions + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 62-78 + + +Now we can define our RosenbrockSolver. This time we'll form the reactions and chemical system in place. +Also, notice the ``false`` in our :cpp:class:`micm::RosenbrockSolverParameters`. This tells the solver +not to reorder the state variables. The reordering is an optimization that can minizie fill-in for some +of the linear algebra operations. For this example, it's turned off so that the order of the state matches +the order the species are added to the gas phase. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 80-84 + + +Now we need to get a state and set the concentations of each of the species. In the +":ref:`Rate constants set concentations`" section of the rate constants tutorial, +we used a ``std::unordered_map>`` +to set the concentrations. Here we will set the concentations by providing the :cpp:class:`micm::Species` objects. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 86-91 + +Then we set the reaction rates by creating a vector that is 3 elements long, one for each grid cell. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 93-98 + +And lastly set the temperature, pressure, and air density for each grid cell. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 100-108 + +Now we are ready to run the simulation. + +.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp + :language: cpp + :lines: 110-133 + + +And these are the results. + +.. csv-table:: + :header: "time", "grid", "A", "B", "C" + :widths: 6, 6, 10, 10, 10 + + 0, 1, 1.00e+00, 0.00e+00, 0.00e+00 + 0, 2, 2.00e+00, 0.00e+00, 0.00e+00 + 0, 3, 5.00e-01, 0.00e+00, 0.00e+00 + 200, 1, 5.35e-01, 4.49e-06, 4.65e-01 + 200, 2, 1.21e+00, 6.01e-06, 7.89e-01 + 200, 3, 2.30e-01, 3.31e-06, 2.70e-01 + 400, 1, 4.50e-01, 3.23e-06, 5.50e-01 + 400, 2, 1.05e+00, 4.42e-06, 9.45e-01 + 400, 3, 1.85e-01, 2.32e-06, 3.15e-01 + 600, 1, 4.00e-01, 2.63e-06, 6.00e-01 + 600, 2, 9.59e-01, 3.65e-06, 1.04e+00 + 600, 3, 1.60e-01, 1.85e-06, 3.40e-01 + 800, 1, 3.64e-01, 2.27e-06, 6.36e-01 + 800, 2, 8.90e-01, 3.18e-06, 1.11e+00 + 800, 3, 1.42e-01, 1.57e-06, 3.58e-01 + 1000, 1, 3.37e-01, 2.01e-06, 6.63e-01 + 1000, 2, 8.35e-01, 2.85e-06, 1.16e+00 + 1000, 3, 1.29e-01, 1.38e-06, 3.71e-01 + 1200, 1, 3.15e-01, 1.82e-06, 6.85e-01 + 1200, 2, 7.91e-01, 2.60e-06, 1.21e+00 + 1200, 3, 1.19e-01, 1.24e-06, 3.81e-01 + 1400, 1, 2.96e-01, 1.67e-06, 7.04e-01 + 1400, 2, 7.54e-01, 2.40e-06, 1.25e+00 + 1400, 3, 1.11e-01, 1.13e-06, 3.89e-01 + 1600, 1, 2.81e-01, 1.55e-06, 7.19e-01 + 1600, 2, 7.21e-01, 2.24e-06, 1.28e+00 + 1600, 3, 1.04e-01, 1.04e-06, 3.96e-01 + 1800, 1, 2.67e-01, 1.45e-06, 7.33e-01 + 1800, 2, 6.93e-01, 2.11e-06, 1.31e+00 + 1800, 3, 9.77e-02, 9.65e-07, 4.02e-01 + 2000, 1, 2.55e-01, 1.37e-06, 7.45e-01 + 2000, 2, 6.68e-01, 2.00e-06, 1.33e+00 + 2000, 3, 9.25e-02, 9.02e-07, 4.07e-01 \ No newline at end of file diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index fad6b2bdc..8a55acfe5 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -18,5 +18,6 @@ If you happen to find our examples are lacking for your needs, please, :maxdepth: 1 :caption: Contents: + but_how_fast_is_it rate_constant_tutorial user_defined_rate_constant_tutorial diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index 430a4e8c5..337702bdb 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -8,6 +8,7 @@ include(test_util) create_standard_test(NAME README_example SOURCES test_README_example.cpp) create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) +create_standard_test(NAME but_how_fast_is_it SOURCES test_but_how_fast_is_it.cpp) if(ENABLE_JSON) create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) endif() diff --git a/test/tutorial/test_but_how_fast_is_it.cpp b/test/tutorial/test_but_how_fast_is_it.cpp new file mode 100644 index 000000000..851e268dd --- /dev/null +++ b/test/tutorial/test_but_how_fast_is_it.cpp @@ -0,0 +1,134 @@ +#include +#include + +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(5) << "grid" + << "," << std::setw(10) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" << std::endl; +} + +template class T> +void print_state(double time, State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "1," << std::setw(10) + << state.variables_[0][state.variable_map_["A"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["C"]] << std::endl; + + std::cout.copyfmt(oldState); + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "2," << std::setw(10) + << state.variables_[1][state.variable_map_["A"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[1][state.variable_map_["C"]] << std::endl; + + std::cout.copyfmt(oldState); + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "3," << std::setw(10) + << state.variables_[2][state.variable_map_["A"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[2][state.variable_map_["C"]] << std::endl; + + std::cout.copyfmt(oldState); +} + +int main() +{ + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b, b }) + .products({ yields(b, 1), yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b, c }) + .products({ yields(a, 1), yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) + }; + + State state = solver.GetState(); + + // mol m-3 + state.SetConcentration(a, std::vector{1, 2, 0.5 }); + state.SetConcentration(b, std::vector(3, 0)); + state.SetConcentration(c, std::vector(3, 0)); + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("r1", std::vector(3, k1)); + state.SetCustomRateParameter("r2", std::vector(3, k2)); + state.SetCustomRateParameter("r3", std::vector(3, k3)); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + for(size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) { + state.conditions_[cell].temperature_ = temperature; + state.conditions_[cell].pressure_ = pressure; + state.conditions_[cell].air_density_ = air_density; + } + + // choose a timestep and print the initial state + double time_step = 200; // s + + print_header(); + print_state(0, state); + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + + print_state(time_step * (i + 1), state); + } +} \ No newline at end of file From 5fe2baf0aab3028cf0e8b387800e9310ce54e288 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 29 Sep 2023 10:55:37 -0700 Subject: [PATCH 047/318] add LuDecompositionPolicy to LinearSolver --- include/micm/solver/jit_linear_solver.hpp | 5 ++- include/micm/solver/jit_linear_solver.inl | 45 ++++++++++++--------- include/micm/solver/linear_solver.hpp | 25 ++++++++---- include/micm/solver/linear_solver.inl | 34 +++++++++++----- test/unit/solver/test_jit_linear_solver.cpp | 12 ++++-- 5 files changed, 79 insertions(+), 42 deletions(-) diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index c872d6fa2..701fa09c2 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -16,8 +17,8 @@ namespace micm /// /// See LinearSolver class description for algorithm details /// The template parameter is the number of blocks (i.e. grid cells) in the block-diagonal matrix - template class SparseMatrixPolicy> - class JitLinearSolver : public LinearSolver + template class SparseMatrixPolicy, class LuDecompositionPolicy = JitLuDecomposition> + class JitLinearSolver : public LinearSolver { std::shared_ptr compiler_; llvm::orc::ResourceTrackerSP solve_function_resource_tracker_; diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index e2657bbbe..e3a284be8 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -4,19 +4,23 @@ namespace micm { - template class SparseMatrixPolicy> - inline JitLinearSolver::JitLinearSolver( + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline JitLinearSolver::JitLinearSolver( std::shared_ptr compiler, const SparseMatrix> &matrix, double initial_value) - : LinearSolver(matrix, initial_value), + : LinearSolver( + matrix, + initial_value, + [&](const SparseMatrixPolicy &m) -> JitLuDecomposition + { return JitLuDecomposition(compiler, m); }), compiler_(compiler) { solve_function_ = NULL; } - template class SparseMatrixPolicy> - inline JitLinearSolver::~JitLinearSolver() + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline JitLinearSolver::~JitLinearSolver() { if (solve_function_ != NULL) { @@ -25,26 +29,29 @@ namespace micm } } - template class SparseMatrixPolicy> - inline void JitLinearSolver::Factor(SparseMatrix> &matrix) + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline void JitLinearSolver::Factor( + SparseMatrix> &matrix) { - LinearSolver::Factor(matrix); + LinearSolver::Factor(matrix); GenerateSolveFunction(); } - template class SparseMatrixPolicy> + template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> - inline void JitLinearSolver::Solve(const MatrixPolicy &b, MatrixPolicy &x) + inline void JitLinearSolver::Solve( + const MatrixPolicy &b, + MatrixPolicy &x) { solve_function_( b.AsVector().data(), x.AsVector().data(), - LinearSolver::lower_matrix_.AsVector().data(), - LinearSolver::upper_matrix_.AsVector().data()); + LinearSolver::lower_matrix_.AsVector().data(), + LinearSolver::upper_matrix_.AsVector().data()); } - template class SparseMatrixPolicy> - inline void JitLinearSolver::GenerateSolveFunction() + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline void JitLinearSolver::GenerateSolveFunction() { JitFunction func = JitFunction::create(compiler_) .name("linear_solve") @@ -54,10 +61,10 @@ namespace micm { "U", JitType::DoublePtr } }) .return_type(JitType::Void); llvm::Type *double_type = func.GetType(JitType::Double); - auto Lij_yj = LinearSolver::Lij_yj_.begin(); - auto Uij_xj = LinearSolver::Uij_xj_.begin(); + auto Lij_yj = LinearSolver::Lij_yj_.begin(); + auto Uij_xj = LinearSolver::Uij_xj_.begin(); std::size_t offset = 0; - for (auto &nLij_Lii : LinearSolver::nLij_Lii_) + for (auto &nLij_Lii : LinearSolver::nLij_Lii_) { // the x vector is used for y values to conserve memory { @@ -107,8 +114,8 @@ namespace micm } offset += L; } - offset = L * LinearSolver::nUij_Uii_.size(); - for (auto &nUij_Uii : LinearSolver::nUij_Uii_) + offset = L * LinearSolver::nUij_Uii_.size(); + for (auto &nUij_Uii : LinearSolver::nUij_Uii_) { offset -= L; for (std::size_t i = 0; i < nUij_Uii.first; ++i) diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index c8046448c..47045f1be 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -20,7 +21,7 @@ namespace micm /// @brief A general-use block-diagonal sparse-matrix linear solver /// /// The sparsity pattern of each block in the block diagonal matrix is the same. - template class SparseMatrixPolicy> + template class SparseMatrixPolicy, class LuDecompositionPolicy = LuDecomposition> class LinearSolver { protected: @@ -47,7 +48,7 @@ namespace micm // Indices of non-zero combinations of U_ij and x_j std::vector> Uij_xj_; - LuDecomposition lu_decomp_; + LuDecompositionPolicy lu_decomp_; SparseMatrixPolicy lower_matrix_; SparseMatrixPolicy upper_matrix_; @@ -57,20 +58,28 @@ namespace micm /// @brief Constructs a linear solver for the sparsity structure of the given matrix /// @param matrix Sparse matrix + /// @param initial_value Initial value for matrix elements LinearSolver(const SparseMatrixPolicy& matrix, T initial_value); + /// @brief Constructs a linear solver for the sparsity structure of the given matrix + /// @param matrix Sparse matrix + /// @param initial_value Initial value for matrix elements + /// @param create_lu_decomp Function to create an LU Decomposition object that adheres to LuDecompositionPolicy + LinearSolver( + const SparseMatrixPolicy& matrix, + T initial_value, + const std::function&)> create_lu_decomp); + /// @brief Decompose the matrix into upper and lower triangular matrices void Factor(const SparseMatrixPolicy& matrix); /// @brief Solve for x in Ax = b template class MatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) void Solve( - const MatrixPolicy& b, - MatrixPolicy& x); + requires(!VectorizableDense> || !VectorizableSparse>) + void Solve(const MatrixPolicy& b, MatrixPolicy& x); template class MatrixPolicy> - requires(VectorizableDense>&& VectorizableSparse>) void Solve( - const MatrixPolicy& b, - MatrixPolicy& x); + requires(VectorizableDense> && VectorizableSparse>) + void Solve(const MatrixPolicy& b, MatrixPolicy& x); }; } // namespace micm diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index be5fb5ac7..ec6ebbad3 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -49,13 +49,27 @@ namespace micm return perm; } - template class SparseMatrixPolicy> - inline LinearSolver::LinearSolver(const SparseMatrixPolicy& matrix, T initial_value) + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline LinearSolver::LinearSolver( + const SparseMatrixPolicy& matrix, + T initial_value) + : LinearSolver( + matrix, + initial_value, + [](const SparseMatrixPolicy& m) -> LuDecompositionPolicy { return LuDecompositionPolicy(m); }) + { + } + + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline LinearSolver::LinearSolver( + const SparseMatrixPolicy& matrix, + T initial_value, + const std::function&)> create_lu_decomp) : nLij_Lii_(), Lij_yj_(), nUij_Uii_(), Uij_xj_(), - lu_decomp_(matrix) + lu_decomp_(create_lu_decomp(matrix)) { auto lu = lu_decomp_.GetLUMatrices(matrix, initial_value); lower_matrix_ = std::move(lu.first); @@ -90,16 +104,16 @@ namespace micm } }; - template class SparseMatrixPolicy> - inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix) + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix) { - lu_decomp_.Decompose(matrix, lower_matrix_, upper_matrix_); + lu_decomp_.template Decompose(matrix, lower_matrix_, upper_matrix_); } - template class SparseMatrixPolicy> + template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> requires(!VectorizableDense> || !VectorizableSparse>) - inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x) + inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x) { for (std::size_t i_cell = 0; i_cell < b.size(); ++i_cell) { @@ -152,10 +166,10 @@ namespace micm } } - template class SparseMatrixPolicy> + template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> requires(VectorizableDense> && VectorizableSparse>) - inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x) + inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x) { const std::size_t n_cells = b.GroupVectorSize(); // Loop over groups of blocks diff --git a/test/unit/solver/test_jit_linear_solver.cpp b/test/unit/solver/test_jit_linear_solver.cpp index 0f5fb7fce..aa942e08e 100644 --- a/test/unit/solver/test_jit_linear_solver.cpp +++ b/test/unit/solver/test_jit_linear_solver.cpp @@ -35,10 +35,16 @@ TEST(JitLinearSolver, DenseMatrixVectorOrdering) llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - testDenseMatrix>( + testDenseMatrix< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix, micm::JitLuDecomposition<1>>>( [&](const Group1SparseVectorMatrix& matrix, - double initial_value) -> micm::JitLinearSolver<1, Group1SparseVectorMatrix> { - return micm::JitLinearSolver<1, Group1SparseVectorMatrix>{ jit.get(), matrix, initial_value }; + double initial_value) -> micm::JitLinearSolver<1, Group1SparseVectorMatrix, micm::JitLuDecomposition<1>> + { + return micm::JitLinearSolver<1, Group1SparseVectorMatrix, micm::JitLuDecomposition<1>>{ jit.get(), + matrix, + initial_value }; }); } From 7ad568e7fce33f482e5d5d4a502d9f80f56550bd Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 29 Sep 2023 15:41:55 -0500 Subject: [PATCH 048/318] adding timing functions (#265) --- include/micm/solver/rosenbrock.hpp | 7 +++++++ include/micm/solver/rosenbrock.inl | 27 ++++++++++++++++++++++----- test/unit/solver/test_rosenbrock.cpp | 23 +++++++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index f7bef36be..9874e323b 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -158,6 +159,11 @@ namespace micm uint64_t singular{}; // Nsng uint64_t total_steps{}; // Ntotstp + std::chrono::duration total_forcing_time {}; + std::chrono::duration total_jacobian_time {}; + std::chrono::duration total_linear_factor_time {}; + std::chrono::duration total_linear_solve_time {}; + void Reset(); }; @@ -206,6 +212,7 @@ namespace micm /// @brief Advances the given step over the specified time step /// @param time_step Time [s] to advance the state by /// @return A struct containing results and a status code + template SolverResult Solve(double time_step, State& state) noexcept; /// @brief Calculate a chemical forcing diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 57d347cb5..6b3e1b381 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -1,6 +1,18 @@ // Copyright (C) 2023 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 +#define TIMED_METHOD(assigned_increment, time_it, method, ...) \ + { \ + if constexpr (time_it) { \ + auto start = std::chrono::steady_clock::now(); \ + method(__VA_ARGS__); \ + auto end = std::chrono::steady_clock::now(); \ + assigned_increment += std::chrono::duration_cast(end - start); \ + } else { \ + method(__VA_ARGS__); \ + } \ + } \ + namespace micm { // @@ -378,6 +390,10 @@ namespace micm solves = 0; singular = 0; total_steps = 0; + total_forcing_time = std::chrono::nanoseconds::zero(); + total_jacobian_time = std::chrono::nanoseconds::zero(); + total_linear_factor_time = std::chrono::nanoseconds::zero(); + total_linear_solve_time = std::chrono::nanoseconds::zero(); } inline std::string StateToString(const SolverState& state) @@ -472,6 +488,7 @@ namespace micm } template class MatrixPolicy, template class SparseMatrixPolicy> + template inline typename RosenbrockSolver::SolverResult RosenbrockSolver::Solve(double time_step, State& state) noexcept { @@ -521,10 +538,10 @@ namespace micm H = std::min(H, std::abs(time_step - present_time)); // compute the forcing at the beginning of the current time - CalculateForcing(state.rate_constants_, Y, initial_forcing); + TIMED_METHOD(stats_.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Y, initial_forcing); // compute the jacobian at the beginning of the current time - CalculateJacobian(state.rate_constants_, Y, jacobian_); + TIMED_METHOD(stats_.total_jacobian_time, time_it, CalculateJacobian, state.rate_constants_, Y, jacobian_); bool accepted = false; // Repeat step calculation until current step accepted @@ -532,7 +549,7 @@ namespace micm { bool is_singular{ false }; // Form and factor the rosenbrock ode jacobian - LinearFactor(H, parameters_.gamma_[0], is_singular, Y); + TIMED_METHOD(stats_.total_linear_factor_time, time_it, LinearFactor, H, parameters_.gamma_[0], is_singular, Y); if (is_singular) { result.state_ = SolverState::RepeatedlySingularMatrix; @@ -557,7 +574,7 @@ namespace micm auto a = parameters_.a_[stage_combinations + j]; Ynew.ForEach([&](double& iYnew, double& iKj) { iYnew += a * iKj; }, K[j]); } - CalculateForcing(state.rate_constants_, Ynew, forcing); + TIMED_METHOD(stats_.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Ynew, forcing); } } K[stage].AsVector().assign(forcing.AsVector().begin(), forcing.AsVector().end()); @@ -567,7 +584,7 @@ namespace micm K[stage].ForEach([&](double& iKstage, double& iKj) { iKstage += HC * iKj; }, K[j]); } temp.AsVector().assign(K[stage].AsVector().begin(), K[stage].AsVector().end()); - linear_solver_.template Solve(temp, K[stage]); + TIMED_METHOD(stats_.total_linear_solve_time, time_it, linear_solver_.template Solve, temp, K[stage]); stats_.solves += 1; } diff --git a/test/unit/solver/test_rosenbrock.cpp b/test/unit/solver/test_rosenbrock.cpp index 0c8ad8b80..b806fa46d 100644 --- a/test/unit/solver/test_rosenbrock.cpp +++ b/test/unit/solver/test_rosenbrock.cpp @@ -128,4 +128,27 @@ TEST(RosenbrockSolver, DenseAlphaMinusJacobian) testAlphaMinusJacobian(4); testAlphaMinusJacobian(3); testAlphaMinusJacobian(2); +} + +TEST(RosenbrockSolver, Timing) +{ + auto solver = getSolver(1); + + auto state = solver.GetState(); + + state.variables_[0] = { + 1, 1, 1, 1, 1, + }; + + auto result = solver.Solve(1, state); + EXPECT_EQ(result.stats_.total_forcing_time.count(), 0); + EXPECT_EQ(result.stats_.total_jacobian_time.count(), 0); + EXPECT_EQ(result.stats_.total_linear_factor_time.count(), 0); + EXPECT_EQ(result.stats_.total_linear_solve_time.count(), 0); + + result = solver.Solve(1, state); + EXPECT_NE( + result.stats_.total_forcing_time.count() + result.stats_.total_jacobian_time.count() + + result.stats_.total_linear_factor_time.count() + result.stats_.total_linear_solve_time.count(), + 0.0); } \ No newline at end of file From 515eba52e479914581d8dde189a196a4cde973c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:53:46 -0700 Subject: [PATCH 049/318] Auto-format code changes (#266) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/solver/rosenbrock.hpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 9874e323b..affa50370 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -18,9 +18,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -159,10 +159,10 @@ namespace micm uint64_t singular{}; // Nsng uint64_t total_steps{}; // Ntotstp - std::chrono::duration total_forcing_time {}; - std::chrono::duration total_jacobian_time {}; - std::chrono::duration total_linear_factor_time {}; - std::chrono::duration total_linear_solve_time {}; + std::chrono::duration total_forcing_time{}; + std::chrono::duration total_jacobian_time{}; + std::chrono::duration total_linear_factor_time{}; + std::chrono::duration total_linear_solve_time{}; void Reset(); }; From 27586bdea1ae77eabb1451600c3978e21a68b63b Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 29 Sep 2023 15:31:19 -0700 Subject: [PATCH 050/318] update README example --- README.md | 2 +- test/tutorial/test_README_example.cpp | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 869659c10..da2482149 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,7 @@ int main(const int argc, const char *argv[]) std::vector reactions{ r1, r2 }; - RosenbrockSolver solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index 6681190ab..457594621 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -5,9 +5,6 @@ using namespace micm; -template -using SparseMatrixPolicy = SparseMatrix; - int main(const int argc, const char *argv[]) { auto foo = Species{ "Foo" }; @@ -32,9 +29,7 @@ int main(const int argc, const char *argv[]) std::vector reactions{ r1, r2 }; - RosenbrockSolver solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; solver.parameters_.print(); State state = solver.GetState(); From c21e7d64517e86ce86331b8116fbad26e578586c Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 29 Sep 2023 15:49:41 -0700 Subject: [PATCH 051/318] add alias for standard-ordered sparse matrix --- include/micm/solver/rosenbrock.hpp | 2 +- include/micm/util/sparse_matrix.hpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index affa50370..ccb34570f 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -143,7 +143,7 @@ namespace micm /// @brief An implementation of the Rosenbrock ODE solver /// /// The template parameter is the type of matrix to use - template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = SparseMatrix> + template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = StandardSparseMatrix> class RosenbrockSolver { public: diff --git a/include/micm/util/sparse_matrix.hpp b/include/micm/util/sparse_matrix.hpp index 4720d8a51..fe4915a70 100644 --- a/include/micm/util/sparse_matrix.hpp +++ b/include/micm/util/sparse_matrix.hpp @@ -25,6 +25,12 @@ namespace micm template class SparseMatrixBuilder; + template + class SparseMatrix; + + template + using StandardSparseMatrix = SparseMatrix; + /// @brief A sparse block-diagonal 2D matrix class with contiguous memory /// /// Each block sub-matrix is square and has the same structure of non-zero elements @@ -33,7 +39,7 @@ namespace micm /// /// The template parameters are the type of the matrix elements and a class that /// defines the sizing and ordering of the data elements - template + template class SparseMatrix : public OrderingPolicy { std::size_t number_of_blocks_; // Number of block sub-matrices in the overall matrix From 8510a3120265e0e8b3b55d58011f99efd448c453 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 29 Sep 2023 15:58:14 -0700 Subject: [PATCH 052/318] update tutorials to remove alias --- .../test_rate_constants_no_user_defined_by_hand.cpp | 12 +++--------- ...st_rate_constants_no_user_defined_with_config.cpp | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index b53f2851a..ec35aa5a9 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -14,12 +14,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -135,9 +129,9 @@ int main(const int argc, const char* argv[]) auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7 }; - RosenbrockSolver solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); state.conditions_[0].temperature_ = 287.45; // K diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index a43b9a69b..09ed08640 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -15,12 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -68,9 +62,9 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); From 86dcebe33231af6f601b057a7b3fcd59ee22de6c Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 11:04:49 -0500 Subject: [PATCH 053/318] starting more content --- docs/source/user_guide/but_how_fast_is_it.rst | 65 +------------------ 1 file changed, 3 insertions(+), 62 deletions(-) diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst index a285d702c..6e0eb69d9 100644 --- a/docs/source/user_guide/but_how_fast_is_it.rst +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -5,7 +5,7 @@ But how fast is it? This tutorial will focus on timing the solver to show how you can measure performance. We will use a simple 3-reaction 3-species mechanism. The setup here is the same in -:ref:`Multiple grid cells`, so to understand the full setup, read that tutorial. Otherwise, +:ref:`Multiple grid cells`. To understand the full setup, read that tutorial. Otherwise, we assume that configuring a rosenbrock solver is understood and instead we will focus on timing the solver. @@ -28,70 +28,11 @@ the appropriate tab below and be on your way! Otherwise, stick around for a line Line-by-line explanation ------------------------ -This mechanism only needs the user defined rate constant and the rosenbrock solver. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 1-5 - -After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the -rosenbrock solver. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 7-14 - -To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) -and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these as -well as a :cpp:class:`micm::Phase`. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 56-60 - - -With the species and gas phase, we can define all of our reactions - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 62-78 - - -Now we can define our RosenbrockSolver. This time we'll form the reactions and chemical system in place. -Also, notice the ``false`` in our :cpp:class:`micm::RosenbrockSolverParameters`. This tells the solver -not to reorder the state variables. The reordering is an optimization that can minizie fill-in for some -of the linear algebra operations. For this example, it's turned off so that the order of the state matches -the order the species are added to the gas phase. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 80-84 - - -Now we need to get a state and set the concentations of each of the species. In the -":ref:`Rate constants set concentations`" section of the rate constants tutorial, -we used a ``std::unordered_map>`` -to set the concentrations. Here we will set the concentations by providing the :cpp:class:`micm::Species` objects. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 86-91 - -Then we set the reaction rates by creating a vector that is 3 elements long, one for each grid cell. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 93-98 - -And lastly set the temperature, pressure, and air density for each grid cell. - -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp - :language: cpp - :lines: 100-108 +Up until now we have neglected to talk about what the solver returns, which is a :cpp:class:`micm::SolverResult` Now we are ready to run the simulation. -.. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp +.. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp :language: cpp :lines: 110-133 From 356341624e51a981055be974e775668514159294 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 2 Oct 2023 11:34:56 -0700 Subject: [PATCH 054/318] add first JitRosenbrock regression test --- include/micm/solver/jit_linear_solver.hpp | 2 + include/micm/solver/jit_linear_solver.inl | 4 +- include/micm/solver/jit_lu_decomposition.hpp | 2 + include/micm/solver/jit_rosenbrock.hpp | 16 ++- include/micm/solver/linear_solver.hpp | 2 +- include/micm/solver/rosenbrock.hpp | 54 +++++---- include/micm/solver/rosenbrock.inl | 22 +++- .../util/sparse_matrix_vector_ordering.hpp | 7 +- include/micm/util/vector_matrix.hpp | 2 +- .../RosenbrockChapman/CMakeLists.txt | 6 +- .../regression/RosenbrockChapman/jit_util.hpp | 103 +++++++++++++++++ .../regression_test_dforce_dy.cpp | 106 +++++------------- .../regression_test_dforce_dy_policy.hpp | 72 ++++++++++++ .../regression_test_jit_dforce_dy.cpp | 16 +++ test/regression/RosenbrockChapman/util.hpp | 5 +- test/unit/solver/test_jit_rosenbrock.cpp | 40 +++---- 16 files changed, 329 insertions(+), 130 deletions(-) create mode 100644 test/regression/RosenbrockChapman/jit_util.hpp create mode 100644 test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp create mode 100644 test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index 701fa09c2..a2e35b28d 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -25,6 +25,8 @@ namespace micm void (*solve_function_)(const double*, double*, const double*, const double*); public: + JitLinearSolver(){}; + /// @brief Create a JITed linear solver for a given sparse matrix structure /// @param compiler JIT compiler /// @param matrix Block-diagonal sparse matrix to create solver for diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index e3a284be8..f886a4f7a 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -12,8 +12,8 @@ namespace micm : LinearSolver( matrix, initial_value, - [&](const SparseMatrixPolicy &m) -> JitLuDecomposition - { return JitLuDecomposition(compiler, m); }), + [&](const SparseMatrixPolicy &m) -> LuDecompositionPolicy + { return LuDecompositionPolicy(compiler, m); }), compiler_(compiler) { solve_function_ = NULL; diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index 7d8258427..a46f24814 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -23,6 +23,8 @@ namespace micm void (*decompose_function_)(const double *, double *, double *); public: + JitLuDecomposition(){}; + /// @brief Create a JITed LU decomposer for a given sparse matrix structure /// @param compiler JIT compiler /// @param matrix Sparse matrix to create LU decomposer for diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 933bf5f8d..422be8ca7 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -21,14 +21,16 @@ #include #include #include +#include +#include namespace micm { template< - template class MatrixPolicy = Matrix, - template class SparseMatrixPolicy = SparseMatrix, - class LinearSolverPolicy = LinearSolver> + template class MatrixPolicy = VectorMatrix, + template class SparseMatrixPolicy = VectorSparseMatrix, + class LinearSolverPolicy = JitLinearSolver<4, SparseMatrixPolicy>> class JitRosenbrockSolver : public RosenbrockSolver { std::shared_ptr compiler_; @@ -45,7 +47,13 @@ namespace micm const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters) - : RosenbrockSolver(system, processes, parameters), + : RosenbrockSolver( + system, + processes, + parameters, + [&](const SparseMatrixPolicy& matrix, double initial_value) -> LinearSolverPolicy { + return LinearSolverPolicy{ compiler, matrix, initial_value }; + }), compiler_(compiler) { this->GenerateAlphaMinusJacobian(); diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index 47045f1be..2a53386cf 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -54,7 +54,7 @@ namespace micm public: /// @brief default constructor - LinearSolver() = default; + LinearSolver(){}; /// @brief Constructs a linear solver for the sparsity structure of the given matrix /// @param matrix Sparse matrix diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index dd4854e68..ac8bb733c 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -86,37 +86,37 @@ namespace micm void print() const; /// @brief an L-stable method, 2 stages, order 2 - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters two_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief an L-stable method, 3 stages, order 3, 2 function evaluations - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters three_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief L-stable rosenbrock method of order 4, with 4 stages - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters four_stage_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief A stiffly-stable method, 4 stages, order 3 - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters four_stage_differential_algebraic_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); /// @brief stiffly-stable rosenbrock method of order 4, with 6 stages - /// @param number_of_grid_cells - /// @param reorder_state - /// @return + /// @param number_of_grid_cells + /// @param reorder_state + /// @return static RosenbrockSolverParameters six_stage_differential_algebraic_rosenbrock_parameters( size_t number_of_grid_cells = 1, bool reorder_state = true); @@ -142,9 +142,10 @@ namespace micm /// @brief An implementation of the Rosenbrock ODE solver /// /// The template parameter is the type of matrix to use - template class MatrixPolicy = Matrix, - template class SparseMatrixPolicy = SparseMatrix, - class LinearSolverPolicy = LinearSolver> + template< + template class MatrixPolicy = Matrix, + template class SparseMatrixPolicy = SparseMatrix, + class LinearSolverPolicy = LinearSolver> class RosenbrockSolver { public: @@ -194,11 +195,24 @@ namespace micm /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters /// @param system The chemical system to create the solver for /// @param processes The collection of chemical processes that will be applied during solving + /// @param parameters Rosenbrock algorithm parameters RosenbrockSolver( const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters); + /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters, + /// with a specific function provided to create the linear solver + /// @param system The chemical system to create the solver for + /// @param processes The collection of chemical processes that will be applied during solving + /// @param parameters Rosenbrock algorithm parameters + /// @param create_linear_solver Function that will be used to create a linear solver instance + RosenbrockSolver( + const System& system, + const std::vector& processes, + const RosenbrockSolverParameters& parameters, + const std::function, double)> create_linear_solver); + virtual ~RosenbrockSolver() = default; /// @brief Returns a state object for use with the solver @@ -223,9 +237,9 @@ namespace micm /// @param jacobian Jacobian matrix (dforce_dy) /// @param alpha void AlphaMinusJacobian(SparseMatrixPolicy& jacobian, const double& alpha) const - requires(!VectorizableSparse>); + requires(!VectorizableSparse>); void AlphaMinusJacobian(SparseMatrixPolicy& jacobian, const double& alpha) const - requires(VectorizableSparse>); + requires(VectorizableSparse>); /// @brief Update the rate constants for the environment state /// @param state The current state of the chemical system diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index f8621d526..4a2d3a548 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -416,6 +416,22 @@ namespace micm const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters) + : RosenbrockSolver( + system, + processes, + parameters, + [](const SparseMatrixPolicy& matrix, double initial_value) -> LinearSolverPolicy { + return LinearSolverPolicy{ matrix, initial_value }; + }) + { + } + + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline RosenbrockSolver::RosenbrockSolver( + const System& system, + const std::vector& processes, + const RosenbrockSolverParameters& parameters, + const std::function, double)> create_linear_solver) : system_(system), processes_(processes), parameters_(parameters), @@ -451,7 +467,7 @@ namespace micm builder = builder.with_element(i, i); jacobian_ = builder; - linear_solver_ = LinearSolverPolicy(jacobian_, 1.0e-30); + linear_solver_ = create_linear_solver(jacobian_, 1.0e-30); process_set_.SetJacobianFlatIds(jacobian_); for (std::size_t i = 0; i < jacobian_[0].size(); ++i) jacobian_diagonal_elements_.push_back(jacobian_.VectorIndex(0, i, i)); @@ -473,7 +489,9 @@ namespace micm template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> inline typename RosenbrockSolver::SolverResult - RosenbrockSolver::Solve(double time_step, State& state) noexcept + RosenbrockSolver::Solve( + double time_step, + State& state) noexcept { typename RosenbrockSolver::SolverResult result{}; result.state_ = SolverState::Running; diff --git a/include/micm/util/sparse_matrix_vector_ordering.hpp b/include/micm/util/sparse_matrix_vector_ordering.hpp index b3b17cc0b..eaa7b84d8 100644 --- a/include/micm/util/sparse_matrix_vector_ordering.hpp +++ b/include/micm/util/sparse_matrix_vector_ordering.hpp @@ -15,7 +15,7 @@ namespace micm /// /// The template argument is the number of blocks per set of blocks and should be /// approximately the size of the vector register. - template + template class SparseMatrixVectorOrdering { protected: @@ -61,4 +61,9 @@ namespace micm return std::ceil((double)number_of_blocks / (double)L); } }; + + // Default vectorized SparseMatrix + template + using VectorSparseMatrix = SparseMatrix>; + } // namespace micm \ No newline at end of file diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index 9a75bc06e..32000cef6 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -19,7 +19,7 @@ namespace micm /// /// The template arguments are the type of the matrix elements and the size of the number /// of rows per group. - template + template class VectorMatrix { std::vector data_; diff --git a/test/regression/RosenbrockChapman/CMakeLists.txt b/test/regression/RosenbrockChapman/CMakeLists.txt index 6af23ca24..cdef173cd 100644 --- a/test/regression/RosenbrockChapman/CMakeLists.txt +++ b/test/regression/RosenbrockChapman/CMakeLists.txt @@ -8,4 +8,8 @@ include(test_util) create_standard_test(NAME regression_test_rosenbrock_p_force SOURCES regression_test_p_force.cpp) create_standard_test(NAME regression_test_rosenbrock_dforce_dy SOURCES regression_test_dforce_dy.cpp) -create_standard_test(NAME regression_test_solve SOURCES regression_test_solve.cpp) \ No newline at end of file +create_standard_test(NAME regression_test_solve SOURCES regression_test_solve.cpp) + +if(ENABLE_LLVM) + create_standard_test(NAME regression_test_jit_dforce_dy SOURCES regression_test_jit_dforce_dy.cpp) +endif() \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/jit_util.hpp b/test/regression/RosenbrockChapman/jit_util.hpp new file mode 100644 index 000000000..19c2d27c2 --- /dev/null +++ b/test/regression/RosenbrockChapman/jit_util.hpp @@ -0,0 +1,103 @@ +#include + +#include "util.hpp" + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::JitRosenbrockSolver getTwoStageMultiCellJitChapmanSolver( + const size_t number_of_grid_cells) +{ + micm::Phase gas_phase = createGasPhase(); + std::vector processes = createProcesses(gas_phase); + + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + return micm::JitRosenbrockSolver( + jit.get(), + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::move(processes), + micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells)); +} + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::JitRosenbrockSolver getThreeStageMultiCellJitChapmanSolver( + const size_t number_of_grid_cells) +{ + micm::Phase gas_phase = createGasPhase(); + std::vector processes = createProcesses(gas_phase); + + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + return micm::JitRosenbrockSolver( + jit.get(), + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::move(processes), + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells)); +} + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::JitRosenbrockSolver getFourStageMultiCellJitChapmanSolver( + const size_t number_of_grid_cells) +{ + micm::Phase gas_phase = createGasPhase(); + std::vector processes = createProcesses(gas_phase); + + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + return micm::JitRosenbrockSolver( + jit.get(), + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::move(processes), + micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells)); +} + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::JitRosenbrockSolver getFourStageDAMultiCellJitChapmanSolver( + const size_t number_of_grid_cells) +{ + micm::Phase gas_phase = createGasPhase(); + std::vector processes = createProcesses(gas_phase); + + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + return micm::JitRosenbrockSolver( + jit.get(), + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::move(processes), + micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); +} + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +micm::JitRosenbrockSolver getSixStageDAMultiCellJitChapmanSolver( + const size_t number_of_grid_cells) +{ + micm::Phase gas_phase = createGasPhase(); + std::vector processes = createProcesses(gas_phase); + + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + return micm::JitRosenbrockSolver( + jit.get(), + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::move(processes), + micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); +} \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp b/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp index 49335c002..5ac2aa8dc 100644 --- a/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp +++ b/test/regression/RosenbrockChapman/regression_test_dforce_dy.cpp @@ -1,87 +1,43 @@ #include #include -#include -#include -#include -#include -#include -#include "chapman_ode_solver.hpp" -#include "util.hpp" - -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -void testJacobian() -{ - std::random_device rnd_device; - std::mt19937 engine{ rnd_device() }; - std::lognormal_distribution dist{ -2.0, 4.0 }; - - micm::ChapmanODESolver fixed_solver{}; - auto solver = getThreeStageMultiCellChapmanSolver(3); - - auto state = solver.GetState(); - - auto& state_vec = state.variables_.AsVector(); - std::generate(state_vec.begin(), state_vec.end(), [&]() { return dist(engine); }); - auto& rate_const_vec = state.rate_constants_.AsVector(); - std::generate(state_vec.begin(), state_vec.end(), [&]() { return dist(engine); }); - - auto& jacobian = solver.jacobian_; - solver.CalculateJacobian(state.rate_constants_, state.variables_, jacobian); - - for (std::size_t i{}; i < 3; ++i) - { - double number_density_air = 1.0; - std::vector rate_constants = state.rate_constants_[i]; - std::vector variables(state.variables_[i].size()); - for (std::size_t j{}; j < state.variables_[i].size(); ++j) - variables[j] = state.variables_[i][state.variable_map_[fixed_solver.species_names()[j]]]; - std::vector fixed_jacobian = fixed_solver.dforce_dy(rate_constants, variables, number_density_air); - - // TODO: The sparse matrix data ordering in the hard-coded solver is different (maybe because of pivoting?) - // As the remaining linear solver functions are generalized, use the logic in the preprocessor to - // decipher the data elements in the hard-coded solver sparse matrix to finish this test. - // EXPECT_EQ(jacobian.FlatBlockSize(), fixed_jacobian.size()); - for (std::size_t j{}; j < fixed_jacobian.size(); ++j) - { - // EXPECT_NEAR(jacobian.AsVector()[i * jacobian.FlatBlockSize() + j], fixed_jacobian[j], 1.0e-10); - } - } -} - -template -using DenseMatrix = micm::Matrix; -template -using SparseMatrix = micm::SparseMatrix; - -template -using Group1VectorMatrix = micm::VectorMatrix; -template -using Group2VectorMatrix = micm::VectorMatrix; -template -using Group3VectorMatrix = micm::VectorMatrix; -template -using Group4VectorMatrix = micm::VectorMatrix; - -template -using Group1SparseVectorMatrix = micm::SparseMatrix>; -template -using Group2SparseVectorMatrix = micm::SparseMatrix>; -template -using Group3SparseVectorMatrix = micm::SparseMatrix>; -template -using Group4SparseVectorMatrix = micm::SparseMatrix>; +#include "regression_test_dforce_dy_policy.hpp" TEST(RegressionRosenbrock, Jacobian) { - testJacobian>(); + auto solver = getThreeStageMultiCellChapmanSolver>(3); + testJacobian<>(solver); } TEST(RegressionRosenbrock, VectorJacobian) { - testJacobian>(); - testJacobian>(); - testJacobian>(); - testJacobian>(); + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::LinearSolver>(3); + testJacobian<>(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group2VectorMatrix, + Group2SparseVectorMatrix, + micm::LinearSolver>(3); + testJacobian<>(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::LinearSolver>(3); + testJacobian<>(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group4VectorMatrix, + Group4SparseVectorMatrix, + micm::LinearSolver>(3); + testJacobian<>(solver); + } } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp b/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp new file mode 100644 index 000000000..f95bb15a0 --- /dev/null +++ b/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "chapman_ode_solver.hpp" +#include "util.hpp" + +template +void testJacobian(OdeSolverPolicy& solver) +{ + std::random_device rnd_device; + std::mt19937 engine{ rnd_device() }; + std::lognormal_distribution dist{ -2.0, 4.0 }; + + micm::ChapmanODESolver fixed_solver{}; + + auto state = solver.GetState(); + + auto& state_vec = state.variables_.AsVector(); + std::generate(state_vec.begin(), state_vec.end(), [&]() { return dist(engine); }); + auto& rate_const_vec = state.rate_constants_.AsVector(); + std::generate(state_vec.begin(), state_vec.end(), [&]() { return dist(engine); }); + + auto& jacobian = solver.jacobian_; + solver.CalculateJacobian(state.rate_constants_, state.variables_, jacobian); + + for (std::size_t i{}; i < 3; ++i) + { + double number_density_air = 1.0; + std::vector rate_constants = state.rate_constants_[i]; + std::vector variables(state.variables_[i].size()); + for (std::size_t j{}; j < state.variables_[i].size(); ++j) + variables[j] = state.variables_[i][state.variable_map_[fixed_solver.species_names()[j]]]; + std::vector fixed_jacobian = fixed_solver.dforce_dy(rate_constants, variables, number_density_air); + + // TODO: The sparse matrix data ordering in the hard-coded solver is different (maybe because of pivoting?) + // As the remaining linear solver functions are generalized, use the logic in the preprocessor to + // decipher the data elements in the hard-coded solver sparse matrix to finish this test. + // EXPECT_EQ(jacobian.FlatBlockSize(), fixed_jacobian.size()); + for (std::size_t j{}; j < fixed_jacobian.size(); ++j) + { + // EXPECT_NEAR(jacobian.AsVector()[i * jacobian.FlatBlockSize() + j], fixed_jacobian[j], 1.0e-10); + } + } +} + +template +using DenseMatrix = micm::Matrix; +template +using SparseMatrix = micm::SparseMatrix; + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp b/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp new file mode 100644 index 000000000..05f4ed3a1 --- /dev/null +++ b/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp @@ -0,0 +1,16 @@ +#include + +#include +#include + +#include "jit_util.hpp" +#include "regression_test_dforce_dy_policy.hpp" + +TEST(RegressionJitRosenbrock, VectorJacobian) +{ + auto solver = getThreeStageMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testJacobian<>(solver); +} \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/util.hpp b/test/regression/RosenbrockChapman/util.hpp index ec99671a7..31c942316 100644 --- a/test/regression/RosenbrockChapman/util.hpp +++ b/test/regression/RosenbrockChapman/util.hpp @@ -1,3 +1,5 @@ +#pragma once + #include #include @@ -80,7 +82,8 @@ std::vector createProcesses(const micm::Phase& gas_phase) } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -micm::RosenbrockSolver getTwoStageMultiCellChapmanSolver(const size_t number_of_grid_cells) +micm::RosenbrockSolver getTwoStageMultiCellChapmanSolver( + const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index 2ef4c01e1..52b99ded3 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -9,10 +9,9 @@ #include #include -template class MatrixPolicy, template class SparseMatrixPolicy> -micm::JitRosenbrockSolver getSolver( - std::shared_ptr jit, - std::size_t number_of_grid_cells) +template class MatrixPolicy, template class SparseMatrixPolicy> +micm::JitRosenbrockSolver> +getSolver(std::shared_ptr jit) { // ---- foo bar baz quz quuz // foo 0 1 2 - - @@ -44,20 +43,21 @@ micm::JitRosenbrockSolver getSolver( micm::Process r3 = micm::Process::create().reactants({ quz }).products({}).phase(gas_phase).rate_constant( micm::ArrheniusRateConstant({ .A_ = 3.5e-6 })); - return micm::JitRosenbrockSolver( - jit, - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, false)); + return micm:: + JitRosenbrockSolver>( + jit, + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, false)); } template using SparseMatrix = micm::SparseMatrix; -template class MatrixPolicy, template class SparseMatrixPolicy> -void testAlphaMinusJacobian(std::shared_ptr jit, std::size_t number_of_grid_cells) +template class MatrixPolicy, template class SparseMatrixPolicy> +void testAlphaMinusJacobian(std::shared_ptr jit) { - auto solver = getSolver(jit, number_of_grid_cells); + auto solver = getSolver(jit); // return; auto jacobian = solver.jacobian_; @@ -120,7 +120,7 @@ using Group3SparseVectorMatrix = micm::SparseMatrix using Group4SparseVectorMatrix = micm::SparseMatrix>; -TEST(JitRosenbrockSolver, DenseAlphaMinusJacobian) +TEST(JitRosenbrockSolver, AlphaMinusJacobian) { auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) @@ -128,12 +128,8 @@ TEST(JitRosenbrockSolver, DenseAlphaMinusJacobian) llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - testAlphaMinusJacobian(jit.get(), 1); - testAlphaMinusJacobian(jit.get(), 2); - testAlphaMinusJacobian(jit.get(), 3); - testAlphaMinusJacobian(jit.get(), 4); -} - -// create mozart-micm -// enable multiple grid cells for KPP -// add mozart to performace \ No newline at end of file + testAlphaMinusJacobian<1, Group1VectorMatrix, Group1SparseVectorMatrix>(jit.get()); + testAlphaMinusJacobian<2, Group2VectorMatrix, Group2SparseVectorMatrix>(jit.get()); + testAlphaMinusJacobian<3, Group3VectorMatrix, Group3SparseVectorMatrix>(jit.get()); + testAlphaMinusJacobian<4, Group4VectorMatrix, Group4SparseVectorMatrix>(jit.get()); +} \ No newline at end of file From 19b3e541f140dd40c01099a7f0bf434b5bcfcf3e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 13:44:23 -0500 Subject: [PATCH 055/318] timing tutorial --- docs/CMakeLists.txt | 2 +- docs/source/user_guide/but_how_fast_is_it.rst | 100 +++++++----- docs/source/user_guide/index.rst | 2 +- include/micm/solver/rosenbrock.hpp | 40 +++-- include/micm/solver/rosenbrock.inl | 8 +- test/tutorial/test_but_how_fast_is_it.cpp | 151 +++++++----------- 6 files changed, 147 insertions(+), 156 deletions(-) diff --git a/docs/CMakeLists.txt b/docs/CMakeLists.txt index c48767b73..319c85f3a 100644 --- a/docs/CMakeLists.txt +++ b/docs/CMakeLists.txt @@ -23,7 +23,7 @@ configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) #Doxygen won't create this for us add_custom_command( - OUTPUT ${DOXYGEN_INDEX_FILE} + OUTPUT ${DOXYGEN_INDEX_FILE} __fake_file_to_ensure_this_always_run DEPENDS ${PUBLIC_HEADERS} COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst index 6e0eb69d9..4be09365d 100644 --- a/docs/source/user_guide/but_how_fast_is_it.rst +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -28,51 +28,63 @@ the appropriate tab below and be on your way! Otherwise, stick around for a line Line-by-line explanation ------------------------ -Up until now we have neglected to talk about what the solver returns, which is a :cpp:class:`micm::SolverResult` +Up until now we have neglected to talk about what the solver returns, which is a :cpp:class:`micm::RosenbrockSolver::SolverResult`. -Now we are ready to run the simulation. +There are four values returned. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::final_time_` + + * This is the final simulation time achieved by the solver. The :cpp:func:`micm::RosenbrockSolver::Solve` function attempts to integrate the passed in state forward a set number of seconds. Often, the solver is able to complete the integration. However, extremely stiff systems may only solve for a fraction of the time. It is imperative that the ``final_time_`` value is checked. If it is not equal to the amount of time you intended to solve for, call solve again as we do in the tutorials with the difference between what was solved and how long you intended to solve. + + .. note:: + This does **not** represent the amount of time taken by the solve routine. You must measure that yourself(shown below). The ``final_time_`` is simulation time. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::result_` + + * This contains the integrated state value; the concentrations reached at the end of Solve function after the amount of time specified by ``final_time_``. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::state_` + + * There are many possible reasons for the solver to return. This value is one of the possible enum values define on the :cpp:enum:`micm::SolverState`. Hopefully, you receive a :cpp:enumerator:`micm::SolverState::Converged` state. But, it is good to always check this to ensure the solver really did converge. You can print this value using the :cpp:func:`micm::StateToString` function. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::stats_` + + * This is an instance of a :cpp:class:`micm::RosenbrockSolver::SolverStats` struct which contains information about the number of function calls and optionally the total cumulative amount of time spent calling each function. For the time to be collected, you must call the ``Solve`` function with a ``true`` templated parameter. Please see the example below. + +First, let's run the simulation but without collecting the solve time. We'll inspect the solver state and look at what's collected +in the stats object. .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp :language: cpp - :lines: 110-133 - - -And these are the results. - -.. csv-table:: - :header: "time", "grid", "A", "B", "C" - :widths: 6, 6, 10, 10, 10 - - 0, 1, 1.00e+00, 0.00e+00, 0.00e+00 - 0, 2, 2.00e+00, 0.00e+00, 0.00e+00 - 0, 3, 5.00e-01, 0.00e+00, 0.00e+00 - 200, 1, 5.35e-01, 4.49e-06, 4.65e-01 - 200, 2, 1.21e+00, 6.01e-06, 7.89e-01 - 200, 3, 2.30e-01, 3.31e-06, 2.70e-01 - 400, 1, 4.50e-01, 3.23e-06, 5.50e-01 - 400, 2, 1.05e+00, 4.42e-06, 9.45e-01 - 400, 3, 1.85e-01, 2.32e-06, 3.15e-01 - 600, 1, 4.00e-01, 2.63e-06, 6.00e-01 - 600, 2, 9.59e-01, 3.65e-06, 1.04e+00 - 600, 3, 1.60e-01, 1.85e-06, 3.40e-01 - 800, 1, 3.64e-01, 2.27e-06, 6.36e-01 - 800, 2, 8.90e-01, 3.18e-06, 1.11e+00 - 800, 3, 1.42e-01, 1.57e-06, 3.58e-01 - 1000, 1, 3.37e-01, 2.01e-06, 6.63e-01 - 1000, 2, 8.35e-01, 2.85e-06, 1.16e+00 - 1000, 3, 1.29e-01, 1.38e-06, 3.71e-01 - 1200, 1, 3.15e-01, 1.82e-06, 6.85e-01 - 1200, 2, 7.91e-01, 2.60e-06, 1.21e+00 - 1200, 3, 1.19e-01, 1.24e-06, 3.81e-01 - 1400, 1, 2.96e-01, 1.67e-06, 7.04e-01 - 1400, 2, 7.54e-01, 2.40e-06, 1.25e+00 - 1400, 3, 1.11e-01, 1.13e-06, 3.89e-01 - 1600, 1, 2.81e-01, 1.55e-06, 7.19e-01 - 1600, 2, 7.21e-01, 2.24e-06, 1.28e+00 - 1600, 3, 1.04e-01, 1.04e-06, 3.96e-01 - 1800, 1, 2.67e-01, 1.45e-06, 7.33e-01 - 1800, 2, 6.93e-01, 2.11e-06, 1.31e+00 - 1800, 3, 9.77e-02, 9.65e-07, 4.02e-01 - 2000, 1, 2.55e-01, 1.37e-06, 7.45e-01 - 2000, 2, 6.68e-01, 2.00e-06, 1.33e+00 - 2000, 3, 9.25e-02, 9.02e-07, 4.07e-01 \ No newline at end of file + :lines: 75-86 + +.. code-block:: console + + Solver state: Converged + accepted: 20 + function_calls: 40 + jacobian_updates: 20 + number_of_steps: 20 + accepted: 20 + rejected: 0 + decompositions: 20 + solves: 60 + singular: 0 + +To get the total accumulated time of each function call, you need to specify the templated boolean argument to turn the timing on. +We can also record the total runtime of the ``Solve`` function. + +.. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp + :language: cpp + :lines: 88-96 + +.. code-block:: console + + Total solve time: 24416 nanoseconds + total_forcing_time: 3167 nanoseconds + total_jacobian_time: 1710 nanoseconds + total_linear_factor_time: 4584 nanoseconds + total_linear_solve_time: 3290 nanoseconds + +.. note:: + Your systems clock may not report the same values depending on how accurate your system clock is. \ No newline at end of file diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index ce4d56531..f8f5e0e14 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -14,7 +14,7 @@ If you happen to find our examples are lacking for your needs, please, :maxdepth: 1 :caption: Contents: - but_how_fast_is_it rate_constant_tutorial user_defined_rate_constant_tutorial multiple_grid_cells + but_how_fast_is_it diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index affa50370..fad379736 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -129,12 +129,19 @@ namespace micm /// @brief The final state the solver was in after the Solve function finishes enum class SolverState { + /// @brief This is the initial value at the start of the Solve function NotYetCalled, + /// @brief This is only used for control flow in the Solve function Running, + /// @brief A successful integration will have this value Converged, + /// @brief If the number of steps exceeds the maximum value on the solver parameter, this value will be returned ConvergenceExceededMaxSteps, + /// @brief Very stiff systems will likely result in a step size that is not useable for the solver StepSizeTooSmall, + /// @brief Matrices that are singular more than once will set this value. At present, this should never be returned RepeatedlySingularMatrix, + /// @brief Mostly this value is returned by systems that tend toward chemical explosions NaNDetected }; @@ -149,21 +156,32 @@ namespace micm public: struct SolverStats { - uint64_t function_calls{}; // Nfun - uint64_t jacobian_updates{}; // Njac - uint64_t number_of_steps{}; // Nstp - uint64_t accepted{}; // Nacc - uint64_t rejected{}; // Nrej - uint64_t decompositions{}; // Ndec - uint64_t solves{}; // Nsol - uint64_t singular{}; // Nsng - uint64_t total_steps{}; // Ntotstp - + /// @brief The number of forcing function calls + uint64_t function_calls{}; + /// @brief The number of jacobian function calls + uint64_t jacobian_updates{}; + /// @brief The total number of internal time steps taken + uint64_t number_of_steps{}; + /// @brief The number of accepted integrations + uint64_t accepted{}; + /// @brief The number of rejected integrations + uint64_t rejected{}; + /// @brief The number of LU decompositions + uint64_t decompositions{}; + /// @brief The number of linear solvers + uint64_t solves{}; + /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix is never singular + uint64_t singular{}; + /// @brief The cumulative amount of time spent calculating the forcing function std::chrono::duration total_forcing_time{}; + /// @brief The cumulative amount of time spent calculating the jacobian std::chrono::duration total_jacobian_time{}; + /// @brief The cumulative amount of time spent calculating the linear factorization std::chrono::duration total_linear_factor_time{}; + /// @brief The cumulative amount of time spent calculating the linear solve std::chrono::duration total_linear_solve_time{}; + /// @brief Set all member variables to zero void Reset(); }; @@ -171,7 +189,7 @@ namespace micm { /// @brief The new state computed by the solver MatrixPolicy result_{}; - /// @brief The finals state the solver was in + /// @brief The final state the solver was in SolverState state_ = SolverState::NotYetCalled; /// @brief A collection of runtime state for this call of the solver SolverStats stats_{}; diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 6b3e1b381..651c538b2 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -4,9 +4,9 @@ #define TIMED_METHOD(assigned_increment, time_it, method, ...) \ { \ if constexpr (time_it) { \ - auto start = std::chrono::steady_clock::now(); \ + auto start = std::chrono::high_resolution_clock::now(); \ method(__VA_ARGS__); \ - auto end = std::chrono::steady_clock::now(); \ + auto end = std::chrono::high_resolution_clock::now(); \ assigned_increment += std::chrono::duration_cast(end - start); \ } else { \ method(__VA_ARGS__); \ @@ -389,7 +389,6 @@ namespace micm decompositions = 0; solves = 0; singular = 0; - total_steps = 0; total_forcing_time = std::chrono::nanoseconds::zero(); total_jacobian_time = std::chrono::nanoseconds::zero(); total_linear_factor_time = std::chrono::nanoseconds::zero(); @@ -608,10 +607,9 @@ namespace micm parameters_.safety_factor_ / std::pow(error, 1 / parameters_.estimator_of_local_order_))); double Hnew = H * fac; - // Check the error magnitude and adjust step size stats_.number_of_steps += 1; - stats_.total_steps += 1; + // Check the error magnitude and adjust step size if (std::isnan(error)) { Y.AsVector().assign(Ynew.AsVector().begin(), Ynew.AsVector().end()); diff --git a/test/tutorial/test_but_how_fast_is_it.cpp b/test/tutorial/test_but_how_fast_is_it.cpp index 851e268dd..0cb133a83 100644 --- a/test/tutorial/test_but_how_fast_is_it.cpp +++ b/test/tutorial/test_but_how_fast_is_it.cpp @@ -1,8 +1,8 @@ #include #include - #include #include +#include // Use our namespace so that this example is easier to read using namespace micm; @@ -13,80 +13,41 @@ using namespace micm; template using SparseMatrixPolicy = SparseMatrix; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(5) << "grid" - << "," << std::setw(10) << "A" - << "," << std::setw(10) << "B" - << "," << std::setw(10) << "C" << std::endl; -} - -template class T> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "1," << std::setw(10) - << state.variables_[0][state.variable_map_["A"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "2," << std::setw(10) - << state.variables_[1][state.variable_map_["A"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "3," << std::setw(10) - << state.variables_[2][state.variable_map_["A"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); -} - int main() { - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ b, b }) - .products({ yields(b, 1), yields(c, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b, c }) - .products({ yields(a, 1), yields(c, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) - }; + auto a = Species("A"); + auto b = Species("B"); + auto c = Species("C"); + + Phase gas_phase{ std::vector{ a, b, c } }; + + Process r1 = Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + Process r2 = Process::create() + .reactants({ b, b }) + .products({ yields(b, 1), yields(c, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + Process r3 = Process::create() + .reactants({ b, c }) + .products({ yields(a, 1), yields(c, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + RosenbrockSolver solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters( + 3, false) }; State state = solver.GetState(); // mol m-3 - state.SetConcentration(a, std::vector{1, 2, 0.5 }); + state.SetConcentration(a, std::vector{ 1, 2, 0.5 }); state.SetConcentration(b, std::vector(3, 0)); state.SetConcentration(c, std::vector(3, 0)); @@ -97,11 +58,12 @@ int main() state.SetCustomRateParameter("r2", std::vector(3, k2)); state.SetCustomRateParameter("r3", std::vector(3, k3)); - double temperature = 272.5; // [K] - double pressure = 101253.3; // [Pa] - double air_density = 1e6; // [mol m-3] + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] - for(size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) { + for (size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) + { state.conditions_[cell].temperature_ = temperature; state.conditions_[cell].pressure_ = pressure; state.conditions_[cell].air_density_ = air_density; @@ -110,25 +72,26 @@ int main() // choose a timestep and print the initial state double time_step = 200; // s - print_header(); - print_state(0, state); - - // solve for ten iterations - for (int i = 0; i < 10; ++i) - { - // Depending on how stiff the system is - // the solver integration step may not be able to solve for the full time step - // so we need to track how much time the solver was able to integrate for and continue - // solving until we finish - double elapsed_solve_time = 0; - - while (elapsed_solve_time < time_step) - { - auto result = solver.Solve(time_step - elapsed_solve_time, state); - elapsed_solve_time = result.final_time_; - state.variables_ = result.result_; - } - - print_state(time_step * (i + 1), state); - } + auto result = solver.Solve(time_step, state); + std::cout << "Solver state: " << StateToString(result.state_) << std::endl; + std::cout << "accepted: " << result.stats_.accepted << std::endl; + std::cout << "function_calls: " << result.stats_.function_calls << std::endl; + std::cout << "jacobian_updates: " << result.stats_.jacobian_updates << std::endl; + std::cout << "number_of_steps: " << result.stats_.number_of_steps << std::endl; + std::cout << "accepted: " << result.stats_.accepted << std::endl; + std::cout << "rejected: " << result.stats_.rejected << std::endl; + std::cout << "decompositions: " << result.stats_.decompositions << std::endl; + std::cout << "solves: " << result.stats_.solves << std::endl; + std::cout << "singular: " << result.stats_.singular << std::endl; + std::cout << "final simulation time: " << result.final_time_ << std::endl; + + auto start = std::chrono::high_resolution_clock::now(); + result = solver.Solve(time_step, state); + auto end = std::chrono::high_resolution_clock::now(); + auto solve_time = std::chrono::duration_cast(end - start); + std::cout << "Total solve time: " << solve_time.count() << " nanoseconds" << std::endl; + std::cout << "total_forcing_time: " << result.stats_.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "total_jacobian_time: " << result.stats_.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_factor_time: " << result.stats_.total_linear_factor_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_solve_time: " << result.stats_.total_linear_solve_time.count() << " nanoseconds" << std::endl; } \ No newline at end of file From eeea93cf44f579b94c17c0a427e7bae701d70575 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 14:05:54 -0500 Subject: [PATCH 056/318] better representation --- docs/source/user_guide/but_how_fast_is_it.rst | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 docs/source/user_guide/but_how_fast_is_it.rst diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst new file mode 100644 index 000000000..9db5a6166 --- /dev/null +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -0,0 +1,90 @@ +.. _But how fast is it: + +But how fast is it? +=================== + +This tutorial will focus on timing the solver to show how you can measure performance. +We will use a simple 3-reaction 3-species mechanism. The setup here is the same in +:ref:`Multiple grid cells`. To understand the full setup, read that tutorial. Otherwise, +we assume that configuring a rosenbrock solver is understood and instead we will focus on timing +the solver. + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ + +If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +Up until now we have neglected to talk about what the solver returns, which is a :cpp:class:`micm::RosenbrockSolver::SolverResult`. + +There are four values returned. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::final_time_` + + * This is the final simulation time achieved by the solver. The :cpp:func:`micm::RosenbrockSolver::Solve` function attempts to integrate the passed in state forward a set number of seconds. Often, the solver is able to complete the integration. However, extremely stiff systems may only solve for a fraction of the time. It is imperative that the ``final_time_`` value is checked. If it is not equal to the amount of time you intended to solve for, call solve again as we do in the tutorials with the difference between what was solved and how long you intended to solve. + + .. note:: + This does **not** represent the amount of time taken by the solve routine. You must measure that yourself(shown below). The ``final_time_`` is simulation time. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::result_` + + * This contains the integrated state value; the concentrations reached at the end of Solve function after the amount of time specified by ``final_time_``. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::state_` + + * There are many possible reasons for the solver to return. This value is one of the possible enum values define on the :cpp:enum:`micm::SolverState`. Hopefully, you receive a :cpp:enumerator:`micm::SolverState::Converged` state. But, it is good to always check this to ensure the solver really did converge. You can print this value using the :cpp:func:`micm::StateToString` function. + +#. :cpp:member:`micm::RosenbrockSolver::SolverResult::stats_` + + * This is an instance of a :cpp:class:`micm::RosenbrockSolver::SolverStats` struct which contains information about the number of function calls and optionally the total cumulative amount of time spent calling each function. For the time to be collected, you must call the ``Solve`` function with a ``true`` templated parameter. Please see the example below. + +First, let's run the simulation but without collecting the solve time. We'll inspect the solver state and look at what's collected +in the stats object. + +.. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp + :language: cpp + :lines: 75-86 + +.. code-block:: console + + Solver state: Converged + accepted: 20 + function_calls: 40 + jacobian_updates: 20 + number_of_steps: 20 + accepted: 20 + rejected: 0 + decompositions: 20 + solves: 60 + singular: 0 + +To get the total accumulated time of each function call, you need to specify the templated boolean argument to turn the timing on. +We can also record the total runtime of the ``Solve`` function. + +.. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp + :language: cpp + :lines: 88-96 + +.. code-block:: console + + Total solve time: 24416 nanoseconds + total_forcing_time: 3167 nanoseconds + total_jacobian_time: 1710 nanoseconds + total_linear_factor_time: 4584 nanoseconds + total_linear_solve_time: 3290 nanoseconds + +.. note:: + Your systems clock may not report the same granularity depending on how accurate your system clock is. \ No newline at end of file From 56ed6e7f32a485bd36ed11a0f4b3bcd66f1c0304 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 14:16:26 -0500 Subject: [PATCH 057/318] added moar detail --- docs/source/user_guide/but_how_fast_is_it.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst index 9db5a6166..85cbfbfc9 100644 --- a/docs/source/user_guide/but_how_fast_is_it.rst +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -72,7 +72,9 @@ in the stats object. singular: 0 To get the total accumulated time of each function call, you need to specify the templated boolean argument to turn the timing on. -We can also record the total runtime of the ``Solve`` function. +We can also record the total runtime of the ``Solve`` function. Through the magic of templates, the timing information is only +collected when you use the ``true`` version of the templated function. These values are not even computed, meaning no CPU cycles are +wasted, for the ``false`` version. .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp :language: cpp From 69d97f6becbb6763f33d92af2e6b1717dfa986ec Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 16:14:32 -0500 Subject: [PATCH 058/318] wrote the tutorial code for solver configurations --- docs/source/user_guide/index.rst | 1 + .../user_guide/solver_configurations.rst | 31 +++ test/tutorial/CMakeLists.txt | 1 + test/tutorial/test_solver_configuration.cpp | 188 ++++++++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 docs/source/user_guide/solver_configurations.rst create mode 100644 test/tutorial/test_solver_configuration.cpp diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index e579327ff..3dff99d64 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -17,3 +17,4 @@ If you happen to find our examples are lacking for your needs, please, rate_constant_tutorial user_defined_rate_constant_tutorial multiple_grid_cells + solver_configurations diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst new file mode 100644 index 000000000..917cfd7f6 --- /dev/null +++ b/docs/source/user_guide/solver_configurations.rst @@ -0,0 +1,31 @@ +.. _Solver configurations: + +Solver configurations +===================== + +This tutorial will focus on configuring the solver with different stages. +We will use a simple 3-reaction 3-species mechanism. The setup here is the same in +:ref:`Multiple grid cells`, except only one grid cell is used. + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ + +If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. + +.. tabs:: + + .. tab:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +.. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp + :language: cpp + :lines: 88-96 diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index a65cec863..4cac7925d 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -11,6 +11,7 @@ create_standard_test(NAME README_example SOURCES test_README_example.cpp) create_standard_test(NAME multiple_grid_cells SOURCES test_multiple_grid_cells.cpp) create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) create_standard_test(NAME rate_constants_user_defined_example_by_hand SOURCES test_rate_constants_user_defined_by_hand.cpp) +create_standard_test(NAME solver_configuration SOURCES test_solver_configuration.cpp) if(ENABLE_JSON) create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) create_standard_test(NAME rate_constants_user_defined_example_with_config SOURCES test_rate_constants_user_defined_with_config.cpp) diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp new file mode 100644 index 000000000..533a7eb64 --- /dev/null +++ b/test/tutorial/test_solver_configuration.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +// The Rosenbrock solver can use many matrix ordering types +// Here, we use the default ordering, but we still need to provide a templated +// Arguent to the solver so it can use the proper ordering with any data type +template +using SparseMatrixPolicy = SparseMatrix; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(10) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" << std::endl; +} + +template class T> +void print_state(double time, State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) + << std::setw(10) << state.variables_[0][state.variable_map_["A"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["C"]] + << std::endl; + + std::cout.copyfmt(oldState); +} + +template +void test_solver_type(T solver) +{ + State state = solver.GetState(); + + // mol m-3 + state.variables_[0] = {1, 0, 0}; + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("r1", k1); + state.SetCustomRateParameter("r2", k2); + state.SetCustomRateParameter("r3", k3); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + // choose a timestep and print the initial state + double time_step = 200; // s + + print_header(); + print_state(0, state); + + typename T::SolverStats total_stats; + std::chrono::duration total_solve_time = std::chrono::nanoseconds::zero();; + + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto start = std::chrono::high_resolution_clock::now(); + auto result = solver.template Solve(time_step - elapsed_solve_time, state); + auto end = std::chrono::high_resolution_clock::now(); + + total_solve_time += std::chrono::duration_cast(end - start); + total_stats.function_calls += result.stats_.function_calls; + total_stats.jacobian_updates += result.stats_.jacobian_updates; + total_stats.number_of_steps += result.stats_.number_of_steps; + total_stats.accepted += result.stats_.accepted; + total_stats.rejected += result.stats_.rejected; + total_stats.decompositions += result.stats_.decompositions; + total_stats.solves += result.stats_.solves; + total_stats.singular += result.stats_.singular; + total_stats.total_steps += result.stats_.total_steps; + total_stats.total_forcing_time += result.stats_.total_forcing_time; + total_stats.total_jacobian_time += result.stats_.total_jacobian_time; + total_stats.total_linear_factor_time += result.stats_.total_linear_factor_time; + total_stats.total_linear_solve_time += result.stats_.total_linear_solve_time; + + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + + print_state(time_step * (i + 1), state); + } + std::cout << "Total solve time: " << total_solve_time.count() << " nanoseconds" << std::endl; + std::cout << "accepted: " << total_stats.accepted << std::endl; + std::cout << "function_calls: " << total_stats.function_calls << std::endl; + std::cout << "jacobian_updates: " << total_stats.jacobian_updates << std::endl; + std::cout << "number_of_steps: " << total_stats.number_of_steps << std::endl; + std::cout << "accepted: " << total_stats.accepted << std::endl; + std::cout << "rejected: " << total_stats.rejected << std::endl; + std::cout << "decompositions: " << total_stats.decompositions << std::endl; + std::cout << "solves: " << total_stats.solves << std::endl; + std::cout << "singular: " << total_stats.singular << std::endl; + std::cout << "total_forcing_time: " << total_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "total_jacobian_time: " << total_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_factor_time: " << total_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_solve_time: " << total_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; +} + +int main() +{ + auto a = Species("A"); + auto b = Species("B"); + auto c = Species("C"); + + Phase gas_phase{ std::vector{ a, b, c } }; + + Process r1 = Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + Process r2 = Process::create() + .reactants({ b, b }) + .products({ yields(b, 1), yields(c, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + Process r3 = Process::create() + .reactants({ b, c }) + .products({ yields(a, 1), yields(c, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + auto system = System(SystemParameters{ .gas_phase_ = gas_phase }); + auto reactions = std::vector{ r1, r2, r3 }; + + RosenbrockSolver two_stage{ + system, reactions, RosenbrockSolverParameters::two_stage_rosenbrock_parameters() + }; + + RosenbrockSolver three_stage{ + system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + RosenbrockSolver four_stage{ + system, reactions, RosenbrockSolverParameters::four_stage_rosenbrock_parameters() + }; + + RosenbrockSolver four_stage_da{ + system, reactions, RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters() + }; + + RosenbrockSolver six_stage_da{ + system, reactions, RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters() + }; + + std::cout << "Two stages: " << std::endl; + test_solver_type(two_stage); + + std::cout << std::endl << "Three stages: " << std::endl; + test_solver_type(three_stage); + + std::cout << std::endl << "Four stages: " << std::endl; + test_solver_type(four_stage); + + std::cout << std::endl << "Four stages differential algebraic: " << std::endl; + test_solver_type(four_stage_da); + + std::cout << std::endl << "Six stages differential algebraic: " << std::endl; + test_solver_type(six_stage_da); +} \ No newline at end of file From 0d3a86dd2c05c124b7a93d252fea6621a5070f2d Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 16:34:07 -0500 Subject: [PATCH 059/318] added bibliography --- docs/requirements.txt | 3 +- .../citing_and_bibliography/bibliography.rst | 7 + .../citation.rst | 0 docs/source/citing_and_bibliography/index.rst | 10 ++ docs/source/conf.py | 4 + docs/source/getting_started.rst | 23 +-- docs/source/index.rst | 2 +- docs/source/references.bib | 154 ++++++++++++++++++ .../user_guide/solver_configurations.rst | 3 + 9 files changed, 193 insertions(+), 13 deletions(-) create mode 100644 docs/source/citing_and_bibliography/bibliography.rst rename docs/source/{ => citing_and_bibliography}/citation.rst (100%) create mode 100644 docs/source/citing_and_bibliography/index.rst create mode 100644 docs/source/references.bib diff --git a/docs/requirements.txt b/docs/requirements.txt index 46798caed..54661a4bb 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,4 +3,5 @@ sphinx sphinx-book-theme sphinx-copybutton sphinx-design -sphinx-tabs \ No newline at end of file +sphinx-tabs +sphinxcontrib-bibtex \ No newline at end of file diff --git a/docs/source/citing_and_bibliography/bibliography.rst b/docs/source/citing_and_bibliography/bibliography.rst new file mode 100644 index 000000000..a066e9074 --- /dev/null +++ b/docs/source/citing_and_bibliography/bibliography.rst @@ -0,0 +1,7 @@ + +############ +Bibliography +############ + +.. bibliography:: + :all: \ No newline at end of file diff --git a/docs/source/citation.rst b/docs/source/citing_and_bibliography/citation.rst similarity index 100% rename from docs/source/citation.rst rename to docs/source/citing_and_bibliography/citation.rst diff --git a/docs/source/citing_and_bibliography/index.rst b/docs/source/citing_and_bibliography/index.rst new file mode 100644 index 000000000..a8b5c7a57 --- /dev/null +++ b/docs/source/citing_and_bibliography/index.rst @@ -0,0 +1,10 @@ + +Citations and Bibliography +========================== + +.. toctree:: + :maxdepth: 1 + :caption: Contents: + + citation + bibliography diff --git a/docs/source/conf.py b/docs/source/conf.py index 8de8f8d9d..b6bd108b6 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -35,8 +35,12 @@ 'sphinx_copybutton', 'sphinx_design', 'sphinx_tabs.tabs', + 'sphinxcontrib.bibtex', ] +bibtex_bibfiles = ['references.bib'] +suppress_warnings = ["bibtex.missing_field"] + breathe_default_project = "micm" # Add any paths that contain templates here, relative to this directory. diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 695731edd..5f323e52e 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -1,12 +1,12 @@ - +############### Getting Started -=============== +############### Build and Test --------------- +============== CPU -~~~ +--- To build and install MICM locally, you must have the following libraries installed: - [sphinx](https://github.com/sphinx-doc/sphinx) @@ -31,7 +31,7 @@ CMake will allow for setting options such as the installation directory with CMAKE_INSTALL_PREFIX, or various build flags such as BUILD_DOCS, ENABLE_CUDA, etc. Docker Container -~~~~~~~~~~~~~~~~ +---------------- Build and run the image:: @@ -41,19 +41,20 @@ Build and run the image:: If you would like, you can ssh into a running docker container and edit the files there. GPU -~~~ +--- NCAR Hardware -------------- +^^^^^^^^^^^^^ On Cheyenne -~~~~~~~~~~~ +^^^^^^^^^^^ On Casper -~~~~~~~~~ +^^^^^^^^^ On Gust and Derecho -~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^ + To compile and test on gust:: $ qinteractive -A NTDD0005 --ngpus=1 @@ -64,7 +65,7 @@ To compile and test on gust:: $ make test NOAA Hardware -------------- +^^^^^^^^^^^^^ Run an Example -------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 8b7c691f9..363ca9a50 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -52,7 +52,7 @@ Welcome to MICM's documentation! user_guide/index api/index contributing/index - citation + citing_and_bibliography/index Indices and tables ================== diff --git a/docs/source/references.bib b/docs/source/references.bib new file mode 100644 index 000000000..dfb3776ed --- /dev/null +++ b/docs/source/references.bib @@ -0,0 +1,154 @@ +@article{daescuDirectAdjointSensitivity2003, + title = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}: {{II}}\textemdash Numerical Validation and Applications}, + shorttitle = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}}, + author = {Daescu, Dacian N. and Sandu, Adrian and Carmichael, Gregory R.}, + year = {2003}, + month = nov, + journal = {Atmospheric Environment}, + volume = {37}, + number = {36}, + pages = {5097--5114}, + issn = {1352-2310}, + doi = {10.1016/j.atmosenv.2003.08.020}, + urldate = {2023-02-15}, + abstract = {The Kinetic PreProcessor KPP was extended to generate the building blocks needed for the direct and adjoint sensitivity analysis of chemical kinetic systems. An overview of the theoretical aspects of sensitivity calculations and a discussion of the KPP software tools is presented in the companion paper. In this work the correctness and efficiency of the KPP generated code for direct and adjoint sensitivity studies are analyzed through an extensive set of numerical experiments. Direct-decoupled Rosenbrock methods are shown to be cost-effective for providing sensitivities at low and medium accuracies. A validation of the discrete\textendash adjoint evaluated gradients is performed against the finite difference estimates. The accuracy of the adjoint gradients is measured using a reference gradient value obtained with a standard direct-decoupled method. The accuracy is studied for both constant step size and variable step size integration of the forward/adjoint model and the consistency between the discrete and continuous adjoint models is analyzed. Applications of the KPP-1.2 software package to direct and adjoint sensitivity studies, variational data assimilation, and parameter identification are considered for the comprehensive chemical mechanism SAPRC-99.}, + langid = {english}, + keywords = {Data assimilation,Optimization,Parameter identification,Sensitivity analysis}, + file = {/Users/kshores/Zotero/storage/UC6A2K82/S1352231003006733.html} +} + +@article{damianKineticPreprocessorKPPa2002, + title = {The Kinetic Preprocessor {{KPP-a}} Software Environment for Solving Chemical Kinetics}, + author = {Damian, Valeriu and Sandu, Adrian and Damian, Mirela and Potra, Florian and Carmichael, Gregory R.}, + year = {2002}, + month = nov, + journal = {Computers \& Chemical Engineering}, + volume = {26}, + number = {11}, + pages = {1567--1579}, + issn = {0098-1354}, + doi = {10.1016/S0098-1354(02)00128-X}, + urldate = {2023-02-15}, + abstract = {The kinetic preprocessor (KPP) is a software tool that assists the computer simulation of chemical kinetic systems. The concentrations of a chemical system evolve in time according to the differential law of mass action kinetics. A computer simulation requires the implementation of the differential system and its numerical integration in time. KPP translates a specification of the chemical mechanism into fortran or c simulation code that implement the concentration time derivative function and its Jacobian, together with a suitable numerical integration scheme. Sparsity in Jacobian is carefully exploited in order to obtain computational efficiency. KPP incorporates a library with several widely used atmospheric chemistry mechanisms and users can add their own chemical mechanisms to the library. KPP also includes a comprehensive suite of stiff numerical integrators. The KPP development environment is designed in a modular fashion and allows for rapid prototyping of new chemical kinetic schemes as well as new numerical integration methods.}, + langid = {english}, + keywords = {Automatic code generation,Chemical kinetics,Numerical integration,Sparsity}, + file = {/Users/kshores/Zotero/storage/2VJ5XQH2/Damian et al. - 2002 - The kinetic preprocessor KPP-a software environmen.pdf;/Users/kshores/Zotero/storage/YQSFJ5YJ/S009813540200128X.html} +} + +@book{hairerSolvingOrdinaryDifferential1996a, + title = {Solving {{Ordinary Differential Equations II}}: {{Stiff}} and {{Differential-Algebraic Problems}}}, + shorttitle = {Solving {{Ordinary Differential Equations II}}}, + author = {Hairer, Ernst and Wanner, Gerhard}, + year = {1996}, + month = sep, + edition = {2nd edition}, + publisher = {{Springer}}, + address = {{Berlin ; New York}}, + abstract = {The subject of this book is the solution of stiff differential equations and of differential-algebraic systems. This second edition contains new material including new numerical tests, recent progress in numerical differential-algebraic equations, and improved FORTRAN codes.From the reviews:"A superb book...Throughout, illuminating graphics, sketches and quotes from papers of researchers in the field add an element of easy informality and motivate the text." --MATHEMATICS TODAY}, + isbn = {978-3-540-60452-5}, + langid = {english}, + file = {/Users/kshores/Zotero/storage/GC4V85EX/Solving Ordinary Differential Equations II.pdf} +} + +@book{hairerSolvingOrdinaryDifferential2009, + title = {Solving {{Ordinary Differential Equations I}}: {{Nonstiff Problems}}}, + shorttitle = {Solving {{Ordinary Differential Equations I}}}, + author = {Hairer, Ernst and N{\o}rsett, Syvert P. and Wanner, Gerhard}, + year = {2009}, + month = dec, + edition = {2nd ed. 1993. Corr. 3rd printing 2009 edition}, + publisher = {{Springer}}, + address = {{Heidelberg ; London}}, + abstract = {This book deals with methods for solving nonstiff ordinary differential equations. The first chapter describes the historical development of the classical theory, and the second chapter includes a modern treatment of Runge-Kutta and extrapolation methods. Chapter three begins with the classical theory of multistep methods, and concludes with the theory of general linear methods. The reader will benefit from many illustrations, a historical and didactic approach, and computer programs which help him/her learn to solve all kinds of ordinary differential equations. This new edition has been rewritten and new material has been included.}, + isbn = {978-3-642-05163-0}, + langid = {english}, + file = {/Users/kshores/Zotero/storage/2AJ3I6HJ/1993 - Solving Ordinary Differential Equations I.pdf} +} + +@article{rosenbrockGeneralImplicitProcesses1963, + title = {Some General Implicit Processes for the Numerical Solution of Differential Equations}, + author = {Rosenbrock, H. H.}, + year = {1963}, + month = jan, + journal = {The Computer Journal}, + volume = {5}, + number = {4}, + pages = {329--330}, + issn = {0010-4620}, + doi = {10.1093/comjnl/5.4.329}, + urldate = {2023-02-15}, + abstract = {Some general implicit processes are given for the solution of simultaneous first-order differential equations. These processes, which use successive substitution, are implicit analogues of the (explicit) Runge-Kutta processes. They require the solution in each time step of one or more set of simultaneous linear equations, usually of a special and simple form.Processes of any required order can be devised, and they can be made to have a wide margin of stability when applied to a linear problem.}, + file = {/Users/kshores/Zotero/storage/4NW8TWI5/Rosenbrock - 1963 - Some general implicit processes for the numerical .pdf;/Users/kshores/Zotero/storage/865XYEWP/316388.html} +} + +@article{sanduBenchmarkingStiffOde1997, + title = {Benchmarking Stiff Ode Solvers for Atmospheric Chemistry Problems {{II}}: {{Rosenbrock}} Solvers}, + shorttitle = {Benchmarking Stiff Ode Solvers for Atmospheric Chemistry Problems {{II}}}, + author = {Sandu, A. and Verwer, J. G. and Blom, J. G. and Spee, E. J. and Carmichael, G. R. and Potra, F. A.}, + year = {1997}, + month = oct, + journal = {Atmospheric Environment}, + volume = {31}, + number = {20}, + pages = {3459--3472}, + issn = {1352-2310}, + doi = {10.1016/S1352-2310(97)83212-8}, + urldate = {2023-02-15}, + abstract = {In the numerical simulation of atmospheric transport-chemistry processes, a major task is the integration of the stiff systems of ordinary differential equations describing the chemical transformations. It is therefore of interest to systematically search for stiff solvers which can be identified as close to optimal for atmospheric applications. In this paper we continue our investigation from Sandu et al. (1996, CWI Report NM-R9603 and Report in Comput. Math., No. 85) and compare eight solvers on a set of seven box-models used in present day models. The focus is on Rosenbrock solvers. These turn out to be very well suited for our application when they are provided with highly efficient sparse matrix techniques to economize on the linear algebra. Two of the Rosenbrock solvers tested are from the literature, viz. rodas and Ros4, and two are new and specially developed for air quality applications, viz. rodas3 and ros3.}, + langid = {english}, + file = {/Users/kshores/Zotero/storage/5JMBVP79/Sandu et al. - 1997 - Benchmarking stiff ode solvers for atmospheric che.pdf;/Users/kshores/Zotero/storage/39YHBE2Z/S1352231097832128.html} +} + +@article{sanduBenchmarkingStiffOde1997a, + title = {Benchmarking Stiff Ode Solvers for Atmospheric Chemistry Problems-{{I}}. Implicit vs Explicit}, + author = {Sandu, A. and Verwer, J. G. and Van Loon, M. and Carmichael, G. R. and Potra, F. A. and Dabdub, D. and Seinfeld, J. H.}, + year = {1997}, + month = oct, + journal = {Atmospheric Environment}, + series = {{{EUMAC}}: {{European Modelling}} of {{Atmospheric Constituents}}}, + volume = {31}, + number = {19}, + pages = {3151--3166}, + issn = {1352-2310}, + doi = {10.1016/S1352-2310(97)00059-9}, + urldate = {2023-02-15}, + abstract = {In many applications of atmospheric transport-chemistry problems, a major task is the numerical integration of the stiff systems of ordinary differential equations describing the chemical transformations. This paper presents a comprehensive numerical comparison between five dedicated explicit and four implicit solvers for a set of seven benchmark problems from actual applications. The implicit solvers use sparse matrix techniques to economize on the numerical linear algebra overhead. As a result they are often more efficient than the dedicated explicit ones, particularly when approximately two or more figures of accuracy are required. In most test cases, sparse RODAs, a Rosenbrock solver, came out as most competitive in the 1\% error region. Of the dedicated explicit solvers, TWOSTEP came out as best. When less than 1\% accuracy is aimed at, this solver performs very efficiently for tropospheric gas-phase problems. However, like all other dedicated explicit solvers, it cannot efficiently deal with gas-liquid phase chemistry. The results presented may constitute a guide for atmospheric modelers to select a suitable integrator based on the type and dimension of their chemical mechanism and on the desired level of accuracy. Furthermore, we would like to consider this paper an open invitation for other groups to add new representative test problems to those described here and to benchmark their numerical algorithms in our standard computational environment.}, + langid = {english}, + file = {/Users/kshores/Zotero/storage/9SETTXQS/Sandu et al. - 1997 - Benchmarking stiff ode solvers for atmospheric che.pdf;/Users/kshores/Zotero/storage/3IF3VR72/S1352231097000599.html} +} + +@article{sanduDirectAdjointSensitivity2003, + title = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}: {{Part I}}\textemdash Theory and Software Tools}, + shorttitle = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}}, + author = {Sandu, Adrian and Daescu, Dacian N. and Carmichael, Gregory R.}, + year = {2003}, + month = nov, + journal = {Atmospheric Environment}, + volume = {37}, + number = {36}, + pages = {5083--5096}, + issn = {1352-2310}, + doi = {10.1016/j.atmosenv.2003.08.019}, + urldate = {2023-02-15}, + abstract = {The analysis of comprehensive chemical reactions mechanisms, parameter estimation techniques, and variational chemical data assimilation applications require the development of efficient sensitivity methods for chemical kinetics systems. The new release (KPP-1.2) of the kinetic preprocessor (KPP) contains software tools that facilitate direct and adjoint sensitivity analysis. The direct-decoupled method, built using BDF formulas, has been the method of choice for direct sensitivity studies. In this work, we extend the direct-decoupled approach to Rosenbrock stiff integration methods. The need for Jacobian derivatives prevented Rosenbrock methods to be used extensively in direct sensitivity calculations; however, the new automatic and symbolic differentiation technologies make the computation of these derivatives feasible. The direct-decoupled method is known to be efficient for computing the sensitivities of a large number of output parameters with respect to a small number of input parameters. The adjoint modeling is presented as an efficient tool to evaluate the sensitivity of a scalar response function with respect to the initial conditions and model parameters. In addition, sensitivity with respect to time-dependent model parameters may be obtained through a single backward integration of the adjoint model. KPP software may be used to completely generate the continuous and discrete adjoint models taking full advantage of the sparsity of the chemical mechanism. Flexible direct-decoupled and adjoint sensitivity code implementations are achieved with minimal user intervention. In a companion paper, we present an extensive set of numerical experiments that validate the KPP software tools for several direct/adjoint sensitivity applications, and demonstrate the efficiency of KPP-generated sensitivity code implementations.}, + langid = {english}, + keywords = {Adjoint model,Chemical kinetics,Direct-decoupled method,Sensitivity analysis}, + file = {/Users/kshores/Zotero/storage/Q2BN8Z5E/Sandu et al. - 2003 - Direct and adjoint sensitivity analysis of chemica.pdf;/Users/kshores/Zotero/storage/B26KZ24Z/S1352231003006721.html} +} + +@article{sanduEfficientImplementationFully1996, + title = {Efficient {{Implementation}} of {{Fully Implicit Methods}} for {{Atmospheric Chemical Kinetics}}}, + author = {Sandu, A. and Potra, F. A. and Carmichael, G. R. and Damian, V.}, + year = {1996}, + month = nov, + journal = {Journal of Computational Physics}, + volume = {129}, + number = {1}, + pages = {101--110}, + issn = {0021-9991}, + doi = {10.1006/jcph.1996.0236}, + urldate = {2023-02-15}, + abstract = {Implicit integrators are very useful in efficiently solving stiff systems of ODEs arising from atmospheric chemistry kinetics, provided that they are modified to take full advantage of the structure of the problem. A systematic way of treating sparsity for reducing the linear algebra cost is presented.}, + langid = {english}, + file = {/Users/kshores/Zotero/storage/XFN28QLI/Sandu et al. - 1996 - Efficient Implementation of Fully Implicit Methods.pdf;/Users/kshores/Zotero/storage/43WPJN5Y/S0021999196902363.html} +} diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index 917cfd7f6..224205a0a 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -26,6 +26,9 @@ the appropriate tab below and be on your way! Otherwise, stick around for a line Line-by-line explanation ------------------------ +There are a total of six different configurations for the Rosenbrock solver. Each corresponds to a different number of stages. +What each configuration means is beyond the scope of this tutorial. However, more information can be found in . + .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp :lines: 88-96 From 971bd67f4554fc01928ac5fc76548cb0f6abae23 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 2 Oct 2023 16:52:15 -0500 Subject: [PATCH 060/318] chaning reference identifiers --- docs/source/citing_and_bibliography/index.rst | 3 ++- docs/source/references.bib | 18 +++++++++--------- .../user_guide/solver_configurations.rst | 2 ++ 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/source/citing_and_bibliography/index.rst b/docs/source/citing_and_bibliography/index.rst index a8b5c7a57..ac5d3a951 100644 --- a/docs/source/citing_and_bibliography/index.rst +++ b/docs/source/citing_and_bibliography/index.rst @@ -1,6 +1,7 @@ +########################## Citations and Bibliography -========================== +########################## .. toctree:: :maxdepth: 1 diff --git a/docs/source/references.bib b/docs/source/references.bib index dfb3776ed..5e848eb60 100644 --- a/docs/source/references.bib +++ b/docs/source/references.bib @@ -1,4 +1,4 @@ -@article{daescuDirectAdjointSensitivity2003, +@article{Daescu2003, title = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}: {{II}}\textemdash Numerical Validation and Applications}, shorttitle = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}}, author = {Daescu, Dacian N. and Sandu, Adrian and Carmichael, Gregory R.}, @@ -17,7 +17,7 @@ @article{daescuDirectAdjointSensitivity2003 file = {/Users/kshores/Zotero/storage/UC6A2K82/S1352231003006733.html} } -@article{damianKineticPreprocessorKPPa2002, +@article{Damian2002, title = {The Kinetic Preprocessor {{KPP-a}} Software Environment for Solving Chemical Kinetics}, author = {Damian, Valeriu and Sandu, Adrian and Damian, Mirela and Potra, Florian and Carmichael, Gregory R.}, year = {2002}, @@ -35,7 +35,7 @@ @article{damianKineticPreprocessorKPPa2002 file = {/Users/kshores/Zotero/storage/2VJ5XQH2/Damian et al. - 2002 - The kinetic preprocessor KPP-a software environmen.pdf;/Users/kshores/Zotero/storage/YQSFJ5YJ/S009813540200128X.html} } -@book{hairerSolvingOrdinaryDifferential1996a, +@book{Hairer1996, title = {Solving {{Ordinary Differential Equations II}}: {{Stiff}} and {{Differential-Algebraic Problems}}}, shorttitle = {Solving {{Ordinary Differential Equations II}}}, author = {Hairer, Ernst and Wanner, Gerhard}, @@ -50,7 +50,7 @@ @book{hairerSolvingOrdinaryDifferential1996a file = {/Users/kshores/Zotero/storage/GC4V85EX/Solving Ordinary Differential Equations II.pdf} } -@book{hairerSolvingOrdinaryDifferential2009, +@book{Hairer2009, title = {Solving {{Ordinary Differential Equations I}}: {{Nonstiff Problems}}}, shorttitle = {Solving {{Ordinary Differential Equations I}}}, author = {Hairer, Ernst and N{\o}rsett, Syvert P. and Wanner, Gerhard}, @@ -65,7 +65,7 @@ @book{hairerSolvingOrdinaryDifferential2009 file = {/Users/kshores/Zotero/storage/2AJ3I6HJ/1993 - Solving Ordinary Differential Equations I.pdf} } -@article{rosenbrockGeneralImplicitProcesses1963, +@article{Rosenbrock1963, title = {Some General Implicit Processes for the Numerical Solution of Differential Equations}, author = {Rosenbrock, H. H.}, year = {1963}, @@ -81,7 +81,7 @@ @article{rosenbrockGeneralImplicitProcesses1963 file = {/Users/kshores/Zotero/storage/4NW8TWI5/Rosenbrock - 1963 - Some general implicit processes for the numerical .pdf;/Users/kshores/Zotero/storage/865XYEWP/316388.html} } -@article{sanduBenchmarkingStiffOde1997, +@article{SanduStiff2-1997, title = {Benchmarking Stiff Ode Solvers for Atmospheric Chemistry Problems {{II}}: {{Rosenbrock}} Solvers}, shorttitle = {Benchmarking Stiff Ode Solvers for Atmospheric Chemistry Problems {{II}}}, author = {Sandu, A. and Verwer, J. G. and Blom, J. G. and Spee, E. J. and Carmichael, G. R. and Potra, F. A.}, @@ -99,7 +99,7 @@ @article{sanduBenchmarkingStiffOde1997 file = {/Users/kshores/Zotero/storage/5JMBVP79/Sandu et al. - 1997 - Benchmarking stiff ode solvers for atmospheric che.pdf;/Users/kshores/Zotero/storage/39YHBE2Z/S1352231097832128.html} } -@article{sanduBenchmarkingStiffOde1997a, +@article{SanduStiff1-1997, title = {Benchmarking Stiff Ode Solvers for Atmospheric Chemistry Problems-{{I}}. Implicit vs Explicit}, author = {Sandu, A. and Verwer, J. G. and Van Loon, M. and Carmichael, G. R. and Potra, F. A. and Dabdub, D. and Seinfeld, J. H.}, year = {1997}, @@ -117,7 +117,7 @@ @article{sanduBenchmarkingStiffOde1997a file = {/Users/kshores/Zotero/storage/9SETTXQS/Sandu et al. - 1997 - Benchmarking stiff ode solvers for atmospheric che.pdf;/Users/kshores/Zotero/storage/3IF3VR72/S1352231097000599.html} } -@article{sanduDirectAdjointSensitivity2003, +@article{Sandu2003, title = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}: {{Part I}}\textemdash Theory and Software Tools}, shorttitle = {Direct and Adjoint Sensitivity Analysis of Chemical Kinetic Systems with {{KPP}}}, author = {Sandu, Adrian and Daescu, Dacian N. and Carmichael, Gregory R.}, @@ -136,7 +136,7 @@ @article{sanduDirectAdjointSensitivity2003 file = {/Users/kshores/Zotero/storage/Q2BN8Z5E/Sandu et al. - 2003 - Direct and adjoint sensitivity analysis of chemica.pdf;/Users/kshores/Zotero/storage/B26KZ24Z/S1352231003006721.html} } -@article{sanduEfficientImplementationFully1996, +@article{Sandu1996, title = {Efficient {{Implementation}} of {{Fully Implicit Methods}} for {{Atmospheric Chemical Kinetics}}}, author = {Sandu, A. and Potra, F. A. and Carmichael, G. R. and Damian, V.}, year = {1996}, diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index 224205a0a..d35c7e449 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -29,6 +29,8 @@ Line-by-line explanation There are a total of six different configurations for the Rosenbrock solver. Each corresponds to a different number of stages. What each configuration means is beyond the scope of this tutorial. However, more information can be found in . +:cite:`Sandu1996` + .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp :lines: 88-96 From a4b424adb53c51c93cd5a2a2a4db37d9a050d6f4 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 2 Oct 2023 15:24:58 -0700 Subject: [PATCH 061/318] add JitRosenbrockSolver p-force regression tests --- .../RosenbrockChapman/CMakeLists.txt | 1 + .../regression_test_jit_p_force.cpp | 25 +++ .../regression_test_p_force.cpp | 176 ++++++------------ .../regression_test_p_force_policy.hpp | 106 +++++++++++ 4 files changed, 193 insertions(+), 115 deletions(-) create mode 100644 test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp create mode 100644 test/regression/RosenbrockChapman/regression_test_p_force_policy.hpp diff --git a/test/regression/RosenbrockChapman/CMakeLists.txt b/test/regression/RosenbrockChapman/CMakeLists.txt index cdef173cd..90a3da090 100644 --- a/test/regression/RosenbrockChapman/CMakeLists.txt +++ b/test/regression/RosenbrockChapman/CMakeLists.txt @@ -11,5 +11,6 @@ create_standard_test(NAME regression_test_rosenbrock_dforce_dy SOURCES regressio create_standard_test(NAME regression_test_solve SOURCES regression_test_solve.cpp) if(ENABLE_LLVM) + create_standard_test(NAME regression_test_jit_p_force SOURCES regression_test_jit_p_force.cpp) create_standard_test(NAME regression_test_jit_dforce_dy SOURCES regression_test_jit_dforce_dy.cpp) endif() \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp b/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp new file mode 100644 index 000000000..c0c6aada4 --- /dev/null +++ b/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp @@ -0,0 +1,25 @@ +#include + +#include +#include + +#include "jit_util.hpp" +#include "regression_test_p_force_policy.hpp" + +TEST(RegressionJitRosenbrock, VectorRateConstants) +{ + auto solver = getThreeStageMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testRateConstants<>(solver); +} + +TEST(RegressionJitRosenbrock, VectorForcing) +{ + auto solver = getThreeStageMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testForcing(solver); +} \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_p_force.cpp b/test/regression/RosenbrockChapman/regression_test_p_force.cpp index 08a0122ce..d69f4165e 100644 --- a/test/regression/RosenbrockChapman/regression_test_p_force.cpp +++ b/test/regression/RosenbrockChapman/regression_test_p_force.cpp @@ -1,135 +1,81 @@ #include #include -#include -#include -#include -#include -#include -#include "chapman_ode_solver.hpp" -#include "util.hpp" - -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -void testRateConstants() -{ - micm::ChapmanODESolver fixed_solver{}; - auto solver = getThreeStageMultiCellChapmanSolver(3); - - auto state = solver.GetState(); - auto fixed_state = fixed_solver.GetState(); - const std::vector> photo_rates{ { 1.0e-4, 1.0e-5, 1.0e-6 }, - { 3.2e-4, 7.3e-5, 3.2e-6 }, - { 5.2e-4, 8.2e-5, 4.6e-6 } }; - state.custom_rate_parameters_ = photo_rates; - state.conditions_[0].temperature_ = 284.19; // [K] - state.conditions_[0].pressure_ = 101245.0; // [Pa] - state.conditions_[1].temperature_ = 215.02; // [K] - state.conditions_[1].pressure_ = 100789.2; // [Pa] - state.conditions_[2].temperature_ = 299.31; // [K] - state.conditions_[2].pressure_ = 101398.0; // [Pa] - - solver.UpdateState(state); - - for (size_t i{}; i < 3; ++i) - { - fixed_state.conditions_[0].temperature_ = state.conditions_[i].temperature_; - fixed_state.conditions_[0].pressure_ = state.conditions_[i].pressure_; - fixed_state.custom_rate_parameters_[0] = photo_rates[i]; - fixed_solver.UpdateState(fixed_state); - - EXPECT_EQ(state.rate_constants_[i].size(), fixed_state.rate_constants_[0].size()); - for (size_t j{}; j < state.rate_constants_[i].size(); ++j) - { - EXPECT_EQ(state.rate_constants_[i][j], fixed_state.rate_constants_[0][j]); - } - } -} - -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -void testForcing() -{ - std::random_device rnd_device; - std::mt19937 engine{ rnd_device() }; - std::lognormal_distribution dist(-2.0, 2.0); - - micm::ChapmanODESolver fixed_solver{}; - auto solver = getThreeStageMultiCellChapmanSolver(3); - - auto state = solver.GetState(); - - auto& state_vec = state.variables_.AsVector(); - std::generate(begin(state_vec), end(state_vec), [&]() { return dist(engine); }); - auto& rate_const_vec = state.rate_constants_.AsVector(); - std::generate(begin(rate_const_vec), end(rate_const_vec), [&]() { return dist(engine); }); - - MatrixPolicy forcing(3, 9); - solver.CalculateForcing(state.rate_constants_, state.variables_, forcing); - - for (std::size_t i{}; i < 3; ++i) - { - double number_density_air = 1.0; - std::vector rate_constants = state.rate_constants_[i]; - std::vector variables(state.variables_[i].size()); - for (std::size_t j{}; j < state.variables_[i].size(); ++j) - variables[j] = state.variables_[i][state.variable_map_[fixed_solver.species_names()[j]]]; - std::vector fixed_forcing = fixed_solver.force(rate_constants, variables, number_density_air); - - EXPECT_EQ(forcing[i].size(), fixed_forcing.size()); - for (std::size_t j{}; j < fixed_forcing.size(); ++j) - { - double a = forcing[i][state.variable_map_[fixed_solver.species_names()[j]]]; - double b = fixed_forcing[j]; - EXPECT_NEAR(a, b, (std::abs(a) + std::abs(b)) * 1.0e-8 + 1.0e-12); - } - } -} - -template -using DenseMatrix = micm::Matrix; -template -using SparseMatrix = micm::SparseMatrix; - -template -using Group1VectorMatrix = micm::VectorMatrix; -template -using Group2VectorMatrix = micm::VectorMatrix; -template -using Group3VectorMatrix = micm::VectorMatrix; -template -using Group4VectorMatrix = micm::VectorMatrix; - -template -using Group1SparseVectorMatrix = micm::SparseMatrix>; -template -using Group2SparseVectorMatrix = micm::SparseMatrix>; -template -using Group3SparseVectorMatrix = micm::SparseMatrix>; -template -using Group4SparseVectorMatrix = micm::SparseMatrix>; +#include "regression_test_p_force_policy.hpp" TEST(RegressionRosenbrock, RateConstants) { - testRateConstants>(); + auto solver = getThreeStageMultiCellChapmanSolver>(3); + testRateConstants<>(solver); } TEST(RegressionRosenbrock, VectorRateConstants) { - testRateConstants>(); - testRateConstants>(); - testRateConstants>(); - testRateConstants>(); + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::LinearSolver>(3); + testRateConstants<>(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group2VectorMatrix, + Group2SparseVectorMatrix, + micm::LinearSolver>(3); + testRateConstants<>(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::LinearSolver>(3); + testRateConstants<>(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group4VectorMatrix, + Group4SparseVectorMatrix, + micm::LinearSolver>(3); + testRateConstants<>(solver); + } } TEST(RegressionRosenbrock, Forcing) { - testForcing>(); + auto solver = getThreeStageMultiCellChapmanSolver>(3); + testForcing(solver); } TEST(RegressionRosenbrock, VectorForcing) { - testForcing>(); - testForcing>(); - testForcing>(); - testForcing>(); + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::LinearSolver>(3); + testForcing(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group2VectorMatrix, + Group2SparseVectorMatrix, + micm::LinearSolver>(3); + testForcing(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::LinearSolver>(3); + testForcing(solver); + } + { + auto solver = getThreeStageMultiCellChapmanSolver< + Group4VectorMatrix, + Group4SparseVectorMatrix, + micm::LinearSolver>(3); + testForcing(solver); + } } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_p_force_policy.hpp b/test/regression/RosenbrockChapman/regression_test_p_force_policy.hpp new file mode 100644 index 000000000..b8910be2a --- /dev/null +++ b/test/regression/RosenbrockChapman/regression_test_p_force_policy.hpp @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "chapman_ode_solver.hpp" +#include "util.hpp" + +template +void testRateConstants(OdeSolverPolicy& solver) +{ + micm::ChapmanODESolver fixed_solver{}; + + auto state = solver.GetState(); + auto fixed_state = fixed_solver.GetState(); + const std::vector> photo_rates{ { 1.0e-4, 1.0e-5, 1.0e-6 }, + { 3.2e-4, 7.3e-5, 3.2e-6 }, + { 5.2e-4, 8.2e-5, 4.6e-6 } }; + state.custom_rate_parameters_ = photo_rates; + state.conditions_[0].temperature_ = 284.19; // [K] + state.conditions_[0].pressure_ = 101245.0; // [Pa] + state.conditions_[1].temperature_ = 215.02; // [K] + state.conditions_[1].pressure_ = 100789.2; // [Pa] + state.conditions_[2].temperature_ = 299.31; // [K] + state.conditions_[2].pressure_ = 101398.0; // [Pa] + + solver.UpdateState(state); + + for (size_t i{}; i < 3; ++i) + { + fixed_state.conditions_[0].temperature_ = state.conditions_[i].temperature_; + fixed_state.conditions_[0].pressure_ = state.conditions_[i].pressure_; + fixed_state.custom_rate_parameters_[0] = photo_rates[i]; + fixed_solver.UpdateState(fixed_state); + + EXPECT_EQ(state.rate_constants_[i].size(), fixed_state.rate_constants_[0].size()); + for (size_t j{}; j < state.rate_constants_[i].size(); ++j) + { + EXPECT_EQ(state.rate_constants_[i][j], fixed_state.rate_constants_[0][j]); + } + } +} + +template class MatrixPolicy, class OdeSolverPolicy> +void testForcing(OdeSolverPolicy& solver) +{ + std::random_device rnd_device; + std::mt19937 engine{ rnd_device() }; + std::lognormal_distribution dist(-2.0, 2.0); + + micm::ChapmanODESolver fixed_solver{}; + + auto state = solver.GetState(); + + auto& state_vec = state.variables_.AsVector(); + std::generate(begin(state_vec), end(state_vec), [&]() { return dist(engine); }); + auto& rate_const_vec = state.rate_constants_.AsVector(); + std::generate(begin(rate_const_vec), end(rate_const_vec), [&]() { return dist(engine); }); + + MatrixPolicy forcing(3, 9); + solver.CalculateForcing(state.rate_constants_, state.variables_, forcing); + + for (std::size_t i{}; i < 3; ++i) + { + double number_density_air = 1.0; + std::vector rate_constants = state.rate_constants_[i]; + std::vector variables(state.variables_[i].size()); + for (std::size_t j{}; j < state.variables_[i].size(); ++j) + variables[j] = state.variables_[i][state.variable_map_[fixed_solver.species_names()[j]]]; + std::vector fixed_forcing = fixed_solver.force(rate_constants, variables, number_density_air); + + EXPECT_EQ(forcing[i].size(), fixed_forcing.size()); + for (std::size_t j{}; j < fixed_forcing.size(); ++j) + { + double a = forcing[i][state.variable_map_[fixed_solver.species_names()[j]]]; + double b = fixed_forcing[j]; + EXPECT_NEAR(a, b, (std::abs(a) + std::abs(b)) * 1.0e-8 + 1.0e-12); + } + } +} + +template +using DenseMatrix = micm::Matrix; +template +using SparseMatrix = micm::SparseMatrix; + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; From 12f1eabb9728b3dfce1b9e2498198e7c8b7ce817 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 15:38:34 -0700 Subject: [PATCH 062/318] Auto-format code changes (#274) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- test/tutorial/test_multiple_grid_cells.cpp | 12 ++++++------ .../test_rate_constants_no_user_defined_by_hand.cpp | 4 +--- ...st_rate_constants_no_user_defined_with_config.cpp | 4 +--- .../test_rate_constants_user_defined_by_hand.cpp | 11 +++++------ 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index 851e268dd..57b17c691 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -1,6 +1,5 @@ #include #include - #include #include @@ -86,7 +85,7 @@ int main() State state = solver.GetState(); // mol m-3 - state.SetConcentration(a, std::vector{1, 2, 0.5 }); + state.SetConcentration(a, std::vector{ 1, 2, 0.5 }); state.SetConcentration(b, std::vector(3, 0)); state.SetConcentration(c, std::vector(3, 0)); @@ -97,11 +96,12 @@ int main() state.SetCustomRateParameter("r2", std::vector(3, k2)); state.SetCustomRateParameter("r3", std::vector(3, k3)); - double temperature = 272.5; // [K] - double pressure = 101253.3; // [Pa] - double air_density = 1e6; // [mol m-3] + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] - for(size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) { + for (size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) + { state.conditions_[cell].temperature_ = temperature; state.conditions_[cell].pressure_ = pressure; state.conditions_[cell].air_density_ = air_density; diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index 510fb18a2..22aff99b3 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -129,9 +129,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7 }; - RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); state.conditions_[0].temperature_ = 287.45; // K diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index 0f11ab730..ca5cc06d9 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -62,9 +62,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index efd35adce..6938c20ee 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -136,18 +136,18 @@ int main(const int argc, const char* argv[]) Process r8 = Process::create() .reactants({ c }) .products({ yields(g, 1) }) - .rate_constant(UserDefinedRateConstant({.label_="my photolysis rate"})) + .rate_constant(UserDefinedRateConstant({ .label_ = "my photolysis rate" })) .phase(gas_phase); Process r9 = Process::create() .products({ yields(a, 1) }) - .rate_constant(UserDefinedRateConstant({.label_="my emission rate"})) + .rate_constant(UserDefinedRateConstant({ .label_ = "my emission rate" })) .phase(gas_phase); Process r10 = Process::create() - .reactants({ b }) - .rate_constant(UserDefinedRateConstant({.label_="my loss rate"})) - .phase(gas_phase); + .reactants({ b }) + .rate_constant(UserDefinedRateConstant({ .label_ = "my loss rate" })) + .phase(gas_phase); auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; @@ -171,7 +171,6 @@ int main(const int argc, const char* argv[]) state.SetCustomRateParameter("C.effective radius [m]", 1e-7); state.SetCustomRateParameter("C.particle number concentration [# m-3]", 2.5e6); - // choose and timestep a print the initial state double time_step = 500; // s From 7df8bd4a8f987d296846b03635121d4fe8dd189e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 3 Oct 2023 11:00:05 -0500 Subject: [PATCH 063/318] added citation --- .../citing_and_bibliography/citation.rst | 33 +++- .../user_guide/rate_constant_tutorial.rst | 40 ++--- .../user_guide/solver_configurations.rst | 146 ++++++++++++++++++ .../user_defined_rate_constant_tutorial.rst | 40 ++--- 4 files changed, 208 insertions(+), 51 deletions(-) diff --git a/docs/source/citing_and_bibliography/citation.rst b/docs/source/citing_and_bibliography/citation.rst index af5bf186f..dd44e7aeb 100644 --- a/docs/source/citing_and_bibliography/citation.rst +++ b/docs/source/citing_and_bibliography/citation.rst @@ -1,3 +1,34 @@ Citing MICM -=========== \ No newline at end of file +=========== + +MICM is a group scientific and software effort. + +Cite the science +----------------- + +Cite the software +----------------- + +The software can be cited like this + +@software{ncar.acom.micm, + author = {Kyle Shores and + Qina Tan and + Matt Dawson and + cacraigucar and + Jiwon Gim and + David Fillmore and + Francis Vitt and + Andrew Conley and + actions-user and + davidfillmore and + Aharon Karsenti}, + title = {NCAR/micm: v3.2.0}, + month = sep, + year = 2023, + publisher = {Zenodo}, + version = {v3.2.0}, + doi = {10.5281/zenodo.8377912}, + url = {https://doi.org/10.5281/zenodo.8377912} +} \ No newline at end of file diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index f3857e8b9..4c788321f 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -158,28 +158,18 @@ Finally, we are ready to pick a timestep and solve the system. This is the output: -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| time | A | B | C | D | E | F | G | -+=======+=============+=============+=============+=============+=============+=============+=============+ -| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1500 | 4.09e-20 | 4.71e-20 | 9.49e-01 | 4.98e-25 | 1.39e-03 | 2.59e-24 | 2.48e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2000 | 1.47e-25 | 1.69e-25 | 9.33e-01 | 1.79e-30 | 1.38e-03 | 9.30e-30 | 3.30e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2500 | 5.26e-31 | 6.05e-31 | 9.17e-01 | 6.40e-36 | 1.37e-03 | 3.33e-35 | 4.11e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 3000 | 1.89e-36 | 2.17e-36 | 9.01e-01 | 2.30e-41 | 1.36e-03 | 1.20e-40 | 4.90e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 3500 | 6.77e-42 | 7.78e-42 | 8.85e-01 | 8.23e-47 | 1.34e-03 | 4.29e-46 | 5.68e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 4000 | 2.43e-47 | 2.79e-47 | 8.70e-01 | 2.95e-52 | 1.33e-03 | 1.54e-51 | 6.44e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 4500 | 8.70e-53 | 1.00e-52 | 8.55e-01 | 1.06e-57 | 1.32e-03 | 5.51e-57 | 7.20e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 5000 | 3.12e-58 | 3.59e-58 | 8.40e-01 | 3.80e-63 | 1.31e-03 | 1.98e-62 | 7.94e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ \ No newline at end of file +.. csv-table:: Table Title + :header: "time", "A", "B", "C", "D", "E", "F", "G" + :widths: 10, 15, 15, 15, 15, 15, 15, 15 + + "0", "1.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00" + "500", "3.18e-09", "3.66e-09", "9.83e-01", "3.88e-14", "1.41e-03", "2.02e-13", "7.92e-03" + "1000", "1.14e-14", "1.31e-14", "9.66e-01", "1.39e-19", "1.40e-03", "7.24e-19", "1.64e-02" + "1500", "4.09e-20", "4.71e-20", "9.49e-01", "4.98e-25", "1.39e-03", "2.59e-24", "2.48e-02" + "2000", "1.47e-25", "1.69e-25", "9.33e-01", "1.79e-30", "1.38e-03", "9.30e-30", "3.30e-02" + "2500", "5.26e-31", "6.05e-31", "9.17e-01", "6.40e-36", "1.37e-03", "3.33e-35", "4.11e-02" + "3000", "1.89e-36", "2.17e-36", "9.01e-01", "2.30e-41", "1.36e-03", "1.20e-40", "4.90e-02" + "3500", "6.77e-42", "7.78e-42", "8.85e-01", "8.23e-47", "1.34e-03", "4.29e-46", "5.68e-02" + "4000", "2.43e-47", "2.79e-47", "8.70e-01", "2.95e-52", "1.33e-03", "1.54e-51", "6.44e-02" + "4500", "8.70e-53", "1.00e-52", "8.55e-01", "1.06e-57", "1.32e-03", "5.51e-57", "7.20e-02" + "5000", "3.12e-58", "3.59e-58", "8.40e-01", "3.80e-63", "1.31e-03", "1.98e-62", "7.94e-02" diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index d35c7e449..71fdbb840 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -34,3 +34,149 @@ What each configuration means is beyond the scope of this tutorial. However, mor .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp :lines: 88-96 + + +You should see an output similar to this: + +.. code-block:: console + + Two stages: + time, A, B, C + 0, 1.00e+00, 0.00e+00, 0.00e+00 + 200, 5.37e-01, 4.50e-06, 4.63e-01 + 400, 4.51e-01, 3.23e-06, 5.49e-01 + 600, 4.01e-01, 2.64e-06, 5.99e-01 + 800, 3.65e-01, 2.28e-06, 6.35e-01 + 1000, 3.38e-01, 2.02e-06, 6.62e-01 + 1200, 3.16e-01, 1.83e-06, 6.84e-01 + 1400, 2.97e-01, 1.68e-06, 7.03e-01 + 1600, 2.82e-01, 1.56e-06, 7.18e-01 + 1800, 2.68e-01, 1.46e-06, 7.32e-01 + 2000, 2.56e-01, 1.37e-06, 7.44e-01 + Total solve time: 135748 nanoseconds + accepted: 178 + function_calls: 356 + jacobian_updates: 178 + number_of_steps: 178 + accepted: 178 + rejected: 0 + decompositions: 178 + solves: 356 + singular: 0 + total_forcing_time: 9962 nanoseconds + total_jacobian_time: 6454 nanoseconds + total_linear_factor_time: 26835 nanoseconds + total_linear_solve_time: 12044 nanoseconds + + Three stages: + time, A, B, C + 0, 1.00e+00, 0.00e+00, 0.00e+00 + 200, 5.35e-01, 4.50e-06, 4.65e-01 + 400, 4.50e-01, 3.23e-06, 5.50e-01 + 600, 4.00e-01, 2.63e-06, 6.00e-01 + 800, 3.64e-01, 2.27e-06, 6.36e-01 + 1000, 3.37e-01, 2.01e-06, 6.63e-01 + 1200, 3.15e-01, 1.82e-06, 6.85e-01 + 1400, 2.96e-01, 1.67e-06, 7.04e-01 + 1600, 2.81e-01, 1.55e-06, 7.19e-01 + 1800, 2.67e-01, 1.45e-06, 7.33e-01 + 2000, 2.55e-01, 1.37e-06, 7.45e-01 + Total solve time: 110002 nanoseconds + accepted: 127 + function_calls: 254 + jacobian_updates: 127 + number_of_steps: 127 + accepted: 127 + rejected: 0 + decompositions: 127 + solves: 381 + singular: 0 + total_forcing_time: 7421 nanoseconds + total_jacobian_time: 4295 nanoseconds + total_linear_factor_time: 19248 nanoseconds + total_linear_solve_time: 12093 nanoseconds + + Four stages: + time, A, B, C + 0, 1.00e+00, 0.00e+00, 0.00e+00 + 200, 5.36e-01, 4.49e-06, 4.64e-01 + 400, 4.50e-01, 3.20e-06, 5.50e-01 + 600, 4.00e-01, 2.62e-06, 6.00e-01 + 800, 3.64e-01, 2.26e-06, 6.36e-01 + 1000, 3.37e-01, 2.01e-06, 6.63e-01 + 1200, 3.15e-01, 1.82e-06, 6.85e-01 + 1400, 2.97e-01, 1.67e-06, 7.03e-01 + 1600, 2.81e-01, 1.55e-06, 7.19e-01 + 1800, 2.67e-01, 1.45e-06, 7.33e-01 + 2000, 2.56e-01, 1.37e-06, 7.44e-01 + Total solve time: 127291 nanoseconds + accepted: 127 + function_calls: 381 + jacobian_updates: 127 + number_of_steps: 127 + accepted: 127 + rejected: 0 + decompositions: 127 + solves: 508 + singular: 0 + total_forcing_time: 9749 nanoseconds + total_jacobian_time: 4537 nanoseconds + total_linear_factor_time: 20040 nanoseconds + total_linear_solve_time: 17923 nanoseconds + + Four stages differential algebraic: + time, A, B, C + 0, 1.00e+00, 0.00e+00, 0.00e+00 + 200, 5.36e-01, 4.49e-06, 4.64e-01 + 400, 4.51e-01, 3.23e-06, 5.49e-01 + 600, 4.00e-01, 2.63e-06, 6.00e-01 + 800, 3.64e-01, 2.27e-06, 6.36e-01 + 1000, 3.37e-01, 2.01e-06, 6.63e-01 + 1200, 3.15e-01, 1.82e-06, 6.85e-01 + 1400, 2.97e-01, 1.68e-06, 7.03e-01 + 1600, 2.81e-01, 1.55e-06, 7.19e-01 + 1800, 2.68e-01, 1.45e-06, 7.32e-01 + 2000, 2.56e-01, 1.37e-06, 7.44e-01 + Total solve time: 126334 nanoseconds + accepted: 128 + function_calls: 384 + jacobian_updates: 128 + number_of_steps: 128 + accepted: 128 + rejected: 0 + decompositions: 128 + solves: 512 + singular: 0 + total_forcing_time: 10584 nanoseconds + total_jacobian_time: 4376 nanoseconds + total_linear_factor_time: 19792 nanoseconds + total_linear_solve_time: 17254 nanoseconds + + + Six stages differential algebraic: + time, A, B, C + 0, 1.00e+00, 0.00e+00, 0.00e+00 + 200, 5.36e-01, 4.49e-06, 4.64e-01 + 400, 4.51e-01, 3.22e-06, 5.49e-01 + 600, 4.00e-01, 2.63e-06, 6.00e-01 + 800, 3.64e-01, 2.27e-06, 6.36e-01 + 1000, 3.37e-01, 2.01e-06, 6.63e-01 + 1200, 3.15e-01, 1.82e-06, 6.85e-01 + 1400, 2.97e-01, 1.67e-06, 7.03e-01 + 1600, 2.81e-01, 1.55e-06, 7.19e-01 + 1800, 2.67e-01, 1.45e-06, 7.33e-01 + 2000, 2.56e-01, 1.37e-06, 7.44e-01 + Total solve time: 174959 nanoseconds + accepted: 127 + function_calls: 762 + jacobian_updates: 127 + number_of_steps: 127 + accepted: 127 + rejected: 0 + decompositions: 127 + solves: 762 + singular: 0 + total_forcing_time: 20743 nanoseconds + total_jacobian_time: 4497 nanoseconds + total_linear_factor_time: 20085 nanoseconds + total_linear_solve_time: 28233 nanoseconds \ No newline at end of file diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index 4688893c8..f6b577da6 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -211,29 +211,19 @@ Finally, set and upate the rate constants as needed: And this is final output. Notice that the concentration of G ends up much higher than in the :ref:`Rate constants` tutorial's result. -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| time | A | B | C | D | E | F | G | -+=======+=============+=============+=============+=============+=============+=============+=============+ -| 0 | 1.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | 0.00e+00 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 500 | 3.18e-09 | 3.66e-09 | 9.83e-01 | 3.88e-14 | 1.41e-03 | 2.02e-13 | 7.92e-03 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1000 | 1.14e-14 | 1.31e-14 | 9.66e-01 | 1.39e-19 | 1.40e-03 | 7.24e-19 | 1.64e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 1500 | 7.27e-20 | 6.40e-20 | 9.49e-01 | 6.53e-25 | 1.39e-03 | 3.19e-24 | 2.48e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2000 | 3.17e-20 | 1.70e-20 | 9.33e-01 | 1.55e-25 | 1.38e-03 | 5.92e-25 | 3.30e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 2500 | 3.17e-20 | 1.70e-20 | 9.17e-01 | 1.55e-25 | 1.37e-03 | 5.92e-25 | 4.11e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 3000 | 3.17e-20 | 1.70e-20 | 9.01e-01 | 1.55e-25 | 1.36e-03 | 5.92e-25 | 4.90e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 3500 | 3.17e-20 | 1.70e-20 | 8.85e-01 | 1.55e-25 | 1.34e-03 | 5.92e-25 | 5.68e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 4000 | 3.17e-20 | 1.70e-20 | 8.70e-01 | 1.55e-25 | 1.33e-03 | 5.92e-25 | 6.44e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 4500 | 3.17e-20 | 1.70e-20 | 8.55e-01 | 1.55e-25 | 1.32e-03 | 5.92e-25 | 7.20e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ -| 5000 | 3.17e-20 | 1.70e-20 | 8.40e-01 | 1.55e-25 | 1.31e-03 | 5.92e-25 | 7.94e-02 | -+-------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+ +.. csv-table:: Table Title + :header: "time", "A", "B", "C", "D", "E", "F", "G" + :widths: 10, 15, 15, 15, 15, 15, 15, 15 + + "0", "1.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00" + "500", "3.18e-09", "3.66e-09", "9.83e-01", "3.88e-14", "1.41e-03", "2.02e-13", "7.92e-03" + "1000", "1.14e-14", "1.31e-14", "9.66e-01", "1.39e-19", "1.40e-03", "7.24e-19", "1.64e-02" + "1500", "7.27e-20", "6.40e-20", "9.49e-01", "6.53e-25", "1.39e-03", "3.19e-24", "2.48e-02" + "2000", "3.17e-20", "1.70e-20", "9.33e-01", "1.55e-25", "1.38e-03", "5.92e-25", "3.30e-02" + "2500", "3.17e-20", "1.70e-20", "9.17e-01", "1.55e-25", "1.37e-03", "5.92e-25", "4.11e-02" + "3000", "3.17e-20", "1.70e-20", "9.01e-01", "1.55e-25", "1.36e-03", "5.92e-25", "4.90e-02" + "3500", "3.17e-20", "1.70e-20", "8.85e-01", "1.55e-25", "1.34e-03", "5.92e-25", "5.68e-02" + "4000", "3.17e-20", "1.70e-20", "8.70e-01", "1.55e-25", "1.33e-03", "5.92e-25", "6.44e-02" + "4500", "3.17e-20", "1.70e-20", "8.55e-01", "1.55e-25", "1.32e-03", "5.92e-25", "7.20e-02" + "5000", "3.17e-20", "1.70e-20", "8.40e-01", "1.55e-25", "1.31e-03", "5.92e-25", "7.94e-02" From 1a1533b99f7916915492bd1926270116fff391fd Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 3 Oct 2023 11:50:46 -0500 Subject: [PATCH 064/318] Update README.md (#275) Added zenodo badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index da2482149..97ab0d751 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Model Independent Chemical Module. MICM can be used to configure and solve atmos [![License](https://img.shields.io/github/license/NCAR/micm.svg)](https://github.com/NCAR/micm/blob/master/LICENSE) [![CI Status](https://github.com/NCAR/micm/actions/workflows/test.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/NCAR/micm/branch/main/graph/badge.svg?token=ATGO4DKTMY)](https://codecov.io/gh/NCAR/micm) +[![DOI](https://zenodo.org/badge/294492778.svg)](https://zenodo.org/badge/latestdoi/294492778) Copyright (C) 2018-2023 National Center for Atmospheric Research From 970fde07e3b1704ac85f7b22c14856c94748810b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 3 Oct 2023 14:43:16 -0500 Subject: [PATCH 065/318] added tutorial for different solver types --- .../citing_and_bibliography/citation.rst | 42 ++++++++++--------- .../user_guide/solver_configurations.rst | 28 ++++++++++--- 2 files changed, 45 insertions(+), 25 deletions(-) diff --git a/docs/source/citing_and_bibliography/citation.rst b/docs/source/citing_and_bibliography/citation.rst index dd44e7aeb..ec308a233 100644 --- a/docs/source/citing_and_bibliography/citation.rst +++ b/docs/source/citing_and_bibliography/citation.rst @@ -12,23 +12,25 @@ Cite the software The software can be cited like this -@software{ncar.acom.micm, - author = {Kyle Shores and - Qina Tan and - Matt Dawson and - cacraigucar and - Jiwon Gim and - David Fillmore and - Francis Vitt and - Andrew Conley and - actions-user and - davidfillmore and - Aharon Karsenti}, - title = {NCAR/micm: v3.2.0}, - month = sep, - year = 2023, - publisher = {Zenodo}, - version = {v3.2.0}, - doi = {10.5281/zenodo.8377912}, - url = {https://doi.org/10.5281/zenodo.8377912} -} \ No newline at end of file +.. code-block:: console + + @software{ncar.acom.micm, + author = {Kyle Shores and + Qina Tan and + Matt Dawson and + cacraigucar and + Jiwon Gim and + David Fillmore and + Francis Vitt and + Andrew Conley and + actions-user and + davidfillmore and + Aharon Karsenti}, + title = {NCAR/micm: v3.2.0}, + month = sep, + year = 2023, + publisher = {Zenodo}, + version = {v3.2.0}, + doi = {10.5281/zenodo.8377912}, + url = {https://doi.org/10.5281/zenodo.8377912} + } \ No newline at end of file diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index 71fdbb840..47bd81905 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -26,17 +26,35 @@ the appropriate tab below and be on your way! Otherwise, stick around for a line Line-by-line explanation ------------------------ -There are a total of six different configurations for the Rosenbrock solver. Each corresponds to a different number of stages. -What each configuration means is beyond the scope of this tutorial. However, more information can be found in . +There are a total of five different configurations for the Rosenbrock solver. Each corresponds to a different number of stages. +What each configuration means is beyond the scope of this tutorial. However, references are included for further reading. -:cite:`Sandu1996` +- Two stage + - An L-stable method, 2 stages, order 2. :cite:`Hairer1996` +- Three stage + - An L-stable method, 3 stages, order 3. :cite:`SanduStiff2-1997` +- Four stage + - An L-stable method, 4 stages, order 4. :cite:`Hairer1996` +- Four stage, differential algebraic + - A stiffly stable method, 4 stages, order 3, :cite:`Hairer1996` +- Six stage, differential algebraic + - Stiffly stable rosenbrock method of order 4 with 6 stages, :cite:`Hairer1996` + + +Configuring the rosenbrock solver is as easy as providing the solver with a set of :cpp:class:`micm::RosenbrockSolverParameters` .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp - :lines: 88-96 + :lines: 154-172 + +After that, the usage is the same as a regular solver. A templated method was used here to run the same solving code +for each of the different solver configurations. +.. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp + :language: cpp + :lines: 40-123 -You should see an output similar to this: +Running this program should give an output similar to this: .. code-block:: console From a16eea1e43a7a4f44192869bfc1f3a455cd3c45c Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Tue, 3 Oct 2023 15:04:52 -0700 Subject: [PATCH 066/318] add JitRosenbrock solve regression test --- include/micm/process/jit_process_set.hpp | 31 +++++ include/micm/solver/jit_linear_solver.hpp | 5 + include/micm/solver/jit_linear_solver.inl | 23 +++- include/micm/solver/jit_lu_decomposition.hpp | 5 + include/micm/solver/jit_lu_decomposition.inl | 21 ++++ include/micm/solver/jit_rosenbrock.hpp | 20 ++++ include/micm/solver/rosenbrock.inl | 2 +- .../RosenbrockChapman/CMakeLists.txt | 1 + .../regression_test_jit_solve.cpp | 52 +++++++++ .../regression_test_solve.cpp | 107 +++--------------- .../regression_test_solve_policy.hpp | 87 ++++++++++++++ 11 files changed, 262 insertions(+), 92 deletions(-) create mode 100644 test/regression/RosenbrockChapman/regression_test_jit_solve.cpp create mode 100644 test/regression/RosenbrockChapman/regression_test_solve_policy.hpp diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 9f59d2886..d2c0e9eed 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -24,6 +24,11 @@ namespace micm void (*jacobian_function_)(const double *, const double *, double *); public: + JitProcessSet(const JitProcessSet &) = delete; + JitProcessSet &operator=(const JitProcessSet &) = delete; + JitProcessSet(JitProcessSet &&); + JitProcessSet &operator=(JitProcessSet &&); + /// @brief Create a JITed process set calculator for a given set of processes /// @param compiler JIT compiler /// @param processes Processes to create calculator for @@ -70,6 +75,32 @@ namespace micm void GenerateJacobianFunction(const SparseMatrix> &matrix); }; + template + inline JitProcessSet::JitProcessSet(JitProcessSet &&other) + : ProcessSet(std::move(other)), + compiler_(std::move(other.compiler_)), + forcing_function_resource_tracker_(std::move(other.forcing_function_resource_tracker_)), + forcing_function_(std::move(other.forcing_function_)), + jacobian_function_resource_tracker_(std::move(other.jacobian_function_resource_tracker_)), + jacobian_function_(std::move(other.jacobian_function_)) + { + other.forcing_function_ = NULL; + other.jacobian_function_ = NULL; + } + + template + inline JitProcessSet &JitProcessSet::operator=(JitProcessSet &&other) + { + ProcessSet::operator=(std::move(other)); + compiler_ = std::move(other.compiler_); + forcing_function_resource_tracker_ = std::move(other.forcing_function_resource_tracker_); + forcing_function_ = std::move(other.forcing_function_); + jacobian_function_resource_tracker_ = std::move(other.jacobian_function_resource_tracker_); + jacobian_function_ = std::move(other.jacobian_function_); + other.forcing_function_ = NULL; + other.jacobian_function_ = NULL; + } + template template class MatrixPolicy> inline JitProcessSet::JitProcessSet( diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index a2e35b28d..1c087422f 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -27,6 +27,11 @@ namespace micm public: JitLinearSolver(){}; + JitLinearSolver(const JitLinearSolver&) = delete; + JitLinearSolver& operator=(const JitLinearSolver&) = delete; + JitLinearSolver(JitLinearSolver&&); + JitLinearSolver& operator=(JitLinearSolver&&); + /// @brief Create a JITed linear solver for a given sparse matrix structure /// @param compiler JIT compiler /// @param matrix Block-diagonal sparse matrix to create solver for diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index f886a4f7a..69f1158e7 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -4,6 +4,27 @@ namespace micm { + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline JitLinearSolver::JitLinearSolver(JitLinearSolver &&other) + : LinearSolver(std::move(other)), + compiler_(std::move(other.compiler_)), + solve_function_resource_tracker_(std::move(other.solve_function_resource_tracker_)), + solve_function_(std::move(other.solve_function_)) + { + other.solve_function_ = NULL; + } + + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline JitLinearSolver & + JitLinearSolver::operator=(JitLinearSolver &&other) + { + LinearSolver::operator=(std::move(other)); + compiler_ = std::move(other.compiler_); + solve_function_resource_tracker_ = std::move(other.solve_function_resource_tracker_); + solve_function_ = std::move(other.solve_function_); + other.solve_function_ = NULL; + } + template class SparseMatrixPolicy, class LuDecompositionPolicy> inline JitLinearSolver::JitLinearSolver( std::shared_ptr compiler, @@ -17,6 +38,7 @@ namespace micm compiler_(compiler) { solve_function_ = NULL; + GenerateSolveFunction(); } template class SparseMatrixPolicy, class LuDecompositionPolicy> @@ -34,7 +56,6 @@ namespace micm SparseMatrix> &matrix) { LinearSolver::Factor(matrix); - GenerateSolveFunction(); } template class SparseMatrixPolicy, class LuDecompositionPolicy> diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index a46f24814..61ed48194 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -25,6 +25,11 @@ namespace micm public: JitLuDecomposition(){}; + JitLuDecomposition(const JitLuDecomposition &) = delete; + JitLuDecomposition &operator=(const JitLuDecomposition &) = delete; + JitLuDecomposition(JitLuDecomposition &&); + JitLuDecomposition &operator=(JitLuDecomposition &&); + /// @brief Create a JITed LU decomposer for a given sparse matrix structure /// @param compiler JIT compiler /// @param matrix Sparse matrix to create LU decomposer for diff --git a/include/micm/solver/jit_lu_decomposition.inl b/include/micm/solver/jit_lu_decomposition.inl index 0284a9170..ed3eee038 100644 --- a/include/micm/solver/jit_lu_decomposition.inl +++ b/include/micm/solver/jit_lu_decomposition.inl @@ -3,6 +3,27 @@ namespace micm { + + template + inline JitLuDecomposition::JitLuDecomposition(JitLuDecomposition &&other) + : LuDecomposition(std::move(other)), + compiler_(std::move(other.compiler_)), + decompose_function_resource_tracker_(std::move(other.decompose_function_resource_tracker_)), + decompose_function_(std::move(other.decompose_function_)) + { + other.decompose_function_ = NULL; + } + + template + inline JitLuDecomposition &JitLuDecomposition::operator=(JitLuDecomposition &&other) + { + LuDecomposition::operator=(std::move(other)); + compiler_ = std::move(other.compiler_); + decompose_function_resource_tracker_ = std::move(other.decompose_function_resource_tracker_); + decompose_function_ = std::move(other.decompose_function_); + other.decompose_function_ = NULL; + } + template inline JitLuDecomposition::JitLuDecomposition( std::shared_ptr compiler, diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 422be8ca7..68e554ab1 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -39,6 +39,26 @@ namespace micm FuncPtr alpha_minus_jacobian_ = nullptr; public: + JitRosenbrockSolver(const JitRosenbrockSolver&) = delete; + JitRosenbrockSolver& operator=(const JitRosenbrockSolver&) = delete; + JitRosenbrockSolver(JitRosenbrockSolver&& other) + : RosenbrockSolver(std::move(other)), + compiler_(std::move(other.compiler_)), + function_resource_tracker_(std::move(other.function_resource_tracker_)), + alpha_minus_jacobian_(std::move(other.alpha_minus_jacobian_)) + { + other.alpha_minus_jacobian_ = NULL; + } + + JitRosenbrockSolver& operator=(JitRosenbrockSolver&& other) + { + RosenbrockSolver::operator=(std::move(other)); + compiler_ = std::move(other.compiler_); + function_resource_tracker_ = std::move(other.function_resource_tracker_); + alpha_minus_jacobian_ = std::move(other.alpha_minus_jacobian_); + other.alpha_minus_jacobian_ = NULL; + } + /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters /// @param system The chemical system to create the solver for /// @param processes The collection of chemical processes that will be applied during solving diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 4a2d3a548..6cb022c19 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -467,7 +467,7 @@ namespace micm builder = builder.with_element(i, i); jacobian_ = builder; - linear_solver_ = create_linear_solver(jacobian_, 1.0e-30); + linear_solver_ = std::move(create_linear_solver(jacobian_, 1.0e-30)); process_set_.SetJacobianFlatIds(jacobian_); for (std::size_t i = 0; i < jacobian_[0].size(); ++i) jacobian_diagonal_elements_.push_back(jacobian_.VectorIndex(0, i, i)); diff --git a/test/regression/RosenbrockChapman/CMakeLists.txt b/test/regression/RosenbrockChapman/CMakeLists.txt index 90a3da090..c97ed3db3 100644 --- a/test/regression/RosenbrockChapman/CMakeLists.txt +++ b/test/regression/RosenbrockChapman/CMakeLists.txt @@ -13,4 +13,5 @@ create_standard_test(NAME regression_test_solve SOURCES regression_test_solve.cp if(ENABLE_LLVM) create_standard_test(NAME regression_test_jit_p_force SOURCES regression_test_jit_p_force.cpp) create_standard_test(NAME regression_test_jit_dforce_dy SOURCES regression_test_jit_dforce_dy.cpp) + create_standard_test(NAME regression_test_jit_solve SOURCES regression_test_jit_solve.cpp) endif() \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp b/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp new file mode 100644 index 000000000..b8a488113 --- /dev/null +++ b/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp @@ -0,0 +1,52 @@ +#include + +#include +#include + +#include "jit_util.hpp" +#include "regression_test_solve_policy.hpp" + +TEST(RegressionJitRosenbrock, TwoStageSolve) +{ + auto solver = getTwoStageMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testSolve<>(solver, 1.0e-2); +} + +TEST(RegressionJitRosenbrock, ThreeStageSolve) +{ + auto solver = getThreeStageMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testSolve<>(solver, 1.0e-2); +} + +TEST(RegressionJitRosenbrock, FourStageSolve) +{ + auto solver = getFourStageMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testSolve<>(solver, 1.0e-2); +} + +TEST(RegressionJitRosenbrock, FourStageDASolve) +{ + auto solver = getFourStageDAMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testSolve<>(solver, 1.0e-2); +} + +TEST(RegressionJitRosenbrock, SixStageDASolve) +{ + auto solver = getSixStageDAMultiCellJitChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + testSolve<>(solver, 1.0e-2); +} \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_solve.cpp b/test/regression/RosenbrockChapman/regression_test_solve.cpp index a98c6d1da..5d429eb63 100644 --- a/test/regression/RosenbrockChapman/regression_test_solve.cpp +++ b/test/regression/RosenbrockChapman/regression_test_solve.cpp @@ -1,93 +1,8 @@ #include #include -#include -#include -#include -#include -#include -#include "chapman_ode_solver.hpp" -#include "util.hpp" - -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -void testSolve(micm::RosenbrockSolver solver, double relative_tolerance = 1.0e-8) -{ - auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); - micm::ChapmanODESolver fixed_solver{}; - - auto state = solver.GetState(); - auto fixed_state = fixed_solver.GetState(); - - // set conditions - const std::vector> photo_rates{ { 1.0e-4, 1.0e-5, 1.0e-6 }, - { 3.2e-4, 7.3e-5, 3.2e-6 }, - { 5.2e-4, 8.2e-5, 4.6e-6 } }; - state.custom_rate_parameters_ = photo_rates; - state.conditions_[0].temperature_ = 284.19; // [K] - state.conditions_[0].pressure_ = 101245.0; // [Pa] - state.conditions_[1].temperature_ = 215.02; // [K] - state.conditions_[1].pressure_ = 100789.2; // [Pa] - state.conditions_[2].temperature_ = 299.31; // [K] - state.conditions_[2].pressure_ = 101398.0; // [Pa] - std::vector> variables(3, std::vector(fixed_state.variables_[0].size(), 0.0)); - double abs_tol = 0.0; - for (int i = 0; i < 3; ++i) - for (int j = 0; j < fixed_state.variables_[0].size(); ++j) - { - variables[i][j] = get_double(); - abs_tol = std::max(abs_tol, variables[i][j]); - } - state.variables_ = variables; - abs_tol *= 1.0e-12; - - // run solvers - auto results = solver.Solve(500.0, state); - micm::ChapmanODESolver::SolverResult fixed_results[3]; - for (int i = 0; i < 3; ++i) - { - for (int j = 0; j < 3; ++j) - fixed_state.custom_rate_parameters_[0][j] = photo_rates[i][j]; - fixed_state.conditions_[0].temperature_ = state.conditions_[i].temperature_; - fixed_state.conditions_[0].pressure_ = state.conditions_[i].pressure_; - for (int j = 0; j < fixed_state.variables_[0].size(); ++j) - fixed_state.variables_[0][j] = variables[i][state.variable_map_[fixed_solver.species_names()[j]]]; - fixed_solver.UpdateState(fixed_state); - fixed_results[i] = fixed_solver.Solve(0.0, 500.0, fixed_state); - } - - // compare results - for (int i = 0; i < 3; ++i) - for (int j = 0; j < fixed_results[i].result_.size(); ++j) - { - double a = results.result_[i][state.variable_map_[fixed_solver.species_names()[j]]]; - double b = fixed_results[i].result_[j]; - EXPECT_NEAR(a, b, (std::abs(a) + std::abs(b)) * relative_tolerance + abs_tol); - } -} - -template -using DenseMatrix = micm::Matrix; -template -using SparseMatrix = micm::SparseMatrix; - -template -using Group1VectorMatrix = micm::VectorMatrix; -template -using Group2VectorMatrix = micm::VectorMatrix; -template -using Group3VectorMatrix = micm::VectorMatrix; -template -using Group4VectorMatrix = micm::VectorMatrix; - -template -using Group1SparseVectorMatrix = micm::SparseMatrix>; -template -using Group2SparseVectorMatrix = micm::SparseMatrix>; -template -using Group3SparseVectorMatrix = micm::SparseMatrix>; -template -using Group4SparseVectorMatrix = micm::SparseMatrix>; +#include "regression_test_solve_policy.hpp" TEST(RegressionRosenbrock, TwoStageSolve) { @@ -121,15 +36,27 @@ TEST(RegressionRosenbrock, SixStageDASolve) TEST(RegressionRosenbrock, VectorSolve) { - auto solver1 = getThreeStageMultiCellChapmanSolver>(3); + auto solver1 = getThreeStageMultiCellChapmanSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::LinearSolver>(3); testSolve(solver1); - auto solver2 = getThreeStageMultiCellChapmanSolver>(3); + auto solver2 = getThreeStageMultiCellChapmanSolver< + Group2VectorMatrix, + Group2SparseVectorMatrix, + micm::LinearSolver>(3); testSolve(solver2); - auto solver3 = getThreeStageMultiCellChapmanSolver>(3); + auto solver3 = getThreeStageMultiCellChapmanSolver< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::LinearSolver>(3); testSolve(solver3); - auto solver4 = getThreeStageMultiCellChapmanSolver>(3); + auto solver4 = getThreeStageMultiCellChapmanSolver< + Group4VectorMatrix, + Group4SparseVectorMatrix, + micm::LinearSolver>(3); testSolve(solver4); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_solve_policy.hpp b/test/regression/RosenbrockChapman/regression_test_solve_policy.hpp new file mode 100644 index 000000000..2d1408176 --- /dev/null +++ b/test/regression/RosenbrockChapman/regression_test_solve_policy.hpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#include "chapman_ode_solver.hpp" +#include "util.hpp" + +template +void testSolve(OdeSolverPolicy& solver, double relative_tolerance = 1.0e-8) +{ + auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); + micm::ChapmanODESolver fixed_solver{}; + + auto state = solver.GetState(); + auto fixed_state = fixed_solver.GetState(); + + // set conditions + const std::vector> photo_rates{ { 1.0e-4, 1.0e-5, 1.0e-6 }, + { 3.2e-4, 7.3e-5, 3.2e-6 }, + { 5.2e-4, 8.2e-5, 4.6e-6 } }; + state.custom_rate_parameters_ = photo_rates; + state.conditions_[0].temperature_ = 284.19; // [K] + state.conditions_[0].pressure_ = 101245.0; // [Pa] + state.conditions_[1].temperature_ = 215.02; // [K] + state.conditions_[1].pressure_ = 100789.2; // [Pa] + state.conditions_[2].temperature_ = 299.31; // [K] + state.conditions_[2].pressure_ = 101398.0; // [Pa] + std::vector> variables(3, std::vector(fixed_state.variables_[0].size(), 0.0)); + double abs_tol = 0.0; + for (int i = 0; i < 3; ++i) + for (int j = 0; j < fixed_state.variables_[0].size(); ++j) + { + variables[i][j] = get_double(); + abs_tol = std::max(abs_tol, variables[i][j]); + } + state.variables_ = variables; + abs_tol *= 1.0e-12; + + // run solvers + auto results = solver.Solve(500.0, state); + micm::ChapmanODESolver::SolverResult fixed_results[3]; + for (int i = 0; i < 3; ++i) + { + for (int j = 0; j < 3; ++j) + fixed_state.custom_rate_parameters_[0][j] = photo_rates[i][j]; + fixed_state.conditions_[0].temperature_ = state.conditions_[i].temperature_; + fixed_state.conditions_[0].pressure_ = state.conditions_[i].pressure_; + for (int j = 0; j < fixed_state.variables_[0].size(); ++j) + fixed_state.variables_[0][j] = variables[i][state.variable_map_[fixed_solver.species_names()[j]]]; + fixed_solver.UpdateState(fixed_state); + fixed_results[i] = fixed_solver.Solve(0.0, 500.0, fixed_state); + } + + // compare results + for (int i = 0; i < 3; ++i) + for (int j = 0; j < fixed_results[i].result_.size(); ++j) + { + double a = results.result_[i][state.variable_map_[fixed_solver.species_names()[j]]]; + double b = fixed_results[i].result_[j]; + EXPECT_NEAR(a, b, (std::abs(a) + std::abs(b)) * relative_tolerance + abs_tol); + } +} + +template +using DenseMatrix = micm::Matrix; +template +using SparseMatrix = micm::SparseMatrix; + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; \ No newline at end of file From 806d1d4ad3fc8a90512efb878a8ddadb33df6e4d Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Tue, 3 Oct 2023 17:09:25 -0700 Subject: [PATCH 067/318] add first JitRosenbrockSolver analytical integration test --- include/micm/process/jit_process_set.hpp | 5 + include/micm/solver/jit_linear_solver.inl | 5 + include/micm/solver/jit_lu_decomposition.inl | 6 +- include/micm/solver/jit_rosenbrock.hpp | 6 + test/integration/CMakeLists.txt | 6 +- .../integration/analytical_jit_rosenbrock.cpp | 33 + .../{analytical.cpp => analytical_policy.hpp} | 26 +- test/integration/analytical_rosenbrock.cpp | 2036 +++++++++++++++++ 8 files changed, 2105 insertions(+), 18 deletions(-) create mode 100644 test/integration/analytical_jit_rosenbrock.cpp rename test/integration/{analytical.cpp => analytical_policy.hpp} (99%) create mode 100644 test/integration/analytical_rosenbrock.cpp diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index d2c0e9eed..73ce47c08 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -99,6 +99,7 @@ namespace micm jacobian_function_ = std::move(other.jacobian_function_); other.forcing_function_ = NULL; other.jacobian_function_ = NULL; + return *this; } template @@ -112,6 +113,10 @@ namespace micm { forcing_function_ = NULL; jacobian_function_ = NULL; + if (state.variables_.size() != L || state.variables_.GroupVectorSize() != L) + { + throw std::runtime_error("Invalid state for JitProcessSet. Check the the VectorMatrix template parameters."); + } this->GenerateForcingFunction(); } diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index 69f1158e7..35d1cf98f 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -23,6 +23,7 @@ namespace micm solve_function_resource_tracker_ = std::move(other.solve_function_resource_tracker_); solve_function_ = std::move(other.solve_function_); other.solve_function_ = NULL; + return *this; } template class SparseMatrixPolicy, class LuDecompositionPolicy> @@ -38,6 +39,10 @@ namespace micm compiler_(compiler) { solve_function_ = NULL; + if (matrix.size() != L || matrix.GroupVectorSize() != L) + { + throw std::runtime_error("Invalid matrix for JitLinearSolver. Check the the VectorMatrix template parameters."); + } GenerateSolveFunction(); } diff --git a/include/micm/solver/jit_lu_decomposition.inl b/include/micm/solver/jit_lu_decomposition.inl index ed3eee038..a9c0b038e 100644 --- a/include/micm/solver/jit_lu_decomposition.inl +++ b/include/micm/solver/jit_lu_decomposition.inl @@ -22,6 +22,7 @@ namespace micm decompose_function_resource_tracker_ = std::move(other.decompose_function_resource_tracker_); decompose_function_ = std::move(other.decompose_function_); other.decompose_function_ = NULL; + return *this; } template @@ -32,7 +33,10 @@ namespace micm compiler_(compiler) { decompose_function_ = NULL; - assert(matrix.size() <= L && "Jit LU Decomposition matrix size mismatch"); + if (matrix.size() > L) + { + throw std::runtime_error("Invalid matrix for JitLuDecomposition. Check the the VectorMatrix template parameters."); + } GenerateDecomposeFunction(); } diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 68e554ab1..14fd8b656 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -57,6 +57,7 @@ namespace micm function_resource_tracker_ = std::move(other.function_resource_tracker_); alpha_minus_jacobian_ = std::move(other.alpha_minus_jacobian_); other.alpha_minus_jacobian_ = NULL; + return *this; } /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters @@ -76,6 +77,11 @@ namespace micm }), compiler_(compiler) { + MatrixPolicy temp{}; + if (temp.GroupVectorSize() != parameters.number_of_grid_cells_) + { + throw std::runtime_error("Number of grid cells for JitRosenbrockSolver must match template parameter."); + } this->GenerateAlphaMinusJacobian(); } diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index cb34b13bb..463e70d86 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -7,4 +7,8 @@ include(test_util) # Tests create_standard_test(NAME chapman_integration SOURCES chapman.cpp) -create_standard_test(NAME analytical_integration SOURCES analytical.cpp) \ No newline at end of file +create_standard_test(NAME analytical_rosenbrock_integration SOURCES analytical_rosenbrock.cpp) + +if(ENABLE_LLVM) + create_standard_test(NAME analytical_jit_rosenbrock_integration SOURCES analytical_jit_rosenbrock.cpp) +endif() \ No newline at end of file diff --git a/test/integration/analytical_jit_rosenbrock.cpp b/test/integration/analytical_jit_rosenbrock.cpp new file mode 100644 index 000000000..1d63191c7 --- /dev/null +++ b/test/integration/analytical_jit_rosenbrock.cpp @@ -0,0 +1,33 @@ +#include + +#include + +#include "analytical_policy.hpp" + +template +using DefaultVectorMatrix = micm::VectorMatrix; + +template +using DefaultSparseVectorMatrix = micm::SparseMatrix>; + +using DefaultJitRosenbrockSolver = micm::JitRosenbrockSolver< + DefaultVectorMatrix, + DefaultSparseVectorMatrix, + micm::JitLinearSolver<1, DefaultSparseVectorMatrix, micm::JitLuDecomposition<1>>>; + +TEST(AnalyticalExamplesJitRosenbrock, Troe) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_troe( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} \ No newline at end of file diff --git a/test/integration/analytical.cpp b/test/integration/analytical_policy.hpp similarity index 99% rename from test/integration/analytical.cpp rename to test/integration/analytical_policy.hpp index 82654f1c6..c713b823d 100644 --- a/test/integration/analytical.cpp +++ b/test/integration/analytical_policy.hpp @@ -1,6 +1,5 @@ -#include - #include +#include #include #include #include @@ -9,19 +8,13 @@ #include #include #include -#include #include #include #include #include -#include #include #include -#include "e5.hpp" -#include "hires.hpp" -#include "oregonator.hpp" - constexpr size_t nsteps = 1000; double relative_difference(double a, double b) @@ -76,7 +69,9 @@ using yields = std::pair; template using SparseMatrixTest = micm::SparseMatrix; -TEST(AnalyticalExamples, Troe) +template +void test_analytical_troe( + const std::function&)> create_solver) { /* * A -> B, k1 @@ -110,11 +105,8 @@ TEST(AnalyticalExamples, Troe) .N_ = 0.8 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = + create_solver(micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2 }); double temperature = 272.5; double pressure = 101253.3; @@ -133,7 +125,7 @@ TEST(AnalyticalExamples, Troe) std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(3)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -193,6 +185,7 @@ TEST(AnalyticalExamples, Troe) } } +#if 0 TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) { /* @@ -2203,4 +2196,5 @@ TEST(AnalyticalExamples, E5) << "difference at (" << i << ", " << j << ")"; } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/test/integration/analytical_rosenbrock.cpp b/test/integration/analytical_rosenbrock.cpp new file mode 100644 index 000000000..19e50d5a2 --- /dev/null +++ b/test/integration/analytical_rosenbrock.cpp @@ -0,0 +1,2036 @@ +#include + +#include +#include + +#include "analytical_policy.hpp" +#include "e5.hpp" +#include "hires.hpp" +#include "oregonator.hpp" + +template +using SparseMatrixTest = micm::SparseMatrix; + +TEST(AnalyticalExamples, Troe) +{ + test_analytical_troe>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) +{ + /* + * A1 -> B, k1 + * A2 -> B, k1 + * A1 -> A2, k3 >>> k1 + * A2 -> A1, k4 >>> k1 + * B -> C, k2 + * + */ + + auto a1 = micm::Species("A1"); + auto a2 = micm::Species("A2"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(b, 1) }) + .rate_constant(micm::TroeRateConstant({ .k0_A_ = 4.0e-10 })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(b, 1) }) + .rate_constant(micm::TroeRateConstant({ .k0_A_ = 4.0e-10 })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::TroeRateConstant({ .k0_A_ = 1.2e-3, + .k0_B_ = 167, + .k0_C_ = 3, + .kinf_A_ = 136, + .kinf_B_ = 5, + .kinf_C_ = 24, + .Fc_ = 0.9, + .N_ = 0.8 })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(a2, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(a1, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k_0 = 4.0e-10; + double k_inf = 1; + double k1 = k_0 * air_density / (1.0 + k_0 * air_density / k_inf) * + std::pow(0.6, 1.0 / (1.0 + (1.0 / 1.0) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); + + // B->C reaction rate + k_0 = 1.2e-3 * std::exp(3.0 / temperature) * std::pow(temperature / 300.0, 167.0); + k_inf = 136.0 * std::exp(24.0 / temperature) * std::pow(temperature / 300.0, 5.0); + double k2 = k_0 * air_density / (1.0 + k_0 * air_density / k_inf) * + std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(4)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + state.SetConcentration(a1, 0.5); + state.SetConcentration(a2, 0.5); + state.SetConcentration(b, 0.0); + state.SetConcentration(c, 0.0); + + model_concentrations[0] = state.variables_[0]; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + auto header = state.variable_names_; + header.insert(header.begin(), "time"); + writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a1 = map.at("A1"); + size_t _a2 = map.at("A2"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4); + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4); + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4); + } +} + +TEST(AnalyticalExamples, Photolysis) +{ + /* + * A -> B, k1 + * B -> C, k2 + * + * Copying the CAMP example: https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_photolysis.F90 + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoA" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoB" })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k1 = 2e-3; + + // B->C reaction rate + double k2 = 3e-3; + + double time_step = 1.0; + micm::State state = solver.GetState(); + + state.SetCustomRateParameter("photoA", k1); + state.SetCustomRateParameter("photoB", k2); + + std::vector> model_concentrations(nsteps, std::vector(3)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + model_concentrations[0] = { 1, 0, 0 }; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + writeCSV("model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) +{ + /* + * A1 -> B, k1 + * A2 -> B, k1 + * A1 -> A2, k3 >>> k1 + * A2 -> A1, k4 >>> k1 + * B -> C, k2 + * + */ + + auto a1 = micm::Species("A1"); + auto a2 = micm::Species("A2"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(b, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoA1B" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(b, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoA2B" })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoB" })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(a2, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(a1, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k1 = 2e-3; + + // B->C reaction rate + double k2 = 3e-3; + + double time_step = 1.0; + micm::State state = solver.GetState(); + + state.SetCustomRateParameter("photoA1B", k1); + state.SetCustomRateParameter("photoA2B", k1); + state.SetCustomRateParameter("photoB", k2); + + std::vector> model_concentrations(nsteps, std::vector(3)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + state.SetConcentration(a1, 0.5); + state.SetConcentration(a2, 0.5); + state.SetConcentration(b, 0.0); + state.SetConcentration(c, 0.0); + + model_concentrations[0] = state.variables_[0]; + + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + writeCSV("model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a1 = map.at("A1"); + size_t _a2 = map.at("A2"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4); + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4); + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4); + } +} + +TEST(AnalyticalExamples, TernaryChemicalActivation) +{ + /* + * A -> B, k1 + * B -> C, k2 + * + * Copying the CAMP example: + * https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_ternary_chemical_activation.F90 + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 4.0e-10, .kinf_A_ = 1 })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2e-3, + .k0_B_ = 167, + .k0_C_ = 3, + .kinf_A_ = 136, + .kinf_B_ = 5, + .kinf_C_ = 24, + .Fc_ = 0.9, + .N_ = 0.8 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k_0 = 4.0e-10; + double k_inf = 1; + double k1 = k_0 / (1.0 + k_0 * air_density / k_inf) * + std::pow(0.6, 1.0 / (1.0 + (1.0 / 1.0) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); + + // B->C reaction rate + k_0 = 1.2e-3 * std::exp(3.0 / temperature) * std::pow(temperature / 300.0, 167.0); + k_inf = 136.0 * std::exp(24.0 / temperature) * std::pow(temperature / 300.0, 5.0); + double k2 = k_0 / (1.0 + k_0 * air_density / k_inf) * + std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(3)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + model_concentrations[0] = { 1, 0, 0 }; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + writeCSV("model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) +{ + /* + * A1 -> B, k1 + * A2 -> B, k1 + * A1 -> A2, k3 >>> k1 + * A2 -> A1, k4 >>> k1 + * B -> C, k2 + * + */ + + auto a1 = micm::Species("A1"); + auto a2 = micm::Species("A2"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(b, 1) }) + .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 4.0e-10, .kinf_A_ = 1 })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(b, 1) }) + .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 4.0e-10, .kinf_A_ = 1 })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2e-3, + .k0_B_ = 167, + .k0_C_ = 3, + .kinf_A_ = 136, + .kinf_B_ = 5, + .kinf_C_ = 24, + .Fc_ = 0.9, + .N_ = 0.8 })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(a2, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(a1, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k_0 = 4.0e-10; + double k_inf = 1; + double k1 = k_0 / (1.0 + k_0 * air_density / k_inf) * + std::pow(0.6, 1.0 / (1.0 + (1.0 / 1.0) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); + + // B->C reaction rate + k_0 = 1.2e-3 * std::exp(3.0 / temperature) * std::pow(temperature / 300.0, 167.0); + k_inf = 136.0 * std::exp(24.0 / temperature) * std::pow(temperature / 300.0, 5.0); + double k2 = k_0 / (1.0 + k_0 * air_density / k_inf) * + std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(4)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + state.SetConcentration(a1, 0.5); + state.SetConcentration(a2, 0.5); + state.SetConcentration(b, 0.0); + state.SetConcentration(c, 0.0); + + model_concentrations[0] = state.variables_[0]; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + auto header = state.variable_names_; + header.insert(header.begin(), "time"); + writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a1 = map.at("A1"); + size_t _a2 = map.at("A2"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4); + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4); + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4); + } +} + +TEST(AnalyticalExamples, Tunneling) +{ + /* + * A -> B, k1 + * B -> C, k2 + * + * Copying the CAMP example: + * https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_wennberg_tunneling.F90 + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::TunnelingRateConstant({ .A_ = 4.0e-3 })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::TunnelingRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 1.0e8 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k1 = 4.0e-3; + + // B->C reaction rate + double k2 = 1.2e-4 * std::exp(-167 / temperature + 1.0e8 / std::pow(temperature, 3)); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(3)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + model_concentrations[0] = { 1, 0, 0 }; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + writeCSV("model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) +{ + /* + * A1 -> B, k1 + * A2 -> B, k1 + * A1 -> A2, k3 >>> k1 + * A2 -> A1, k4 >>> k1 + * B -> C, k2 + * + */ + + auto a1 = micm::Species("A1"); + auto a2 = micm::Species("A2"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(b, 1) }) + .rate_constant(micm::TunnelingRateConstant({ .A_ = 4.0e-3 })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(b, 1) }) + .rate_constant(micm::TunnelingRateConstant({ .A_ = 4.0e-3 })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::TunnelingRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 1.0e8 })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(a2, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(a1, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k1 = 4.0e-3; + + // B->C reaction rate + double k2 = 1.2e-4 * std::exp(-167 / temperature + 1.0e8 / std::pow(temperature, 3)); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(4)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + state.SetConcentration(a1, 0.5); + state.SetConcentration(a2, 0.5); + state.SetConcentration(b, 0.0); + state.SetConcentration(c, 0.0); + + model_concentrations[0] = state.variables_[0]; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + auto header = state.variable_names_; + header.insert(header.begin(), "time"); + writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a1 = map.at("A1"); + size_t _a2 = map.at("A2"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, Arrhenius) +{ + /* + * A -> B, k1 + * B -> C, k2 + * + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e-3, .C_ = 50 })) + .phase(gas_phase); + + micm::Process r2 = + micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 1.2e-4, .B_ = 7, .C_ = 75, .D_ = 50, .E_ = 0.5 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k1 = 4.0e-3 * std::exp(50 / temperature); + + // B->C reaction rate + double k2 = 1.2e-4 * std::exp(75 / temperature) * std::pow(temperature / 50, 7) * (1.0 + 0.5 * pressure); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(3)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + model_concentrations[0] = { 1, 0, 0 }; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + writeCSV("model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) +{ + /* + * A1 -> B, k1 + * A2 -> B, k1 + * A1 -> A2, k3 >>> k1 + * A2 -> A1, k4 >>> k1 + * B -> C, k2 + * + */ + + auto a1 = micm::Species("A1"); + auto a2 = micm::Species("A2"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(b, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e-3, .C_ = 50 })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(b, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e-3, .C_ = 50 })) + .phase(gas_phase); + + micm::Process r3 = + micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 75, .D_ = 50, .E_ = 0.5 })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(a2, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(a1, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double k1 = 4.0e-3 * std::exp(50 / temperature); + + // B->C reaction rate + double k2 = 1.2e-4 * std::exp(75 / temperature) * std::pow(temperature / 50, 167) * (1.0 + 0.5 * pressure); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(4)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + state.SetConcentration(a1, 0.5); + state.SetConcentration(a2, 0.5); + state.SetConcentration(b, 0.0); + state.SetConcentration(c, 0.0); + + model_concentrations[0] = state.variables_[0]; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + auto header = state.variable_names_; + header.insert(header.begin(), "time"); + writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a1 = map.at("A1"); + size_t _a2 = map.at("A2"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, Branched) +{ + /* + * A -> B, k1 + * B -> C, k2 + * + * Copying the CAMP example: https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_wennberg_no_ro2.F90 + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = + micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, + .X_ = 1.2, + .Y_ = 204.3, + .a0_ = 1.0e-3, + .n_ = 2 })) + .phase(gas_phase); + + micm::Process r2 = + micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Nitrate, + .X_ = 1.2, + .Y_ = 204.3, + .a0_ = 1.0e-3, + .n_ = 2 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double air_dens_n_cm3 = air_density * AVOGADRO_CONSTANT * 1.0e-6; + double a_ = 2.0e-22 * std::exp(2) * 2.45e19; + double b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); + double z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; + a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; + b_ = 0.43 * std::pow((temperature / 298.0), -8.0); + double A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); + + double k1 = 1.2 * std::exp(-204.3 / temperature) * (z / (z + A)); + + // B->C reaction rate + a_ = 2.0e-22 * std::exp(2) * 2.45e19; + b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); + z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; + a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; + b_ = 0.43 * std::pow((temperature / 298.0), -8.0); + A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); + + double k2 = 1.2 * std::exp(-204.3 / temperature) * (A / (z + A)); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(3)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + model_concentrations[0] = { 1, 0, 0 }; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + writeCSV("model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-3) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-3) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-3) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, BranchedSuperStiffButAnalytical) +{ + /* + * A1 -> B, k1 + * A2 -> B, k1 + * A1 -> A2, k3 >>> k1 + * A2 -> A1, k4 >>> k1 + * B -> C, k2 + * + */ + + auto a1 = micm::Species("A1"); + auto a2 = micm::Species("A2"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; + + micm::Process r1 = + micm::Process::create() + .reactants({ a1 }) + .products({ yields(b, 1) }) + .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, + .X_ = 1.2, + .Y_ = 204.3, + .a0_ = 1.0e-3, + .n_ = 2 })) + .phase(gas_phase); + + micm::Process r2 = + micm::Process::create() + .reactants({ a2 }) + .products({ yields(b, 1) }) + .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, + .X_ = 1.2, + .Y_ = 204.3, + .a0_ = 1.0e-3, + .n_ = 2 })) + .phase(gas_phase); + + micm::Process r3 = + micm::Process::create() + .reactants({ b }) + .products({ yields(c, 1) }) + .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Nitrate, + .X_ = 1.2, + .Y_ = 204.3, + .a0_ = 1.0e-3, + .n_ = 2 })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a1 }) + .products({ yields(a2, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a2 }) + .products({ yields(a1, 1) }) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + // A->B reaction rate + double air_dens_n_cm3 = air_density * AVOGADRO_CONSTANT * 1.0e-6; + double a_ = 2.0e-22 * std::exp(2) * 2.45e19; + double b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); + double z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; + a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; + b_ = 0.43 * std::pow((temperature / 298.0), -8.0); + double A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); + + double k1 = 1.2 * std::exp(-204.3 / temperature) * (z / (z + A)); + + // B->C reaction rate + a_ = 2.0e-22 * std::exp(2) * 2.45e19; + b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); + z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; + a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; + b_ = 0.43 * std::pow((temperature / 298.0), -8.0); + A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); + + double k2 = 1.2 * std::exp(-204.3 / temperature) * (A / (z + A)); + + double time_step = 1.0; + micm::State state = solver.GetState(); + + std::vector> model_concentrations(nsteps, std::vector(4)); + std::vector> analytical_concentrations(nsteps, std::vector(3)); + + state.SetConcentration(a1, 0.5); + state.SetConcentration(a2, 0.5); + state.SetConcentration(b, 0.0); + state.SetConcentration(c, 0.0); + + model_concentrations[0] = state.variables_[0]; + analytical_concentrations[0] = { 1, 0, 0 }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + size_t idx_A = 0, idx_B = 1, idx_C = 2; + + std::vector times; + times.push_back(0); + for (size_t i_time = 1; i_time < nsteps; ++i_time) + { + times.push_back(time_step); + // Model results + auto result = solver.Solve(time_step, state); + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_concentrations[i_time] = result.result_.AsVector(); + state.variables_[0] = result.result_.AsVector(); + + // Analytical results + double time = i_time * time_step; + + double initial_A = analytical_concentrations[0][idx_A]; + analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); + analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); + + analytical_concentrations[i_time][idx_C] = + initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); + } + + auto header = state.variable_names_; + header.insert(header.begin(), "time"); + writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); + + auto map = state.variable_map_; + + size_t _a1 = map.at("A1"); + size_t _a2 = map.at("A2"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-3) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-3) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-3) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, Robertson) +{ + /* + * A -> B, k1 = 0.04 + * B + B -> C + B, k2 = 3e7 + * B + C -> A + C, k3 = 1e4 + * + * this problem is described in + * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd + * edition. ed. Springer, Berlin ; New York. Page 3 + * + * solutions are provided here + * https://www.unige.ch/~hairer/testset/testset.html + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b, b }) + .products({ yields(b, 1), yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b, c }) + .products({ yields(a, 1), yields(c, 1) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + double temperature = 272.5; + double pressure = 101253.3; + double air_density = 1e6; + + micm::State state = solver.GetState(); + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + + state.SetCustomRateParameter("r1", k1); + state.SetCustomRateParameter("r2", k2); + state.SetCustomRateParameter("r3", k3); + + constexpr size_t N = 12; + + std::vector> model_concentrations(N + 1, std::vector(3)); + std::vector> analytical_concentrations(N + 1, std::vector(3)); + + model_concentrations[0] = { 1, 0, 0 }; + + analytical_concentrations = { { 1, 0, 0 }, + { 0.9664597373330035E+00, 0.3074626578578675E-04, 0.3350951640121071E-01 }, + { 0.8413699238414729E+00, 0.1623390937990473E-04, 0.1586138422491472E+00 }, + { 0.6172348823960878E+00, 0.6153591274639123E-05, 0.3827589640126376E+00 }, + { 0.3368745306607069E+00, 0.2013702318261393E-05, 0.6631234556369748E+00 }, + { 0.1073004285378040E+00, 0.4800166972571660E-06, 0.8926990914454987E+00 }, + { 0.1786592114209946E-01, 0.7274751468436319E-07, 0.9821340061103859E+00 }, + { 0.2031483924973415E-02, 0.8142277783356159E-08, 0.9979685079327488E+00 }, + { 0.2076093439016395E-03, 0.8306077485067610E-09, 0.9997923898254906E+00 }, + { 0.2082417512179460E-04, 0.8329841429908955E-10, 0.9999791757415798E+00 }, + { 0.2083229471647004E-05, 0.8332935037760723E-11, 0.9999979167621954E+00 }, + { 0.2083328471883087E-06, 0.8333315602809495E-12, 0.9999997916663195E+00 }, + { 0.2083340149701284E-07, 0.8333360770334744E-13, 0.9999999791665152E+00 } }; + + state.variables_[0] = model_concentrations[0]; + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + double time_step = 1.0; + std::vector times; + times.push_back(0); + for (size_t i_time = 0; i_time < N; ++i_time) + { + double solve_time = time_step + i_time * time_step; + times.push_back(solve_time); + // Model results + double actual_solve = 0; + while (actual_solve < time_step) + { + auto result = solver.Solve(time_step - actual_solve, state); + state.variables_[0] = result.result_.AsVector(); + actual_solve += result.final_time_; + } + model_concentrations[i_time + 1] = state.variables_[0]; + time_step *= 10; + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("model_concentrations.csv", header, model_concentrations, times); + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + double tol = 1e-1; + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], tol) + << "Arrays differ at index (" << i << ", " << 0 << ")"; + EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], tol) + << "Arrays differ at index (" << i << ", " << 1 << ")"; + EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], tol) + << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, Oregonator) +{ + /* + * I think these are the equations, but I'm really not sure. I don't know how this translates to the jacobian + * and forcing functions used by the ODE book: https://www.unige.ch/~hairer/testset/stiff/orego/equation.f + * A+Y -> X+P + * X+Y -> 2P + * A+X -> 2X+2Z + * 2X -> A+P + * B+Z -> 1/2fY + * + * this problem is described in + * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd + * edition. ed. Springer, Berlin ; New York. Page 3 + * + * solutions are provided here + * https://www.unige.ch/~hairer/testset/testset.html + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ b }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); + params.relative_tolerance_ = 1e-4; + params.absolute_tolerance_ = 1e-6 * params.relative_tolerance_; + Oregonator solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, params); + + double end = 360; + double time_step = 30; + size_t N = static_cast(end / time_step); + + std::vector> model_concentrations(N + 1, std::vector(3)); + std::vector> analytical_concentrations(13, std::vector(3)); + + model_concentrations[0] = { 1, 2, 3 }; + + analytical_concentrations = { + { 1, 2, 3 }, + { 0.1000661467180497E+01, 0.1512778937348249E+04, 0.1035854312767229E+05 }, + { 0.1000874625199626E+01, 0.1144336972384497E+04, 0.8372149966624639E+02 }, + { 0.1001890368438751E+01, 0.5299926232295553E+03, 0.1662279579042420E+01 }, + { 0.1004118022612645E+01, 0.2438326079910346E+03, 0.1008822224048647E+01 }, + { 0.1008995416634061E+01, 0.1121664388662539E+03, 0.1007783229065319E+01 }, + { 0.1019763472537298E+01, 0.5159761322947535E+02, 0.1016985778956374E+01 }, + { 0.1043985088527474E+01, 0.2373442027531524E+02, 0.1037691843544522E+01 }, + { 0.1100849071667922E+01, 0.1091533805469020E+02, 0.1085831969810860E+01 }, + { 0.1249102130020572E+01, 0.5013945178605446E+01, 0.1208326626237875E+01 }, + { 0.1779724751937019E+01, 0.2281852385542403E+01, 0.1613754023671725E+01 }, + { 0.1000889326903503E+01, 0.1125438585746596E+04, 0.1641049483777168E+05 }, + { 0.1000814870318523E+01, 0.1228178521549889E+04, 0.1320554942846513E+03 }, + }; + + micm::State state = solver.GetState(); + + state.variables_[0] = model_concentrations[0]; + + std::vector times; + times.push_back(0); + for (size_t i_time = 0; i_time < N; ++i_time) + { + double solve_time = time_step + i_time * time_step; + times.push_back(solve_time); + // Model results + double actual_solve = 0; + while (actual_solve < time_step) + { + auto result = solver.Solve(time_step - actual_solve, state); + state.variables_[0] = result.result_.AsVector(); + actual_solve += result.final_time_; + } + model_concentrations[i_time + 1] = state.variables_[0]; + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("model_concentrations.csv", header, model_concentrations, times); + std::vector an_times; + an_times.push_back(0); + for (int i = 1; i <= 12; ++i) + { + an_times.push_back(30 * i); + } + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, an_times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + double tol = 1e-3; + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + double rel_diff = relative_difference(model_concentrations[i][_a], analytical_concentrations[i][0]); + EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 0 << ")"; + rel_diff = relative_difference(model_concentrations[i][_b], analytical_concentrations[i][1]); + EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 1 << ")"; + rel_diff = relative_difference(model_concentrations[i][_c], analytical_concentrations[i][2]); + EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, Oregonator2) +{ + /* Equations derived from the forcing function here: https://www.unige.ch/~hairer/testset/stiff/orego/equation.f + * a + b -> ( 1 - (1/77.27)^2 ) b k = 77.27 + * c -> ( 1 / (0.161 * 77.27) ) b k = 0.161 + * b -> ( 77.27 )^2 a k = 1/77.27 + * a -> 2 a + ( 0.161/77.27 ) c k = 77.27 + * a + a -> NULL k = 77.27 * 8.375e-6 + * + * this problem is described in + * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd + * edition. ed. Springer, Berlin ; New York. Page 3 + * + * solutions are provided here + * https://www.unige.ch/~hairer/testset/testset.html + */ + + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + + micm::Phase gas_phase{ std::vector{ a, b, c } }; + + micm::Process r1 = micm::Process::create() + .reactants({ a, b }) + .products({ yields(b, 1 - std::pow((1 / 77.27), 2)) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ c }) + .products({ yields(b, 1 / (0.161 * 77.27)) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create() + .reactants({ b }) + .products({ yields(a, std::pow(77.27, 2)) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + micm::Process r4 = micm::Process::create() + .reactants({ a }) + .products({ yields(a, 2), yields(c, 0.161 / 77.27) }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r4" })) + .phase(gas_phase); + + micm::Process r5 = micm::Process::create() + .reactants({ a, a }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r5" })) + .phase(gas_phase); + + auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); + params.relative_tolerance_ = 1e-4; + params.absolute_tolerance_ = 1e-6 * params.relative_tolerance_; + Oregonator solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5 }, + params); + + double end = 360; + double time_step = 30; + size_t N = static_cast(end / time_step); + + std::vector> model_concentrations(N + 1, std::vector(3)); + std::vector> analytical_concentrations(13, std::vector(3)); + + model_concentrations[0] = { 1, 2, 3 }; + + analytical_concentrations = { + { 1, 2, 3 }, + { 0.1000661467180497E+01, 0.1512778937348249E+04, 0.1035854312767229E+05 }, + { 0.1000874625199626E+01, 0.1144336972384497E+04, 0.8372149966624639E+02 }, + { 0.1001890368438751E+01, 0.5299926232295553E+03, 0.1662279579042420E+01 }, + { 0.1004118022612645E+01, 0.2438326079910346E+03, 0.1008822224048647E+01 }, + { 0.1008995416634061E+01, 0.1121664388662539E+03, 0.1007783229065319E+01 }, + { 0.1019763472537298E+01, 0.5159761322947535E+02, 0.1016985778956374E+01 }, + { 0.1043985088527474E+01, 0.2373442027531524E+02, 0.1037691843544522E+01 }, + { 0.1100849071667922E+01, 0.1091533805469020E+02, 0.1085831969810860E+01 }, + { 0.1249102130020572E+01, 0.5013945178605446E+01, 0.1208326626237875E+01 }, + { 0.1779724751937019E+01, 0.2281852385542403E+01, 0.1613754023671725E+01 }, + { 0.1000889326903503E+01, 0.1125438585746596E+04, 0.1641049483777168E+05 }, + { 0.1000814870318523E+01, 0.1228178521549889E+04, 0.1320554942846513E+03 }, + }; + + micm::State state = solver.GetState(); + + double k1 = 77.27; + double k2 = 0.161; + double k3 = 1 / 77.27; + double k4 = 77.27; + double k5 = 77.27 * 8.375e-6; + + state.SetCustomRateParameter("r1", k1); + state.SetCustomRateParameter("r2", k2); + state.SetCustomRateParameter("r3", k3); + state.SetCustomRateParameter("r4", k4); + state.SetCustomRateParameter("r5", k5); + + state.variables_[0] = model_concentrations[0]; + + std::vector times; + times.push_back(0); + for (size_t i_time = 0; i_time < N; ++i_time) + { + double solve_time = time_step + i_time * time_step; + times.push_back(solve_time); + // Model results + double actual_solve = 0; + while (actual_solve < time_step) + { + auto result = solver.Solve(time_step - actual_solve, state); + state.variables_[0] = result.result_.AsVector(); + actual_solve += result.final_time_; + } + model_concentrations[i_time + 1] = state.variables_[0]; + } + + std::vector header = { "time", "A", "B", "C" }; + writeCSV("model_concentrations.csv", header, model_concentrations, times); + std::vector an_times; + an_times.push_back(0); + for (int i = 1; i <= 12; ++i) + { + an_times.push_back(30 * i); + } + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, an_times); + + auto map = state.variable_map_; + + size_t _a = map.at("A"); + size_t _b = map.at("B"); + size_t _c = map.at("C"); + + double tol = 1e-3; + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + double rel_diff = relative_difference(model_concentrations[i][_a], analytical_concentrations[i][0]); + EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 0 << ")"; + rel_diff = relative_difference(model_concentrations[i][_b], analytical_concentrations[i][1]); + EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 1 << ")"; + rel_diff = relative_difference(model_concentrations[i][_c], analytical_concentrations[i][2]); + EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 2 << ")"; + } +} + +TEST(AnalyticalExamples, HIRES) +{ + /* + * No idea what these equations are + * + * this problem is described in + * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd + * edition. ed. Springer, Berlin ; New York. Page 3 + * + * solutions are provided here + * https://www.unige.ch/~hairer/testset/testset.html + */ + + auto y1 = micm::Species("y1"); + auto y2 = micm::Species("y2"); + auto y3 = micm::Species("y3"); + auto y4 = micm::Species("y4"); + auto y5 = micm::Species("y5"); + auto y6 = micm::Species("y6"); + auto y7 = micm::Species("y7"); + auto y8 = micm::Species("y8"); + + micm::Phase gas_phase{ std::vector{ y1, y2, y3, y4, y5, y6, y7, y8 } }; + + micm::Process r1 = micm::Process::create() + .reactants({ y1 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + micm::Process r2 = micm::Process::create() + .reactants({ y2 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + micm::Process r3 = micm::Process::create() + .reactants({ y3 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + micm::Process r4 = micm::Process::create() + .reactants({ y4 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r4" })) + .phase(gas_phase); + micm::Process r5 = micm::Process::create() + .reactants({ y5 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r5" })) + .phase(gas_phase); + micm::Process r6 = micm::Process::create() + .reactants({ y6 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r6" })) + .phase(gas_phase); + micm::Process r7 = micm::Process::create() + .reactants({ y7 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r7" })) + .phase(gas_phase); + micm::Process r8 = micm::Process::create() + .reactants({ y8 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r8" })) + .phase(gas_phase); + + auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); + params.relative_tolerance_ = 1e-3; + params.absolute_tolerance_ = params.relative_tolerance_ * 1e-4; + HIRES solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3, r4, r5, r6, r7, r8 }, + params); + + size_t N = 2; + + std::vector> model_concentrations(N + 1, std::vector(8)); + std::vector> analytical_concentrations(3, std::vector(8)); + + model_concentrations[0] = { 1, 0, 0, 0, 0, 0, 0, 0.0057 }; + + analytical_concentrations = { + { 1, 0, 0, 0, 0, 0, 0, 0.0057 }, + { 0.000737131257332567, + 0.000144248572631618, + 0.000058887297409676, + 0.001175651343283149, + 0.002386356198831330, + 0.006238968252742796, + 0.002849998395185769, + 0.002850001604814231 }, + { 0.000670305503581864, + 0.000130996846986347, + 0.000046862231597733, + 0.001044668020551705, + 0.000594883830951485, + 0.001399628833942774, + 0.001014492757718480, + 0.004685507242281520 }, + }; + + micm::State state = solver.GetState(); + + state.variables_[0] = model_concentrations[0]; + + std::vector times; + times.push_back(0); + double time_step = 321.8122; + for (size_t i_time = 0; i_time < N; ++i_time) + { + double solve_time = time_step + i_time * time_step; + times.push_back(solve_time); + // Model results + double actual_solve = 0; + while (actual_solve < time_step) + { + auto result = solver.Solve(time_step - actual_solve, state); + state.variables_[0] = result.result_.AsVector(); + actual_solve += result.final_time_; + } + model_concentrations[i_time + 1] = state.variables_[0]; + time_step += 100; + } + + std::vector header = { "time", "y1", "y2", "y3", "y4", "y5", "y6", "y7", "y8" }; + writeCSV("model_concentrations.csv", header, model_concentrations, times); + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + + double tol = 1e-5; + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + for (size_t j = 0; j < model_concentrations[0].size(); ++j) + { + double rel_diff = relative_difference(model_concentrations[i][j], analytical_concentrations[i][j]); + EXPECT_NEAR(model_concentrations[i][j], analytical_concentrations[i][j], tol); + } + } +} + +TEST(AnalyticalExamples, E5) +{ + /* + * No idea what these equations are + * + * this problem is described in + * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd + * edition. ed. Springer, Berlin ; New York. Page 3 + * + * solutions are provided here + * https://www.unige.ch/~hairer/testset/testset.html + */ + + auto y1 = micm::Species("y1"); + auto y2 = micm::Species("y2"); + auto y3 = micm::Species("y3"); + auto y4 = micm::Species("y4"); + + micm::Phase gas_phase{ std::vector{ y1, y2, y3, y4 } }; + + micm::Process r1 = micm::Process::create() + .reactants({ y1 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + micm::Process r2 = micm::Process::create() + .reactants({ y2 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + micm::Process r3 = micm::Process::create() + .reactants({ y3 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + micm::Process r4 = micm::Process::create() + .reactants({ y4 }) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r4" })) + .phase(gas_phase); + + auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); + params.relative_tolerance_ = 1e-2; + params.absolute_tolerance_ = 1.7e-24; + E5 solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4 }, params); + + size_t N = 7; + + std::vector> model_concentrations(N + 1, std::vector(4)); + std::vector> analytical_concentrations(N + 1, std::vector(4)); + + model_concentrations[0] = { 1.76e-3, 0, 0, 0 }; + + analytical_concentrations = { + { 1.76e-3, 0, 0, 0 }, + { 1.7599259497677897058e-003, 1.3846281519376516449e-011, 7.6370038530073911180e-013, 1.3082581134075777338e-011 }, + { 1.6180769999072942552e-003, 1.3822370304983735443e-010, 8.2515735006838336088e-012, 1.2997212954915352082e-010 }, + { 7.4813208224292220114e-006, 2.3734781561205975019e-012, 2.2123586689581663654e-012, 1.6111948716243113653e-013 }, + { 4.7150333630401632232e-010, 1.8188895860807021729e-014, 1.8188812376786725407e-014, 8.3484020296321693074e-020 }, + { 3.1317148329356996037e-014, 1.4840957952870064294e-016, 1.4840957948345691466e-016, 4.5243728279782625194e-026 }, + { 3.8139035189787091771e-049, 1.0192582567660293322e-020, 1.0192582567660293322e-020, 3.7844935507486221171e-065 }, + { 0.0000000000000000000e-000, 8.8612334976263783420e-023, 8.8612334976263783421e-023, 0.0000000000000000000e-000 } + }; + + micm::State state = solver.GetState(); + + state.variables_[0] = model_concentrations[0]; + + std::vector times; + times.push_back(0); + double time_step = 10; + for (size_t i_time = 0; i_time < N; ++i_time) + { + double solve_time = time_step + i_time * time_step; + times.push_back(solve_time); + // Model results + double actual_solve = 0; + while (actual_solve < time_step) + { + auto result = solver.Solve(time_step - actual_solve, state); + state.variables_[0] = result.result_.AsVector(); + actual_solve += result.final_time_; + } + model_concentrations[i_time + 1] = state.variables_[0]; + time_step *= 100; + } + + std::vector header = { "time", "y1", "y2", "y3", "y4" }; + writeCSV("model_concentrations.csv", header, model_concentrations, times); + writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); + + double tol = 1e-5; + for (size_t i = 0; i < model_concentrations.size(); ++i) + { + for (size_t j = 0; j < model_concentrations[0].size(); ++j) + { + double rel_diff = relative_difference(model_concentrations[i][j], analytical_concentrations[i][j]); + EXPECT_NEAR(model_concentrations[i][j], analytical_concentrations[i][j], tol) + << "difference at (" << i << ", " << j << ")"; + } + } +} \ No newline at end of file From 505b6913e07dc15c36644e2533ec305d7506719a Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 11:22:14 -0500 Subject: [PATCH 068/318] adding time to hello world example --- README.md | 22 ++++++++++---------- docs/source/getting_started.rst | 29 +++++++++++++++------------ test/tutorial/test_README_example.cpp | 12 ++++++++--- 3 files changed, 36 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 97ab0d751..864c37224 100644 --- a/README.md +++ b/README.md @@ -144,17 +144,17 @@ g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.1.0/include -std=c++20 Output: ``` -foo, bar, baz -19.034389, 0.762719, 0.197464 -18.105748, 1.478520, 0.395242 -17.213802, 2.150391, 0.592159 -16.358113, 2.781130, 0.787212 -15.538111, 3.373351, 0.979560 -14.753106, 3.929496, 1.168498 -14.002317, 4.451851, 1.353446 -13.284884, 4.942548, 1.533932 -12.599887, 5.403583, 1.709582 -11.946359, 5.836817, 1.880104 +time, foo, bar, baz + 0, 11.843503, 5.904845, 1.907012 + 1, 6.792023, 9.045965, 3.317336 + 2, 3.828700, 10.740589, 4.210461 + 3, 2.138145, 11.663685, 4.739393 + 4, 1.187934, 12.169452, 5.042503 + 5, 0.658129, 12.447502, 5.213261 + 6, 0.363985, 12.600676, 5.308597 + 7, 0.201076, 12.685147, 5.361559 + 8, 0.111028, 12.731727, 5.390884 + 9, 0.061290, 12.757422, 5.407096 ``` # Citation diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 695731edd..b33c4e8fb 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -86,17 +86,20 @@ To build and run the example using GNU:: $ g++ -o foo_chem foo_chem.cpp -I/include -std=c++20 $ ./foo_chem -Output:: - - foo, bar, baz - 19.034389, 0.762719, 0.197464 - 18.105748, 1.478520, 0.395242 - 17.213802, 2.150391, 0.592159 - 16.358113, 2.781130, 0.787212 - 15.538111, 3.373351, 0.979560 - 14.753106, 3.929496, 1.168498 - 14.002317, 4.451851, 1.353446 - 13.284884, 4.942548, 1.533932 - 12.599887, 5.403583, 1.709582 - 11.946359, 5.836817, 1.880104 +You should see an output including this + +.. csv-table:: + :header: "time", "foo", "bar", "baz" + :widths: 6, 10, 10, 10 + + 0, 11.843503, 5.904845, 1.907012 + 1, 6.792023, 9.045965, 3.317336 + 2, 3.828700, 10.740589, 4.210461 + 3, 2.138145, 11.663685, 4.739393 + 4, 1.187934, 12.169452, 5.042503 + 5, 0.658129, 12.447502, 5.213261 + 6, 0.363985, 12.600676, 5.308597 + 7, 0.201076, 12.685147, 5.361559 + 8, 0.111028, 12.731727, 5.390884 + 9, 0.061290, 12.757422, 5.407096 diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index 457594621..c088bc466 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -38,13 +38,19 @@ int main(const int argc, const char *argv[]) state.conditions_[0].pressure_ = 101319.9; // Pa state.SetConcentration(foo, 20.0); // mol m-3 - std::cout << "foo, bar, baz" << std::endl; + std::cout << std::setw(5) << "time," + << std::setw(13) << "foo, " + << std::setw(12) << "bar, " + << std::setw(10) << "baz" << std::endl; for (int i = 0; i < 10; ++i) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - std::cout << std::fixed << std::setprecision(6) << state.variables_[0][state.variable_map_["Foo"]] << ", " - << state.variables_[0][state.variable_map_["Bar"]] << ", " << state.variables_[0][state.variable_map_["Baz"]] + std::cout << std::fixed << std::setprecision(6) + << std::setw(4) << i << ", " + << std::setw(10) << state.variables_[0][state.variable_map_["Foo"]] << ", " + << std::setw(10) << state.variables_[0][state.variable_map_["Bar"]] << ", " + << std::setw(10) << state.variables_[0][state.variable_map_["Baz"]] << std::endl; } From dcb77b6d15c2503b880f7dff7d6d25f731f58705 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 13:30:08 -0500 Subject: [PATCH 069/318] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 38 +++++++++++++++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..dd84ea782 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbcbbe7d6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From db6e1007880dfbd3cfccdc41df85b5dc2084de1e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 13:33:18 -0500 Subject: [PATCH 070/318] adding code of conduct and contributors guide --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++ README.md | 4 +- docs/source/contributing/index.rst | 12 ++- 3 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..bf8c999a0 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +musica-support@ucar.edu. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. \ No newline at end of file diff --git a/README.md b/README.md index 97ab0d751..fcce9f423 100644 --- a/README.md +++ b/README.md @@ -185,10 +185,10 @@ cutting edge science. - Anyone interested in scientific collaboration which would add new software functionality should read the [MUSICA software development plan](https://github.com/NCAR/musica/blob/main/docs/Software%20Development%20Plan.pdf). -- [Code of conduct] +- [Code of conduct](CODE_OF_CONDUCT.md) - Please read this through to you understand the expectations with how to interact with this project. -- [Contributor's guide] +- [Contributor's guide](https://ncar.github.io/micm/contributing/index.html) - Before submiitting a PR, please thouroughly read this to you understand our expectations. We reserve the right to reject any PR not meeting our guidelines. diff --git a/docs/source/contributing/index.rst b/docs/source/contributing/index.rst index 486b24cf9..3acf97464 100644 --- a/docs/source/contributing/index.rst +++ b/docs/source/contributing/index.rst @@ -1,3 +1,13 @@ Contributing -============ \ No newline at end of file +============ + +For all proposed changes (bug fixes, new feature, documentation upates, etc.) please file +and [issue](https://github.com/NCAR/micm/issues/new/choose) detailing what your ask is or what you intend to do. + +The NCAR software developers will work with you on that issue to help recommend implementations, work through questions, +or provide other background knowledge. + +Any code that you contribute must include associated unit tests or an integration test of some kind. Our tests run across +various platforms and with different configurations automatically. By including tests that exercise your code, you help to +minimize the amount of time debugging platform portability issues. \ No newline at end of file From 1a8e7d246e6f0423ae144ae66718d12d73da499f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 13:39:27 -0500 Subject: [PATCH 071/318] 278 add tutorial for use with openmp (#280) * adding more build instructions * adding test for openmp * adding cmake stuff to detect libomp installed with homebrew * stubbed openmp tutorial --- CMakeLists.txt | 3 +- Dockerfile.openmp | 1 + cmake/dependencies.cmake | 35 ++++++ docs/source/_static/tutorials/robertson.zip | Bin 0 -> 857 bytes docs/source/getting_started.rst | 30 +++-- docs/source/user_guide/index.rst | 27 +++++ docs/source/user_guide/openmp.rst | 73 ++++++++++++ test/tutorial/CMakeLists.txt | 4 + .../tutorial/configs/robertson/reactions.json | 43 +++++++ test/tutorial/configs/robertson/species.json | 16 +++ test/tutorial/test_openmp.cpp | 109 ++++++++++++++++++ test/unit/CMakeLists.txt | 3 + test/unit/openmp/CMakeLists.txt | 9 ++ test/unit/openmp/test_openmp.cpp | 87 ++++++++++++++ .../unit_configs/robertson/reactions.json | 43 +++++++ test/unit/unit_configs/robertson/species.json | 16 +++ 16 files changed, 489 insertions(+), 10 deletions(-) create mode 100644 docs/source/_static/tutorials/robertson.zip create mode 100644 docs/source/user_guide/openmp.rst create mode 100644 test/tutorial/configs/robertson/reactions.json create mode 100644 test/tutorial/configs/robertson/species.json create mode 100644 test/tutorial/test_openmp.cpp create mode 100644 test/unit/openmp/CMakeLists.txt create mode 100644 test/unit/openmp/test_openmp.cpp create mode 100644 test/unit/unit_configs/robertson/reactions.json create mode 100644 test/unit/unit_configs/robertson/species.json diff --git a/CMakeLists.txt b/CMakeLists.txt index a74139d93..1bff9bfa4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ option(BUILD_DOCS "Build the documentation" OFF) option(ENABLE_CUDA "Build with Cuda support" OFF) option(ENABLE_OPENACC "Build with OpenACC Support" OFF) option(ENABLE_LLVM "Build with LLVM support for JIT-compiling" OFF) +option(ENABLE_TESTS "Build the tests" ON) include(CMakeDependentOption) # Option to collect custom OpenACC flags @@ -68,7 +69,7 @@ endif() ################################################################################ # Tests -if(PROJECT_IS_TOP_LEVEL) +if(PROJECT_IS_TOP_LEVEL AND ENABLE_TESTS) # Test code coverage if(ENABLE_COVERAGE) include(CodeCoverage) diff --git a/Dockerfile.openmp b/Dockerfile.openmp index ae5ae6f08..66ae9408a 100644 --- a/Dockerfile.openmp +++ b/Dockerfile.openmp @@ -18,6 +18,7 @@ RUN mkdir /build \ && cmake \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ -D ENABLE_OPENMP:BOOL=TRUE \ + -D ENABLE_JSON:BOOL=TRUE \ ../micm \ && make install -j 8 diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index b5fe2d8c5..423ca7c69 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -23,6 +23,41 @@ endif() # OpenMP if(ENABLE_OPENMP) + if(APPLE) + # Apple clang by default doesn't include support for openmp + # but if omp was installed with `brew install libomp`, support can be configured + if(CMAKE_C_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Set the C flags + set(OpenMP_C_FLAGS "-Xpreprocessor -fopenmp") + set(OpenMP_C_LIB_NAMES "omp") + set(OpenMP_omp_LIBRARY omp) + + # Set the CXX flags + set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp") + set(OpenMP_CXX_LIB_NAMES "omp") + set(OpenMP_omp_LIBRARY omp) + + # Assume that libomp is instaleld on mac with brew when using apple clang + # Get the path to libomp from Homebrew + execute_process( + COMMAND brew --prefix libomp + OUTPUT_VARIABLE LIBOMP_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + # Set the full path to the libomp library + set(OpenMP_omp_LIBRARY "${LIBOMP_PREFIX}/lib/libomp.dylib") + + # Set the include directory + set(LIBOMP_INCLUDE_DIR "${LIBOMP_PREFIX}/include") + + include_directories( + ${LIBOMP_INCLUDE_DIR} + ) + endif() + + endif() + find_package(OpenMP REQUIRED) message(STATUS "Compiling with OpenMP support") endif() diff --git a/docs/source/_static/tutorials/robertson.zip b/docs/source/_static/tutorials/robertson.zip new file mode 100644 index 0000000000000000000000000000000000000000..d3396649eb546a2935b83b6f57e0ea302edd467f GIT binary patch literal 857 zcmWIWW@Zs#00HX|mvArxN^mhKFcjq{r52SG=jZ8%hVU}5KW>#x1>sjfTw1}+z{t`+ zm4N}QKLBhF2LlI+IY)tJiJ_QNT#%ZanOdxu1vC`JOpv)~X0Cn~6cFI$cSiSuucwY@ z$flqUHZBc~kY>*XA#q80S^*&^13lT*LV_Y0#EQNZeC_4uD<}R60J;X`BJ6HeWoBTIKyhnPYGQIpW_})(T)R@k)NrSYvAzF7 zjg2Z>BNX-H7jCiG>Y=D=s%xxo>gvh9Y~PYi3~TOPzk2iV#q-8D#1&bM7fIxCZ}QdH z#ccD(`A@A*ng#`a} zch(9E39Z7!Ko8yc2HC|6LL!1VQzRz7mz521OuzIe!=>7O(hH|M|4NrM)%Q;P!<~Ax z=wQXhn62Qzy3Aep@;ES{KtU4V&B!Fej5~<{m4U%qM-W9yf`OO|OESn21WGb6u%rU`gW_6gx`_ + - `installation `_ -You must also have CMake installed on your machine. - -Open a terminal window, navigate to a folder where you would like the MICM files to exist, -and run the following commands:: +Then, it's enough for you to configure and install micm on your computer. Because micm is header-only library, the install +step will simply copy the header files into the normal location required by your system. +.. code-block:: console + $ git clone https://github.com/NCAR/micm.git $ cd micm $ mkdir build @@ -30,6 +28,20 @@ and run the following commands:: CMake will allow for setting options such as the installation directory with CMAKE_INSTALL_PREFIX, or various build flags such as BUILD_DOCS, ENABLE_CUDA, etc. +MICM can optionally include support for json configuration reading, OpenMP, +JIT-compiled chemistry functions, and GPUs. Each of these requires an additional library. +Some of these libraries can be included automatically with cmake build options, +others require that you have libraries installed on your system. + +- JSON configuration support + - When building micm, you need to enable the JSON option. This will download and configure the `nlohmann/jsoncpp library `_ for you. For example: ``cmake -DENABLE_JSON=ON ..`` +- JIT-compiled chemistry functions + - This requires `LLVM `_ to be installed with on your system. Once it is, you can include the jit options with ``cmake -DENBABLE_LLVM=ON ..`` +- GPU support + - Coming soon +- OpenMP + - On macOS, you either need to configure cmake to use gcc which ships with OpenMP (either ``CXX=g++ cmake -DENABLE_OPENMP=ON ..`` or ``cmake -DCMAKE_CXX_COMPILER=g++ -DENABLE_OPENMP=ON ..``) + Docker Container ~~~~~~~~~~~~~~~~ @@ -74,7 +86,7 @@ The following example solves the fictitious chemical system:: foo + bar --k2--> baz The `k1` and `k2` rate constants are for Arrhenius reactions. -See the [MICM documentation](https://ncar.github.io/micm/) +See the `MICM documentation ` for details on the types of reactions available in MICM and how to configure them. To solve this system save the following code in a file named `foo_chem.cpp` diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index e579327ff..b0a58ba74 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -9,6 +9,32 @@ If you happen to find our examples are lacking for your needs, please, `fill out an issue `_ and request the kind of example you'd like. +All of these tutorials are included in our automated tests. Each of them can be found in the code base in the +``test/tutorial`` directory. When building MICM with tests (the default), you can each test individually to see the output. + + +.. code-block:: console + + $ git clone https://github.com/NCAR/micm.git + $ cd micm + $ mkdir build + $ cd build + $ cmake .. + $ make + $ ./test_multiple_grid_cells + $ ./test_rate_constants_no_user_defined_example_by_hand + $ ./test_rate_constants_user_defined_example_by_hand + + +If you would like to include the json examples, you must configure micm to build with json support. + +.. code-block:: console + + $ cmake -DENABLE_JSON=ON .. + $ make + $ ./test_rate_constants_no_user_defined_example_with_config + $ ./test_rate_constants_user_defined_example_with_config + .. toctree:: :maxdepth: 1 @@ -17,3 +43,4 @@ If you happen to find our examples are lacking for your needs, please, rate_constant_tutorial user_defined_rate_constant_tutorial multiple_grid_cells + openmp diff --git a/docs/source/user_guide/openmp.rst b/docs/source/user_guide/openmp.rst new file mode 100644 index 000000000..fe20c65d3 --- /dev/null +++ b/docs/source/user_guide/openmp.rst @@ -0,0 +1,73 @@ +.. _OpenMP: + +OpenMP +====== + +This tutorial will focus on running micm with OpenMP support. +We will use a simple 3-reaction 3-species mechanism. The setup here is the same in +:ref:`Multiple grid cells`, except only one grid cell is used. + +.. math:: + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ + +If you're looking for a copy and paste, copy below and be on your way! Otherwise, stick around for a line by line explanation. + +.. tabs:: + + .. tab:: OpenAtmos Configuration reading + + .. raw:: html + + + + .. literalinclude:: ../../../test/tutorial/test_openmp.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +At present, the Rosenbrock class is not thread safe. Thread safety is being added and will be available soon in new release. + +Until then, you can run one instance of the Rosenbrock solver on its own thread. Configuration data, at least, can be shared +across the threads. This tutorial reads a configuraiton file and then configures three solvers, each on their own thread, +with that same configuration information. + +First we need to bring in the necessary imports and use the micm namespace. + +.. literalinclude:: ../../../test/tutorial/test_openmp.cpp + :language: cpp + :lines: 1-6 + +Then we'll define a funtion that we can call with the configuration information from each thread. This function will build +and run the rosenbrock solver. + +.. literalinclude:: ../../../test/tutorial/test_openmp.cpp + :language: cpp + :lines: 29-69 + +The main function simply reads the configuration file, sets the number of threads, and then sets up an OpenMP blcok +with three threads. The function defined above is called on each thread. + +.. literalinclude:: ../../../test/tutorial/test_openmp.cpp + :language: cpp + :lines: 71-109 + +Running this program should give an output similar to this: + +.. code-block:: console + + Thread 1 + A, B, C + 2.55e-01, 1.37e-06, 7.45e-01 + Thread 2 + A, B, C + 2.55e-01, 1.37e-06, 7.45e-01 + Thread 3 + A, B, C + 2.55e-01, 1.37e-06, 7.45e-01 \ No newline at end of file diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index a65cec863..9b662a07e 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -14,6 +14,10 @@ create_standard_test(NAME rate_constants_user_defined_example_by_hand SOURCES te if(ENABLE_JSON) create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) create_standard_test(NAME rate_constants_user_defined_example_with_config SOURCES test_rate_constants_user_defined_with_config.cpp) + + if(ENABLE_OPENMP) + create_standard_test(NAME openmp_tutorial SOURCES test_openmp.cpp) + endif() endif() ################################################################################ diff --git a/test/tutorial/configs/robertson/reactions.json b/test/tutorial/configs/robertson/reactions.json new file mode 100644 index 000000000..cbffae848 --- /dev/null +++ b/test/tutorial/configs/robertson/reactions.json @@ -0,0 +1,43 @@ +{ + "camp-data": [ + { + "name": "reaction rates no user defined", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "A": {} + }, + "products": { + "B": {} + }, + "MUSICA name": "r1" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": { "qty": 2} + }, + "products": { + "B": {}, + "C": {} + }, + "MUSICA name": "r2" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": {}, + "C": {} + }, + "products": { + "A": {}, + "C": {} + }, + "MUSICA name": "r3" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/tutorial/configs/robertson/species.json b/test/tutorial/configs/robertson/species.json new file mode 100644 index 000000000..5a1a8db68 --- /dev/null +++ b/test/tutorial/configs/robertson/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "A", + "type": "CHEM_SPEC" + }, + { + "name": "B", + "type": "CHEM_SPEC" + }, + { + "name": "C", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/tutorial/test_openmp.cpp b/test/tutorial/test_openmp.cpp new file mode 100644 index 000000000..ead3829ff --- /dev/null +++ b/test/tutorial/test_openmp.cpp @@ -0,0 +1,109 @@ +#include + +#include +#include + +using namespace micm; + +void print_header() +{ + std::cout << std::setw(10) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" << std::endl; +} + +void print_results(std::vector results) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::scientific << std::setprecision(2) + << std::setw(10) << results[0] << "," + << std::setw(10) << results[1] << "," + << std::setw(10) << results[2] + << std::endl; + + std::cout.copyfmt(oldState); +} + +std::vector test_solver_on_thread(System chemical_system, std::vector reactions) +{ + RosenbrockSolver<> solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; + State state = solver.GetState(); + + // mol m-3 + state.variables_[0] = { 1, 0, 0 }; + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("PHOTO.r1", k1); + state.SetCustomRateParameter("PHOTO.r2", k2); + state.SetCustomRateParameter("PHOTO.r3", k3); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + double time_step = 200; // s + + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + } + + return state.variables_.AsVector(); +} + +int main() +{ + constexpr size_t n_threads = 3; + + SolverConfig solverConfig; + + std::string config_path = "./configs/robertson"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + std::vector> results(3); + +#pragma omp parallel num_threads(n_threads) + { + std::vector result = test_solver_on_thread(chemical_system, reactions); + results[omp_get_thread_num()] = result; +#pragma omp barrier + } + + std::cout << "Thread 1" << std::endl; + print_header(); + print_results(results[0]); + + std::cout << "Thread 2" << std::endl; + print_header(); + print_results(results[1]); + + std::cout << "Thread 3" << std::endl; + print_header(); + print_results(results[2]); +} \ No newline at end of file diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index dc0abc627..81c8de5ee 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -9,6 +9,9 @@ add_subdirectory(solver) add_subdirectory(system) add_subdirectory(util) add_subdirectory(version) +if(ENABLE_OPENMP AND ENABLE_JSON) + add_subdirectory(openmp) +endif() ################################################################################ # Copy test data diff --git a/test/unit/openmp/CMakeLists.txt b/test/unit/openmp/CMakeLists.txt new file mode 100644 index 000000000..7db63577c --- /dev/null +++ b/test/unit/openmp/CMakeLists.txt @@ -0,0 +1,9 @@ +################################################################################ +# Test utilities + +include(test_util) + +################################################################################ +# Tests + +create_standard_test(NAME openmp SOURCES test_openmp.cpp) \ No newline at end of file diff --git a/test/unit/openmp/test_openmp.cpp b/test/unit/openmp/test_openmp.cpp new file mode 100644 index 000000000..c398143e5 --- /dev/null +++ b/test/unit/openmp/test_openmp.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include +#include + +using namespace micm; + +template +using SparseMatrixPolicy = SparseMatrix; + +std::vector test_solver_on_thread(System chemical_system, std::vector reactions) +{ + std::cout << "Running solver on thread " << omp_get_thread_num() << std::endl; + RosenbrockSolver<> solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + State state = solver.GetState(); + + // mol m-3 + state.variables_[0] = { 1, 0, 0 }; + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("PHOTO.r1", k1); + state.SetCustomRateParameter("PHOTO.r2", k2); + state.SetCustomRateParameter("PHOTO.r3", k3); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + double time_step = 200; // s + + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + } + + return state.variables_.AsVector(); +} + +TEST(OpenMP, OneFileReadThreeThreads) +{ + constexpr size_t n_threads = 3; + + SolverConfig solverConfig; + + std::string config_path = "./unit_configs/robertson"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + std::vector> results(3); + +#pragma omp parallel num_threads(n_threads) + { + std::vector result = test_solver_on_thread(chemical_system, reactions); + results[omp_get_thread_num()] = result; +#pragma omp barrier + } + + for(int i = 0; i < results[0].size(); ++i) { + EXPECT_EQ(results[0][i], results[1][i]); + EXPECT_EQ(results[0][i], results[2][i]); + EXPECT_EQ(results[1][i], results[2][i]); + } +} \ No newline at end of file diff --git a/test/unit/unit_configs/robertson/reactions.json b/test/unit/unit_configs/robertson/reactions.json new file mode 100644 index 000000000..cbffae848 --- /dev/null +++ b/test/unit/unit_configs/robertson/reactions.json @@ -0,0 +1,43 @@ +{ + "camp-data": [ + { + "name": "reaction rates no user defined", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "A": {} + }, + "products": { + "B": {} + }, + "MUSICA name": "r1" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": { "qty": 2} + }, + "products": { + "B": {}, + "C": {} + }, + "MUSICA name": "r2" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": {}, + "C": {} + }, + "products": { + "A": {}, + "C": {} + }, + "MUSICA name": "r3" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/robertson/species.json b/test/unit/unit_configs/robertson/species.json new file mode 100644 index 000000000..5a1a8db68 --- /dev/null +++ b/test/unit/unit_configs/robertson/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "A", + "type": "CHEM_SPEC" + }, + { + "name": "B", + "type": "CHEM_SPEC" + }, + { + "name": "C", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file From 81d1dd985c7191e07d3bb65c4975067351d273e2 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 13:44:42 -0500 Subject: [PATCH 072/318] only create the formatting branch if changes are detected (#282) --- .github/workflows/clang_format.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml index 7a823ebad..86420017e 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/clang_format.yml @@ -26,17 +26,29 @@ jobs: 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' \) -exec clang-format -i --style=file {} + + - name: Check for changes + id: check-changes + run: | + git diff --exit-code || exit 0 + continue-on-error: true + - name: Commit and push changes + # a failue of this step means changes were detected + if: steps.check-changes.outcome == 'failure' 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 == 'failure' 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 == 'failure' uses: peter-evans/create-pull-request@v3 with: token: ${{ secrets.GITHUB_TOKEN }} From 4d506fd380a9e66d7e303979da18b88a2600dc2b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 13:45:54 -0500 Subject: [PATCH 073/318] 157 steal the tests from the json library to test xcode builds (#283) * adding xcode builds * using macos-12 and macos-13 * removing checkout warning --- .github/workflows/test.yml | 54 +++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1b95a26c1..7de7b08ba 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: # - Dockerfile.mpi steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: submodules: recursive @@ -77,7 +77,7 @@ jobs: CXX: ${{ matrix.compiler.cpp }} steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Install CMake if: matrix.os == 'windows-latest' @@ -98,9 +98,55 @@ jobs: mkdir build cd build cmake .. - cmake --build . + cmake --build . --parallel 10 - name: Run tests run: | cd build - ctest -C Debug --rerun-failed --output-on-failure . --verbose \ No newline at end of file + ctest -C Debug --rerun-failed --output-on-failure . --verbose + + xcode_1: + runs-on: macos-12 + strategy: + matrix: + xcode: ['13.1', '13.2.1', '13.3.1', '13.4.1', '14.0', '14.0.1', '14.1'] + env: + DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer + + steps: + - uses: actions/checkout@v3 + + - name: Configure and build + run: | + mkdir build + cd build + cmake .. + cmake --build . --parallel 10 + + - name: Run tests + run: | + cd build + ctest --rerun-failed --output-on-failure . --verbose -j 10 + + xcode_2: + runs-on: macos-13 + strategy: + matrix: + xcode: ['14.1', '14.2', '14.3.1', '15.0'] + env: + DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer + + steps: + - uses: actions/checkout@v3 + + - name: Configure and build + run: | + mkdir build + cd build + cmake .. + cmake --build . --parallel 10 + + - name: Run tests + run: | + cd build + ctest --rerun-failed --output-on-failure . --verbose -j 10 \ No newline at end of file From d9ea2766cb0322fa1231c362627fe09e8b4b12bd Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 4 Oct 2023 11:52:43 -0700 Subject: [PATCH 074/318] add remaining JitRosenbrockSolver analytical integration tests --- .../integration/analytical_jit_rosenbrock.cpp | 204 +++ test/integration/analytical_policy.hpp | 656 +------ test/integration/analytical_rosenbrock.cpp | 1608 ++--------------- 3 files changed, 395 insertions(+), 2073 deletions(-) diff --git a/test/integration/analytical_jit_rosenbrock.cpp b/test/integration/analytical_jit_rosenbrock.cpp index 1d63191c7..547d74ec6 100644 --- a/test/integration/analytical_jit_rosenbrock.cpp +++ b/test/integration/analytical_jit_rosenbrock.cpp @@ -30,4 +30,208 @@ TEST(AnalyticalExamplesJitRosenbrock, Troe) jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; }); +} + +TEST(AnalyticalExamplesJitRosenbrock, TroeSuperStiffButAnalytical) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_stiff_troe( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, Photolysis) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_photolysis( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, PhotolysisSuperStiffButAnalytical) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_stiff_photolysis( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, TernaryChemicalActivation) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_ternary_chemical_activation( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, TernaryChemicalActivationSuperStiffButAnalytical) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_stiff_ternary_chemical_activation( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, Tunneling) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_tunneling( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, TunnelingSuperStiffButAnalytical) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_stiff_tunneling( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, Arrhenius) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_arrhenius( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, ArrheniusSuperStiffButAnalytical) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_stiff_arrhenius( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, Branched) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_branched( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, BranchedSuperStiffButAnalytical) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_stiff_branched( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + +TEST(AnalyticalExamplesJitRosenbrock, Robertson) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_robertson( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); } \ No newline at end of file diff --git a/test/integration/analytical_policy.hpp b/test/integration/analytical_policy.hpp index c713b823d..5ce4b25ff 100644 --- a/test/integration/analytical_policy.hpp +++ b/test/integration/analytical_policy.hpp @@ -185,8 +185,9 @@ void test_analytical_troe( } } -#if 0 -TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) +template +void test_analytical_stiff_troe( + const std::function&)> create_solver) { /* * A1 -> B, k1 @@ -241,11 +242,8 @@ TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, r5 }); double temperature = 272.5; double pressure = 101253.3; @@ -264,7 +262,7 @@ TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(4)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -325,7 +323,9 @@ TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) } } -TEST(AnalyticalExamples, Photolysis) +template +void test_analytical_photolysis( + const std::function&)> create_solver) { /* * A -> B, k1 @@ -352,11 +352,8 @@ TEST(AnalyticalExamples, Photolysis) .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoB" })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = + create_solver(micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2 }); double temperature = 272.5; double pressure = 101253.3; @@ -369,7 +366,7 @@ TEST(AnalyticalExamples, Photolysis) double k2 = 3e-3; double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); state.SetCustomRateParameter("photoA", k1); state.SetCustomRateParameter("photoB", k2); @@ -432,7 +429,9 @@ TEST(AnalyticalExamples, Photolysis) } } -TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) +template +void test_analytical_stiff_photolysis( + const std::function&)> create_solver) { /* * A1 -> B, k1 @@ -480,11 +479,8 @@ TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, r5 }); double temperature = 272.5; double pressure = 101253.3; @@ -497,7 +493,7 @@ TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) double k2 = 3e-3; double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); state.SetCustomRateParameter("photoA1B", k1); state.SetCustomRateParameter("photoA2B", k1); @@ -563,7 +559,9 @@ TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) } } -TEST(AnalyticalExamples, TernaryChemicalActivation) +template +void test_analytical_ternary_chemical_activation( + const std::function&)> create_solver) { /* * A -> B, k1 @@ -598,11 +596,8 @@ TEST(AnalyticalExamples, TernaryChemicalActivation) .N_ = 0.8 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = + create_solver(micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2 }); double temperature = 272.5; double pressure = 101253.3; @@ -621,7 +616,7 @@ TEST(AnalyticalExamples, TernaryChemicalActivation) std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(3)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -681,7 +676,9 @@ TEST(AnalyticalExamples, TernaryChemicalActivation) } } -TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) +template +void test_analytical_stiff_ternary_chemical_activation( + const std::function&)> create_solver) { /* * A1 -> B, k1 @@ -736,11 +733,8 @@ TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, r5 }); double temperature = 272.5; double pressure = 101253.3; @@ -759,7 +753,7 @@ TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(4)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -820,7 +814,9 @@ TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) } } -TEST(AnalyticalExamples, Tunneling) +template +void test_analytical_tunneling( + const std::function&)> create_solver) { /* * A -> B, k1 @@ -848,11 +844,8 @@ TEST(AnalyticalExamples, Tunneling) .rate_constant(micm::TunnelingRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 1.0e8 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = + create_solver(micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2 }); double temperature = 272.5; double pressure = 101253.3; @@ -865,7 +858,7 @@ TEST(AnalyticalExamples, Tunneling) double k2 = 1.2e-4 * std::exp(-167 / temperature + 1.0e8 / std::pow(temperature, 3)); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(3)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -925,7 +918,9 @@ TEST(AnalyticalExamples, Tunneling) } } -TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) +template +void test_analytical_stiff_tunneling( + const std::function&)> create_solver) { /* * A1 -> B, k1 @@ -973,11 +968,8 @@ TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, r5 }); double temperature = 272.5; double pressure = 101253.3; @@ -990,7 +982,7 @@ TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) double k2 = 1.2e-4 * std::exp(-167 / temperature + 1.0e8 / std::pow(temperature, 3)); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(4)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -1054,7 +1046,9 @@ TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) } } -TEST(AnalyticalExamples, Arrhenius) +template +void test_analytical_arrhenius( + const std::function&)> create_solver) { /* * A -> B, k1 @@ -1081,11 +1075,8 @@ TEST(AnalyticalExamples, Arrhenius) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 1.2e-4, .B_ = 7, .C_ = 75, .D_ = 50, .E_ = 0.5 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = + create_solver(micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2 }); double temperature = 272.5; double pressure = 101253.3; @@ -1098,7 +1089,7 @@ TEST(AnalyticalExamples, Arrhenius) double k2 = 1.2e-4 * std::exp(75 / temperature) * std::pow(temperature / 50, 7) * (1.0 + 0.5 * pressure); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(3)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -1158,7 +1149,9 @@ TEST(AnalyticalExamples, Arrhenius) } } -TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) +template +void test_analytical_stiff_arrhenius( + const std::function&)> create_solver) { /* * A1 -> B, k1 @@ -1207,11 +1200,8 @@ TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, r5 }); double temperature = 272.5; double pressure = 101253.3; @@ -1224,7 +1214,7 @@ TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) double k2 = 1.2e-4 * std::exp(75 / temperature) * std::pow(temperature / 50, 167) * (1.0 + 0.5 * pressure); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(4)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -1288,7 +1278,9 @@ TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) } } -TEST(AnalyticalExamples, Branched) +template +void test_analytical_branched( + const std::function&)> create_solver) { /* * A -> B, k1 @@ -1325,11 +1317,8 @@ TEST(AnalyticalExamples, Branched) .n_ = 2 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = + create_solver(micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2 }); double temperature = 272.5; double pressure = 101253.3; @@ -1357,7 +1346,7 @@ TEST(AnalyticalExamples, Branched) double k2 = 1.2 * std::exp(-204.3 / temperature) * (A / (z + A)); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(3)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -1417,7 +1406,9 @@ TEST(AnalyticalExamples, Branched) } } -TEST(AnalyticalExamples, BranchedSuperStiffButAnalytical) +template +void test_analytical_stiff_branched( + const std::function&)> create_solver) { /* * A1 -> B, k1 @@ -1480,11 +1471,8 @@ TEST(AnalyticalExamples, BranchedSuperStiffButAnalytical) .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, r5 }); double temperature = 272.5; double pressure = 101253.3; @@ -1512,7 +1500,7 @@ TEST(AnalyticalExamples, BranchedSuperStiffButAnalytical) double k2 = 1.2 * std::exp(-204.3 / temperature) * (A / (z + A)); double time_step = 1.0; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector> model_concentrations(nsteps, std::vector(4)); std::vector> analytical_concentrations(nsteps, std::vector(3)); @@ -1576,7 +1564,9 @@ TEST(AnalyticalExamples, BranchedSuperStiffButAnalytical) } } -TEST(AnalyticalExamples, Robertson) +template +void test_analytical_robertson( + const std::function&)> create_solver) { /* * A -> B, k1 = 0.04 @@ -1615,17 +1605,14 @@ TEST(AnalyticalExamples, Robertson) .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) .phase(gas_phase); - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + OdeSolverPolicy solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }); double temperature = 272.5; double pressure = 101253.3; double air_density = 1e6; - micm::State state = solver.GetState(); + auto state = solver.GetState(); double k1 = 0.04; double k2 = 3e7; @@ -1700,501 +1687,4 @@ TEST(AnalyticalExamples, Robertson) EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], tol) << "Arrays differ at index (" << i << ", " << 2 << ")"; } -} - -TEST(AnalyticalExamples, Oregonator) -{ - /* - * I think these are the equations, but I'm really not sure. I don't know how this translates to the jacobian - * and forcing functions used by the ODE book: https://www.unige.ch/~hairer/testset/stiff/orego/equation.f - * A+Y -> X+P - * X+Y -> 2P - * A+X -> 2X+2Z - * 2X -> A+P - * B+Z -> 1/2fY - * - * this problem is described in - * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd - * edition. ed. Springer, Berlin ; New York. Page 3 - * - * solutions are provided here - * https://www.unige.ch/~hairer/testset/testset.html - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ b }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) - .phase(gas_phase); - - auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); - params.relative_tolerance_ = 1e-4; - params.absolute_tolerance_ = 1e-6 * params.relative_tolerance_; - Oregonator solver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, params); - - double end = 360; - double time_step = 30; - size_t N = static_cast(end / time_step); - - std::vector> model_concentrations(N + 1, std::vector(3)); - std::vector> analytical_concentrations(13, std::vector(3)); - - model_concentrations[0] = { 1, 2, 3 }; - - analytical_concentrations = { - { 1, 2, 3 }, - { 0.1000661467180497E+01, 0.1512778937348249E+04, 0.1035854312767229E+05 }, - { 0.1000874625199626E+01, 0.1144336972384497E+04, 0.8372149966624639E+02 }, - { 0.1001890368438751E+01, 0.5299926232295553E+03, 0.1662279579042420E+01 }, - { 0.1004118022612645E+01, 0.2438326079910346E+03, 0.1008822224048647E+01 }, - { 0.1008995416634061E+01, 0.1121664388662539E+03, 0.1007783229065319E+01 }, - { 0.1019763472537298E+01, 0.5159761322947535E+02, 0.1016985778956374E+01 }, - { 0.1043985088527474E+01, 0.2373442027531524E+02, 0.1037691843544522E+01 }, - { 0.1100849071667922E+01, 0.1091533805469020E+02, 0.1085831969810860E+01 }, - { 0.1249102130020572E+01, 0.5013945178605446E+01, 0.1208326626237875E+01 }, - { 0.1779724751937019E+01, 0.2281852385542403E+01, 0.1613754023671725E+01 }, - { 0.1000889326903503E+01, 0.1125438585746596E+04, 0.1641049483777168E+05 }, - { 0.1000814870318523E+01, 0.1228178521549889E+04, 0.1320554942846513E+03 }, - }; - - micm::State state = solver.GetState(); - - state.variables_[0] = model_concentrations[0]; - - std::vector times; - times.push_back(0); - for (size_t i_time = 0; i_time < N; ++i_time) - { - double solve_time = time_step + i_time * time_step; - times.push_back(solve_time); - // Model results - double actual_solve = 0; - while (actual_solve < time_step) - { - auto result = solver.Solve(time_step - actual_solve, state); - state.variables_[0] = result.result_.AsVector(); - actual_solve += result.final_time_; - } - model_concentrations[i_time + 1] = state.variables_[0]; - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("model_concentrations.csv", header, model_concentrations, times); - std::vector an_times; - an_times.push_back(0); - for (int i = 1; i <= 12; ++i) - { - an_times.push_back(30 * i); - } - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, an_times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - double tol = 1e-3; - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - double rel_diff = relative_difference(model_concentrations[i][_a], analytical_concentrations[i][0]); - EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 0 << ")"; - rel_diff = relative_difference(model_concentrations[i][_b], analytical_concentrations[i][1]); - EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 1 << ")"; - rel_diff = relative_difference(model_concentrations[i][_c], analytical_concentrations[i][2]); - EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, Oregonator2) -{ - /* Equations derived from the forcing function here: https://www.unige.ch/~hairer/testset/stiff/orego/equation.f - * a + b -> ( 1 - (1/77.27)^2 ) b k = 77.27 - * c -> ( 1 / (0.161 * 77.27) ) b k = 0.161 - * b -> ( 77.27 )^2 a k = 1/77.27 - * a -> 2 a + ( 0.161/77.27 ) c k = 77.27 - * a + a -> NULL k = 77.27 * 8.375e-6 - * - * this problem is described in - * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd - * edition. ed. Springer, Berlin ; New York. Page 3 - * - * solutions are provided here - * https://www.unige.ch/~hairer/testset/testset.html - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a, b }) - .products({ yields(b, 1 - std::pow((1 / 77.27), 2)) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ c }) - .products({ yields(b, 1 / (0.161 * 77.27)) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b }) - .products({ yields(a, std::pow(77.27, 2)) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a }) - .products({ yields(a, 2), yields(c, 0.161 / 77.27) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r4" })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a, a }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r5" })) - .phase(gas_phase); - - auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); - params.relative_tolerance_ = 1e-4; - params.absolute_tolerance_ = 1e-6 * params.relative_tolerance_; - Oregonator solver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - params); - - double end = 360; - double time_step = 30; - size_t N = static_cast(end / time_step); - - std::vector> model_concentrations(N + 1, std::vector(3)); - std::vector> analytical_concentrations(13, std::vector(3)); - - model_concentrations[0] = { 1, 2, 3 }; - - analytical_concentrations = { - { 1, 2, 3 }, - { 0.1000661467180497E+01, 0.1512778937348249E+04, 0.1035854312767229E+05 }, - { 0.1000874625199626E+01, 0.1144336972384497E+04, 0.8372149966624639E+02 }, - { 0.1001890368438751E+01, 0.5299926232295553E+03, 0.1662279579042420E+01 }, - { 0.1004118022612645E+01, 0.2438326079910346E+03, 0.1008822224048647E+01 }, - { 0.1008995416634061E+01, 0.1121664388662539E+03, 0.1007783229065319E+01 }, - { 0.1019763472537298E+01, 0.5159761322947535E+02, 0.1016985778956374E+01 }, - { 0.1043985088527474E+01, 0.2373442027531524E+02, 0.1037691843544522E+01 }, - { 0.1100849071667922E+01, 0.1091533805469020E+02, 0.1085831969810860E+01 }, - { 0.1249102130020572E+01, 0.5013945178605446E+01, 0.1208326626237875E+01 }, - { 0.1779724751937019E+01, 0.2281852385542403E+01, 0.1613754023671725E+01 }, - { 0.1000889326903503E+01, 0.1125438585746596E+04, 0.1641049483777168E+05 }, - { 0.1000814870318523E+01, 0.1228178521549889E+04, 0.1320554942846513E+03 }, - }; - - micm::State state = solver.GetState(); - - double k1 = 77.27; - double k2 = 0.161; - double k3 = 1 / 77.27; - double k4 = 77.27; - double k5 = 77.27 * 8.375e-6; - - state.SetCustomRateParameter("r1", k1); - state.SetCustomRateParameter("r2", k2); - state.SetCustomRateParameter("r3", k3); - state.SetCustomRateParameter("r4", k4); - state.SetCustomRateParameter("r5", k5); - - state.variables_[0] = model_concentrations[0]; - - std::vector times; - times.push_back(0); - for (size_t i_time = 0; i_time < N; ++i_time) - { - double solve_time = time_step + i_time * time_step; - times.push_back(solve_time); - // Model results - double actual_solve = 0; - while (actual_solve < time_step) - { - auto result = solver.Solve(time_step - actual_solve, state); - state.variables_[0] = result.result_.AsVector(); - actual_solve += result.final_time_; - } - model_concentrations[i_time + 1] = state.variables_[0]; - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("model_concentrations.csv", header, model_concentrations, times); - std::vector an_times; - an_times.push_back(0); - for (int i = 1; i <= 12; ++i) - { - an_times.push_back(30 * i); - } - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, an_times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - double tol = 1e-3; - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - double rel_diff = relative_difference(model_concentrations[i][_a], analytical_concentrations[i][0]); - EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 0 << ")"; - rel_diff = relative_difference(model_concentrations[i][_b], analytical_concentrations[i][1]); - EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 1 << ")"; - rel_diff = relative_difference(model_concentrations[i][_c], analytical_concentrations[i][2]); - EXPECT_TRUE(rel_diff < tol) << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, HIRES) -{ - /* - * No idea what these equations are - * - * this problem is described in - * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd - * edition. ed. Springer, Berlin ; New York. Page 3 - * - * solutions are provided here - * https://www.unige.ch/~hairer/testset/testset.html - */ - - auto y1 = micm::Species("y1"); - auto y2 = micm::Species("y2"); - auto y3 = micm::Species("y3"); - auto y4 = micm::Species("y4"); - auto y5 = micm::Species("y5"); - auto y6 = micm::Species("y6"); - auto y7 = micm::Species("y7"); - auto y8 = micm::Species("y8"); - - micm::Phase gas_phase{ std::vector{ y1, y2, y3, y4, y5, y6, y7, y8 } }; - - micm::Process r1 = micm::Process::create() - .reactants({ y1 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) - .phase(gas_phase); - micm::Process r2 = micm::Process::create() - .reactants({ y2 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) - .phase(gas_phase); - micm::Process r3 = micm::Process::create() - .reactants({ y3 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) - .phase(gas_phase); - micm::Process r4 = micm::Process::create() - .reactants({ y4 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r4" })) - .phase(gas_phase); - micm::Process r5 = micm::Process::create() - .reactants({ y5 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r5" })) - .phase(gas_phase); - micm::Process r6 = micm::Process::create() - .reactants({ y6 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r6" })) - .phase(gas_phase); - micm::Process r7 = micm::Process::create() - .reactants({ y7 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r7" })) - .phase(gas_phase); - micm::Process r8 = micm::Process::create() - .reactants({ y8 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r8" })) - .phase(gas_phase); - - auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); - params.relative_tolerance_ = 1e-3; - params.absolute_tolerance_ = params.relative_tolerance_ * 1e-4; - HIRES solver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5, r6, r7, r8 }, - params); - - size_t N = 2; - - std::vector> model_concentrations(N + 1, std::vector(8)); - std::vector> analytical_concentrations(3, std::vector(8)); - - model_concentrations[0] = { 1, 0, 0, 0, 0, 0, 0, 0.0057 }; - - analytical_concentrations = { - { 1, 0, 0, 0, 0, 0, 0, 0.0057 }, - { 0.000737131257332567, - 0.000144248572631618, - 0.000058887297409676, - 0.001175651343283149, - 0.002386356198831330, - 0.006238968252742796, - 0.002849998395185769, - 0.002850001604814231 }, - { 0.000670305503581864, - 0.000130996846986347, - 0.000046862231597733, - 0.001044668020551705, - 0.000594883830951485, - 0.001399628833942774, - 0.001014492757718480, - 0.004685507242281520 }, - }; - - micm::State state = solver.GetState(); - - state.variables_[0] = model_concentrations[0]; - - std::vector times; - times.push_back(0); - double time_step = 321.8122; - for (size_t i_time = 0; i_time < N; ++i_time) - { - double solve_time = time_step + i_time * time_step; - times.push_back(solve_time); - // Model results - double actual_solve = 0; - while (actual_solve < time_step) - { - auto result = solver.Solve(time_step - actual_solve, state); - state.variables_[0] = result.result_.AsVector(); - actual_solve += result.final_time_; - } - model_concentrations[i_time + 1] = state.variables_[0]; - time_step += 100; - } - - std::vector header = { "time", "y1", "y2", "y3", "y4", "y5", "y6", "y7", "y8" }; - writeCSV("model_concentrations.csv", header, model_concentrations, times); - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - - double tol = 1e-5; - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - for (size_t j = 0; j < model_concentrations[0].size(); ++j) - { - double rel_diff = relative_difference(model_concentrations[i][j], analytical_concentrations[i][j]); - EXPECT_NEAR(model_concentrations[i][j], analytical_concentrations[i][j], tol); - } - } -} - -TEST(AnalyticalExamples, E5) -{ - /* - * No idea what these equations are - * - * this problem is described in - * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd - * edition. ed. Springer, Berlin ; New York. Page 3 - * - * solutions are provided here - * https://www.unige.ch/~hairer/testset/testset.html - */ - - auto y1 = micm::Species("y1"); - auto y2 = micm::Species("y2"); - auto y3 = micm::Species("y3"); - auto y4 = micm::Species("y4"); - - micm::Phase gas_phase{ std::vector{ y1, y2, y3, y4 } }; - - micm::Process r1 = micm::Process::create() - .reactants({ y1 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) - .phase(gas_phase); - micm::Process r2 = micm::Process::create() - .reactants({ y2 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) - .phase(gas_phase); - micm::Process r3 = micm::Process::create() - .reactants({ y3 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) - .phase(gas_phase); - micm::Process r4 = micm::Process::create() - .reactants({ y4 }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r4" })) - .phase(gas_phase); - - auto params = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(); - params.relative_tolerance_ = 1e-2; - params.absolute_tolerance_ = 1.7e-24; - E5 solver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4 }, params); - - size_t N = 7; - - std::vector> model_concentrations(N + 1, std::vector(4)); - std::vector> analytical_concentrations(N + 1, std::vector(4)); - - model_concentrations[0] = { 1.76e-3, 0, 0, 0 }; - - analytical_concentrations = { - { 1.76e-3, 0, 0, 0 }, - { 1.7599259497677897058e-003, 1.3846281519376516449e-011, 7.6370038530073911180e-013, 1.3082581134075777338e-011 }, - { 1.6180769999072942552e-003, 1.3822370304983735443e-010, 8.2515735006838336088e-012, 1.2997212954915352082e-010 }, - { 7.4813208224292220114e-006, 2.3734781561205975019e-012, 2.2123586689581663654e-012, 1.6111948716243113653e-013 }, - { 4.7150333630401632232e-010, 1.8188895860807021729e-014, 1.8188812376786725407e-014, 8.3484020296321693074e-020 }, - { 3.1317148329356996037e-014, 1.4840957952870064294e-016, 1.4840957948345691466e-016, 4.5243728279782625194e-026 }, - { 3.8139035189787091771e-049, 1.0192582567660293322e-020, 1.0192582567660293322e-020, 3.7844935507486221171e-065 }, - { 0.0000000000000000000e-000, 8.8612334976263783420e-023, 8.8612334976263783421e-023, 0.0000000000000000000e-000 } - }; - - micm::State state = solver.GetState(); - - state.variables_[0] = model_concentrations[0]; - - std::vector times; - times.push_back(0); - double time_step = 10; - for (size_t i_time = 0; i_time < N; ++i_time) - { - double solve_time = time_step + i_time * time_step; - times.push_back(solve_time); - // Model results - double actual_solve = 0; - while (actual_solve < time_step) - { - auto result = solver.Solve(time_step - actual_solve, state); - state.variables_[0] = result.result_.AsVector(); - actual_solve += result.final_time_; - } - model_concentrations[i_time + 1] = state.variables_[0]; - time_step *= 100; - } - - std::vector header = { "time", "y1", "y2", "y3", "y4" }; - writeCSV("model_concentrations.csv", header, model_concentrations, times); - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - - double tol = 1e-5; - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - for (size_t j = 0; j < model_concentrations[0].size(); ++j) - { - double rel_diff = relative_difference(model_concentrations[i][j], analytical_concentrations[i][j]); - EXPECT_NEAR(model_concentrations[i][j], analytical_concentrations[i][j], tol) - << "difference at (" << i << ", " << j << ")"; - } - } -} -#endif \ No newline at end of file +} \ No newline at end of file diff --git a/test/integration/analytical_rosenbrock.cpp b/test/integration/analytical_rosenbrock.cpp index 19e50d5a2..83dae3188 100644 --- a/test/integration/analytical_rosenbrock.cpp +++ b/test/integration/analytical_rosenbrock.cpp @@ -25,1518 +25,146 @@ TEST(AnalyticalExamples, Troe) TEST(AnalyticalExamples, TroeSuperStiffButAnalytical) { - /* - * A1 -> B, k1 - * A2 -> B, k1 - * A1 -> A2, k3 >>> k1 - * A2 -> A1, k4 >>> k1 - * B -> C, k2 - * - */ - - auto a1 = micm::Species("A1"); - auto a2 = micm::Species("A2"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(b, 1) }) - .rate_constant(micm::TroeRateConstant({ .k0_A_ = 4.0e-10 })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(b, 1) }) - .rate_constant(micm::TroeRateConstant({ .k0_A_ = 4.0e-10 })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::TroeRateConstant({ .k0_A_ = 1.2e-3, - .k0_B_ = 167, - .k0_C_ = 3, - .kinf_A_ = 136, - .kinf_B_ = 5, - .kinf_C_ = 24, - .Fc_ = 0.9, - .N_ = 0.8 })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(a2, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(a1, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k_0 = 4.0e-10; - double k_inf = 1; - double k1 = k_0 * air_density / (1.0 + k_0 * air_density / k_inf) * - std::pow(0.6, 1.0 / (1.0 + (1.0 / 1.0) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); - - // B->C reaction rate - k_0 = 1.2e-3 * std::exp(3.0 / temperature) * std::pow(temperature / 300.0, 167.0); - k_inf = 136.0 * std::exp(24.0 / temperature) * std::pow(temperature / 300.0, 5.0); - double k2 = k_0 * air_density / (1.0 + k_0 * air_density / k_inf) * - std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(4)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - state.SetConcentration(a1, 0.5); - state.SetConcentration(a2, 0.5); - state.SetConcentration(b, 0.0); - state.SetConcentration(c, 0.0); - - model_concentrations[0] = state.variables_[0]; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - auto header = state.variable_names_; - header.insert(header.begin(), "time"); - writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a1 = map.at("A1"); - size_t _a2 = map.at("A2"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4); - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4); - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4); - } + test_analytical_stiff_troe>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); } TEST(AnalyticalExamples, Photolysis) { - /* - * A -> B, k1 - * B -> C, k2 - * - * Copying the CAMP example: https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_photolysis.F90 - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoA" })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoB" })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k1 = 2e-3; - - // B->C reaction rate - double k2 = 3e-3; - - double time_step = 1.0; - micm::State state = solver.GetState(); - - state.SetCustomRateParameter("photoA", k1); - state.SetCustomRateParameter("photoB", k2); - - std::vector> model_concentrations(nsteps, std::vector(3)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - model_concentrations[0] = { 1, 0, 0 }; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); - EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - writeCSV("model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) -{ - /* - * A1 -> B, k1 - * A2 -> B, k1 - * A1 -> A2, k3 >>> k1 - * A2 -> A1, k4 >>> k1 - * B -> C, k2 - * - */ - - auto a1 = micm::Species("A1"); - auto a2 = micm::Species("A2"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(b, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoA1B" })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(b, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoA2B" })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "photoB" })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(a2, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(a1, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k1 = 2e-3; - - // B->C reaction rate - double k2 = 3e-3; - - double time_step = 1.0; - micm::State state = solver.GetState(); - - state.SetCustomRateParameter("photoA1B", k1); - state.SetCustomRateParameter("photoA2B", k1); - state.SetCustomRateParameter("photoB", k2); - - std::vector> model_concentrations(nsteps, std::vector(3)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - state.SetConcentration(a1, 0.5); - state.SetConcentration(a2, 0.5); - state.SetConcentration(b, 0.0); - state.SetConcentration(c, 0.0); - - model_concentrations[0] = state.variables_[0]; - - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - writeCSV("model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a1 = map.at("A1"); - size_t _a2 = map.at("A2"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4); - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4); - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4); - } -} - -TEST(AnalyticalExamples, TernaryChemicalActivation) -{ - /* - * A -> B, k1 - * B -> C, k2 - * - * Copying the CAMP example: - * https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_ternary_chemical_activation.F90 - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 4.0e-10, .kinf_A_ = 1 })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2e-3, - .k0_B_ = 167, - .k0_C_ = 3, - .kinf_A_ = 136, - .kinf_B_ = 5, - .kinf_C_ = 24, - .Fc_ = 0.9, - .N_ = 0.8 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k_0 = 4.0e-10; - double k_inf = 1; - double k1 = k_0 / (1.0 + k_0 * air_density / k_inf) * - std::pow(0.6, 1.0 / (1.0 + (1.0 / 1.0) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); - - // B->C reaction rate - k_0 = 1.2e-3 * std::exp(3.0 / temperature) * std::pow(temperature / 300.0, 167.0); - k_inf = 136.0 * std::exp(24.0 / temperature) * std::pow(temperature / 300.0, 5.0); - double k2 = k_0 / (1.0 + k_0 * air_density / k_inf) * - std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(3)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - model_concentrations[0] = { 1, 0, 0 }; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); - EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - writeCSV("model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) -{ - /* - * A1 -> B, k1 - * A2 -> B, k1 - * A1 -> A2, k3 >>> k1 - * A2 -> A1, k4 >>> k1 - * B -> C, k2 - * - */ - - auto a1 = micm::Species("A1"); - auto a2 = micm::Species("A2"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(b, 1) }) - .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 4.0e-10, .kinf_A_ = 1 })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(b, 1) }) - .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 4.0e-10, .kinf_A_ = 1 })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::TernaryChemicalActivationRateConstant({ .k0_A_ = 1.2e-3, - .k0_B_ = 167, - .k0_C_ = 3, - .kinf_A_ = 136, - .kinf_B_ = 5, - .kinf_C_ = 24, - .Fc_ = 0.9, - .N_ = 0.8 })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(a2, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(a1, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k_0 = 4.0e-10; - double k_inf = 1; - double k1 = k_0 / (1.0 + k_0 * air_density / k_inf) * - std::pow(0.6, 1.0 / (1.0 + (1.0 / 1.0) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); - - // B->C reaction rate - k_0 = 1.2e-3 * std::exp(3.0 / temperature) * std::pow(temperature / 300.0, 167.0); - k_inf = 136.0 * std::exp(24.0 / temperature) * std::pow(temperature / 300.0, 5.0); - double k2 = k_0 / (1.0 + k_0 * air_density / k_inf) * - std::pow(0.9, 1.0 / (1.0 + (1.0 / 0.8) * std::pow(std::log10(k_0 * air_density / k_inf), 2))); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(4)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - state.SetConcentration(a1, 0.5); - state.SetConcentration(a2, 0.5); - state.SetConcentration(b, 0.0); - state.SetConcentration(c, 0.0); - - model_concentrations[0] = state.variables_[0]; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - auto header = state.variable_names_; - header.insert(header.begin(), "time"); - writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a1 = map.at("A1"); - size_t _a2 = map.at("A2"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4); - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4); - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4); - } -} - -TEST(AnalyticalExamples, Tunneling) -{ - /* - * A -> B, k1 - * B -> C, k2 - * - * Copying the CAMP example: - * https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_wennberg_tunneling.F90 - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::TunnelingRateConstant({ .A_ = 4.0e-3 })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::TunnelingRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 1.0e8 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k1 = 4.0e-3; - - // B->C reaction rate - double k2 = 1.2e-4 * std::exp(-167 / temperature + 1.0e8 / std::pow(temperature, 3)); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(3)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - model_concentrations[0] = { 1, 0, 0 }; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); - EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - writeCSV("model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) -{ - /* - * A1 -> B, k1 - * A2 -> B, k1 - * A1 -> A2, k3 >>> k1 - * A2 -> A1, k4 >>> k1 - * B -> C, k2 - * - */ - - auto a1 = micm::Species("A1"); - auto a2 = micm::Species("A2"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(b, 1) }) - .rate_constant(micm::TunnelingRateConstant({ .A_ = 4.0e-3 })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(b, 1) }) - .rate_constant(micm::TunnelingRateConstant({ .A_ = 4.0e-3 })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::TunnelingRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 1.0e8 })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(a2, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(a1, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k1 = 4.0e-3; - - // B->C reaction rate - double k2 = 1.2e-4 * std::exp(-167 / temperature + 1.0e8 / std::pow(temperature, 3)); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(4)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - state.SetConcentration(a1, 0.5); - state.SetConcentration(a2, 0.5); - state.SetConcentration(b, 0.0); - state.SetConcentration(c, 0.0); - - model_concentrations[0] = state.variables_[0]; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - auto header = state.variable_names_; - header.insert(header.begin(), "time"); - writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a1 = map.at("A1"); - size_t _a2 = map.at("A2"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, Arrhenius) -{ - /* - * A -> B, k1 - * B -> C, k2 - * - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e-3, .C_ = 50 })) - .phase(gas_phase); - - micm::Process r2 = - micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 1.2e-4, .B_ = 7, .C_ = 75, .D_ = 50, .E_ = 0.5 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k1 = 4.0e-3 * std::exp(50 / temperature); - - // B->C reaction rate - double k2 = 1.2e-4 * std::exp(75 / temperature) * std::pow(temperature / 50, 7) * (1.0 + 0.5 * pressure); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(3)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - model_concentrations[0] = { 1, 0, 0 }; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); - EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - writeCSV("model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-8) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-8) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-8) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } -} - -TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) -{ - /* - * A1 -> B, k1 - * A2 -> B, k1 - * A1 -> A2, k3 >>> k1 - * A2 -> A1, k4 >>> k1 - * B -> C, k2 - * - */ - - auto a1 = micm::Species("A1"); - auto a2 = micm::Species("A2"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(b, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e-3, .C_ = 50 })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(b, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e-3, .C_ = 50 })) - .phase(gas_phase); - - micm::Process r3 = - micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 1.2e-4, .B_ = 167, .C_ = 75, .D_ = 50, .E_ = 0.5 })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(a2, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(a1, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double k1 = 4.0e-3 * std::exp(50 / temperature); - - // B->C reaction rate - double k2 = 1.2e-4 * std::exp(75 / temperature) * std::pow(temperature / 50, 167) * (1.0 + 0.5 * pressure); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(4)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - state.SetConcentration(a1, 0.5); - state.SetConcentration(a2, 0.5); - state.SetConcentration(b, 0.0); - state.SetConcentration(c, 0.0); - - model_concentrations[0] = state.variables_[0]; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - auto header = state.variable_names_; - header.insert(header.begin(), "time"); - writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a1 = map.at("A1"); - size_t _a2 = map.at("A2"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-4) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-4) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-4) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } + test_analytical_photolysis>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); } -TEST(AnalyticalExamples, Branched) +TEST(AnalyticalExamples, PhotolysisSuperStiffButAnalytical) { - /* - * A -> B, k1 - * B -> C, k2 - * - * Copying the CAMP example: https://github.com/open-atmos/camp/blob/main/test/unit_rxn_data/test_rxn_wennberg_no_ro2.F90 - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = - micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, - .X_ = 1.2, - .Y_ = 204.3, - .a0_ = 1.0e-3, - .n_ = 2 })) - .phase(gas_phase); - - micm::Process r2 = - micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Nitrate, - .X_ = 1.2, - .Y_ = 204.3, - .a0_ = 1.0e-3, - .n_ = 2 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double air_dens_n_cm3 = air_density * AVOGADRO_CONSTANT * 1.0e-6; - double a_ = 2.0e-22 * std::exp(2) * 2.45e19; - double b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); - double z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; - a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; - b_ = 0.43 * std::pow((temperature / 298.0), -8.0); - double A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); - - double k1 = 1.2 * std::exp(-204.3 / temperature) * (z / (z + A)); - - // B->C reaction rate - a_ = 2.0e-22 * std::exp(2) * 2.45e19; - b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); - z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; - a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; - b_ = 0.43 * std::pow((temperature / 298.0), -8.0); - A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); - - double k2 = 1.2 * std::exp(-204.3 / temperature) * (A / (z + A)); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(3)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - model_concentrations[0] = { 1, 0, 0 }; - analytical_concentrations[0] = { 1, 0, 0 }; + test_analytical_stiff_photolysis>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; +TEST(AnalyticalExamples, TernaryChemicalActivation) +{ + test_analytical_ternary_chemical_activation>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - size_t idx_A = 0, idx_B = 1, idx_C = 2; +TEST(AnalyticalExamples, TernaryChemicalActivationSuperStiffButAnalytical) +{ + test_analytical_stiff_ternary_chemical_activation>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); - EXPECT_NEAR(k2, state.rate_constants_.AsVector()[1], 1e-8); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } +TEST(AnalyticalExamples, Tunneling) +{ + test_analytical_tunneling>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - std::vector header = { "time", "A", "B", "C" }; - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - writeCSV("model_concentrations.csv", header, model_concentrations, times); +TEST(AnalyticalExamples, TunnelingSuperStiffButAnalytical) +{ + test_analytical_stiff_tunneling>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - auto map = state.variable_map_; +TEST(AnalyticalExamples, Arrhenius) +{ + test_analytical_arrhenius>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); +TEST(AnalyticalExamples, ArrheniusSuperStiffButAnalytical) +{ + test_analytical_stiff_arrhenius>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], 1e-3) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-3) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-3) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } +TEST(AnalyticalExamples, Branched) +{ + test_analytical_branched>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); } TEST(AnalyticalExamples, BranchedSuperStiffButAnalytical) { - /* - * A1 -> B, k1 - * A2 -> B, k1 - * A1 -> A2, k3 >>> k1 - * A2 -> A1, k4 >>> k1 - * B -> C, k2 - * - */ - - auto a1 = micm::Species("A1"); - auto a2 = micm::Species("A2"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a1, a2, b, c } }; - - micm::Process r1 = - micm::Process::create() - .reactants({ a1 }) - .products({ yields(b, 1) }) - .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, - .X_ = 1.2, - .Y_ = 204.3, - .a0_ = 1.0e-3, - .n_ = 2 })) - .phase(gas_phase); - - micm::Process r2 = - micm::Process::create() - .reactants({ a2 }) - .products({ yields(b, 1) }) - .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, - .X_ = 1.2, - .Y_ = 204.3, - .a0_ = 1.0e-3, - .n_ = 2 })) - .phase(gas_phase); - - micm::Process r3 = - micm::Process::create() - .reactants({ b }) - .products({ yields(c, 1) }) - .rate_constant(micm::BranchedRateConstant({ .branch_ = micm::BranchedRateConstantParameters::Branch::Nitrate, - .X_ = 1.2, - .Y_ = 204.3, - .a0_ = 1.0e-3, - .n_ = 2 })) - .phase(gas_phase); - - micm::Process r4 = micm::Process::create() - .reactants({ a1 }) - .products({ yields(a2, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 4.0e10 })) - .phase(gas_phase); - - micm::Process r5 = micm::Process::create() - .reactants({ a2 }) - .products({ yields(a1, 1) }) - .rate_constant(micm::ArrheniusRateConstant({ .A_ = 0.9 * 4.0e10 })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3, r4, r5 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - // A->B reaction rate - double air_dens_n_cm3 = air_density * AVOGADRO_CONSTANT * 1.0e-6; - double a_ = 2.0e-22 * std::exp(2) * 2.45e19; - double b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); - double z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; - a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; - b_ = 0.43 * std::pow((temperature / 298.0), -8.0); - double A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); - - double k1 = 1.2 * std::exp(-204.3 / temperature) * (z / (z + A)); - - // B->C reaction rate - a_ = 2.0e-22 * std::exp(2) * 2.45e19; - b_ = 0.43 * std::pow((293.0 / 298.0), -8.0); - z = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))) * (1.0 - 1.0e-3) / 1.0e-3; - a_ = 2.0e-22 * std::exp(2) * air_dens_n_cm3; - b_ = 0.43 * std::pow((temperature / 298.0), -8.0); - A = a_ / (1.0 + a_ / b_) * std::pow(0.41, 1.0 / (1.0 + std::pow(std::log10(a_ / b_), 2))); - - double k2 = 1.2 * std::exp(-204.3 / temperature) * (A / (z + A)); - - double time_step = 1.0; - micm::State state = solver.GetState(); - - std::vector> model_concentrations(nsteps, std::vector(4)); - std::vector> analytical_concentrations(nsteps, std::vector(3)); - - state.SetConcentration(a1, 0.5); - state.SetConcentration(a2, 0.5); - state.SetConcentration(b, 0.0); - state.SetConcentration(c, 0.0); - - model_concentrations[0] = state.variables_[0]; - analytical_concentrations[0] = { 1, 0, 0 }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - size_t idx_A = 0, idx_B = 1, idx_C = 2; - - std::vector times; - times.push_back(0); - for (size_t i_time = 1; i_time < nsteps; ++i_time) - { - times.push_back(time_step); - // Model results - auto result = solver.Solve(time_step, state); - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - model_concentrations[i_time] = result.result_.AsVector(); - state.variables_[0] = result.result_.AsVector(); - - // Analytical results - double time = i_time * time_step; - - double initial_A = analytical_concentrations[0][idx_A]; - analytical_concentrations[i_time][idx_A] = initial_A * std::exp(-(k1)*time); - analytical_concentrations[i_time][idx_B] = initial_A * (k1 / (k2 - k1)) * (std::exp(-k1 * time) - std::exp(-k2 * time)); - - analytical_concentrations[i_time][idx_C] = - initial_A * (1.0 + (k1 * std::exp(-k2 * time) - k2 * std::exp(-k1 * time)) / (k2 - k1)); - } - - auto header = state.variable_names_; - header.insert(header.begin(), "time"); - writeCSV("stiff_model_concentrations.csv", header, model_concentrations, times); - - auto map = state.variable_map_; - - size_t _a1 = map.at("A1"); - size_t _a2 = map.at("A2"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a1] + model_concentrations[i][_a2], analytical_concentrations[i][0], 1e-3) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], 1e-3) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], 1e-3) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } + test_analytical_stiff_branched>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); } TEST(AnalyticalExamples, Robertson) { - /* - * A -> B, k1 = 0.04 - * B + B -> C + B, k2 = 3e7 - * B + C -> A + C, k3 = 1e4 - * - * this problem is described in - * Hairer, E., Wanner, G., 1996. Solving Ordinary Differential Equations II: Stiff and Differential-Algebraic Problems, 2nd - * edition. ed. Springer, Berlin ; New York. Page 3 - * - * solutions are provided here - * https://www.unige.ch/~hairer/testset/testset.html - */ - - auto a = micm::Species("A"); - auto b = micm::Species("B"); - auto c = micm::Species("C"); - - micm::Phase gas_phase{ std::vector{ a, b, c } }; - - micm::Process r1 = micm::Process::create() - .reactants({ a }) - .products({ yields(b, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r1" })) - .phase(gas_phase); - - micm::Process r2 = micm::Process::create() - .reactants({ b, b }) - .products({ yields(b, 1), yields(c, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r2" })) - .phase(gas_phase); - - micm::Process r3 = micm::Process::create() - .reactants({ b, c }) - .products({ yields(a, 1), yields(c, 1) }) - .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) - .phase(gas_phase); - - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - double temperature = 272.5; - double pressure = 101253.3; - double air_density = 1e6; - - micm::State state = solver.GetState(); - - double k1 = 0.04; - double k2 = 3e7; - double k3 = 1e4; - - state.SetCustomRateParameter("r1", k1); - state.SetCustomRateParameter("r2", k2); - state.SetCustomRateParameter("r3", k3); - - constexpr size_t N = 12; - - std::vector> model_concentrations(N + 1, std::vector(3)); - std::vector> analytical_concentrations(N + 1, std::vector(3)); - - model_concentrations[0] = { 1, 0, 0 }; - - analytical_concentrations = { { 1, 0, 0 }, - { 0.9664597373330035E+00, 0.3074626578578675E-04, 0.3350951640121071E-01 }, - { 0.8413699238414729E+00, 0.1623390937990473E-04, 0.1586138422491472E+00 }, - { 0.6172348823960878E+00, 0.6153591274639123E-05, 0.3827589640126376E+00 }, - { 0.3368745306607069E+00, 0.2013702318261393E-05, 0.6631234556369748E+00 }, - { 0.1073004285378040E+00, 0.4800166972571660E-06, 0.8926990914454987E+00 }, - { 0.1786592114209946E-01, 0.7274751468436319E-07, 0.9821340061103859E+00 }, - { 0.2031483924973415E-02, 0.8142277783356159E-08, 0.9979685079327488E+00 }, - { 0.2076093439016395E-03, 0.8306077485067610E-09, 0.9997923898254906E+00 }, - { 0.2082417512179460E-04, 0.8329841429908955E-10, 0.9999791757415798E+00 }, - { 0.2083229471647004E-05, 0.8332935037760723E-11, 0.9999979167621954E+00 }, - { 0.2083328471883087E-06, 0.8333315602809495E-12, 0.9999997916663195E+00 }, - { 0.2083340149701284E-07, 0.8333360770334744E-13, 0.9999999791665152E+00 } }; - - state.variables_[0] = model_concentrations[0]; - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - double time_step = 1.0; - std::vector times; - times.push_back(0); - for (size_t i_time = 0; i_time < N; ++i_time) - { - double solve_time = time_step + i_time * time_step; - times.push_back(solve_time); - // Model results - double actual_solve = 0; - while (actual_solve < time_step) - { - auto result = solver.Solve(time_step - actual_solve, state); - state.variables_[0] = result.result_.AsVector(); - actual_solve += result.final_time_; - } - model_concentrations[i_time + 1] = state.variables_[0]; - time_step *= 10; - } - - std::vector header = { "time", "A", "B", "C" }; - writeCSV("model_concentrations.csv", header, model_concentrations, times); - writeCSV("analytical_concentrations.csv", header, analytical_concentrations, times); - - auto map = state.variable_map_; - - size_t _a = map.at("A"); - size_t _b = map.at("B"); - size_t _c = map.at("C"); - - double tol = 1e-1; - for (size_t i = 0; i < model_concentrations.size(); ++i) - { - EXPECT_NEAR(model_concentrations[i][_a], analytical_concentrations[i][0], tol) - << "Arrays differ at index (" << i << ", " << 0 << ")"; - EXPECT_NEAR(model_concentrations[i][_b], analytical_concentrations[i][1], tol) - << "Arrays differ at index (" << i << ", " << 1 << ")"; - EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], tol) - << "Arrays differ at index (" << i << ", " << 2 << ")"; - } + test_analytical_robertson>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); } TEST(AnalyticalExamples, Oregonator) From f7a7c50529f87f45c5561eb0a1fb37eeedce38c8 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:10:29 -0500 Subject: [PATCH 075/318] adding more instructions to contributors guide --- docs/source/contributing/index.rst | 92 ++++++++++++++++++- .../user_guide/rate_constant_tutorial.rst | 2 +- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/docs/source/contributing/index.rst b/docs/source/contributing/index.rst index 3acf97464..a444df2da 100644 --- a/docs/source/contributing/index.rst +++ b/docs/source/contributing/index.rst @@ -3,11 +3,99 @@ Contributing ============ For all proposed changes (bug fixes, new feature, documentation upates, etc.) please file -and [issue](https://github.com/NCAR/micm/issues/new/choose) detailing what your ask is or what you intend to do. +an `issue `_ detailing what your ask is or what you intend to do. The NCAR software developers will work with you on that issue to help recommend implementations, work through questions, or provide other background knowledge. +Testing +------- + Any code that you contribute must include associated unit tests or an integration test of some kind. Our tests run across various platforms and with different configurations automatically. By including tests that exercise your code, you help to -minimize the amount of time debugging platform portability issues. \ No newline at end of file +minimize the amount of time debugging platform portability issues. Further, new features must include at least an example +in our documentation. + +MICM collects code coverage statistics which is displayed on our homepage in a little badge + +.. image:: https://codecov.io/gh/NCAR/micm/branch/main/graph/badge.svg?token=ATGO4DKTMY + :target: https://codecov.io/gh/NCAR/micm + :alt: codecov badge + +This coverage is also reported from the code cov bot in most PRs, like `this one `_. +The coverage amount from any PR must not drop because of code that you add. This means that added code is required to be tested. +We don't require your code to have 100% test coverage since some edge cases (like poor configurations) or platforms (like GPUs) +may not be testable in the github runners, but at minimum you must not remove test coverage where it exists. + +Style guide +----------- +We (mostly) follow the `Google C++ style guide `_. Please attempt to do +the same to minimize comments on PRs. However, we are not dogmatic and reasonable exceptions are allowed, especially where they +help to simplify code or API intefaces. + +After each PR is pulled, we have an automated github action that runs ``clang-format`` over our codebase, so don't worry too much +about formatting your code. Some of that format may be removed anyway. No format configuration is liked by everyone, but the use +of ``clang-format`` enforces some consistency which means moving from file to file shouldn't have a large congitive load on the developer. + +Setting up developer environments +--------------------------------- + +macOS +^^^^^ + +Linux +^^^^^ + +Windows +^^^^^^^ + +Building the documentation +-------------------------- + +All of our docs are stored in the ``docs`` directory. There are several python dependencies that need to install. To make +this easy, we provide a ``reuirements.txt`` file that you can use to install. + +Virtualenv +^^^^^^^^^^ + +From the root directory of micm: + +.. code-block:: bash + + pip install virtualenv + virtualenv micm_env + source micm_env/bin/activate + cd docs + pip install -r requirements.txt + cd .. && mkdir build && cd build + cmake -DBUILD_DOCS=ON .. + make docs + open docs/sphinx/index.html + +Venv +^^^^ + +.. code-block:: bash + + python -m venv micm_env + source micm_env/bin/activate + cd docs + pip install -r requirements.txt + cd .. && mkdir build && cd build + cmake -DBUILD_DOCS=ON .. + make docs + open docs/sphinx/index.html + +Conda +^^^^^ + +.. code-block:: bash + + conda create --name micm_env python=3.8 -y + conda activate micm_env + cd docs + pip install -r requirements.txt + cd .. && mkdir build && cd build + cmake -DBUILD_DOCS=ON .. + make docs + open docs/sphinx/index.html \ No newline at end of file diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index f3857e8b9..d4ae23515 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -152,7 +152,7 @@ Finally, we are ready to pick a timestep and solve the system. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 157-183 + :lines: 149-173 This is the output: From 68de1919fe4a7e06f2a2aed9b26b7b710228bf91 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:18:47 -0500 Subject: [PATCH 076/318] Update test/tutorial/test_README_example.cpp Co-authored-by: Matt Dawson --- test/tutorial/test_README_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index c088bc466..91d379e92 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -38,7 +38,7 @@ int main(const int argc, const char *argv[]) state.conditions_[0].pressure_ = 101319.9; // Pa state.SetConcentration(foo, 20.0); // mol m-3 - std::cout << std::setw(5) << "time," + std::cout << std::setw(5) << "time [s]," << std::setw(13) << "foo, " << std::setw(12) << "bar, " << std::setw(10) << "baz" << std::endl; From 467f618a1540ead9bcdf1532445fae9f6c40c344 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:21:35 -0500 Subject: [PATCH 077/318] adding units to output --- README.md | 22 +++++++++++----------- docs/source/getting_started.rst | 2 +- test/tutorial/test_README_example.cpp | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 864c37224..d3298048d 100644 --- a/README.md +++ b/README.md @@ -144,17 +144,17 @@ g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.1.0/include -std=c++20 Output: ``` -time, foo, bar, baz - 0, 11.843503, 5.904845, 1.907012 - 1, 6.792023, 9.045965, 3.317336 - 2, 3.828700, 10.740589, 4.210461 - 3, 2.138145, 11.663685, 4.739393 - 4, 1.187934, 12.169452, 5.042503 - 5, 0.658129, 12.447502, 5.213261 - 6, 0.363985, 12.600676, 5.308597 - 7, 0.201076, 12.685147, 5.361559 - 8, 0.111028, 12.731727, 5.390884 - 9, 0.061290, 12.757422, 5.407096 +time [s], foo, bar, baz + 0, 11.843503, 5.904845, 1.907012 + 1, 6.792023, 9.045965, 3.317336 + 2, 3.828700, 10.740589, 4.210461 + 3, 2.138145, 11.663685, 4.739393 + 4, 1.187934, 12.169452, 5.042503 + 5, 0.658129, 12.447502, 5.213261 + 6, 0.363985, 12.600676, 5.308597 + 7, 0.201076, 12.685147, 5.361559 + 8, 0.111028, 12.731727, 5.390884 + 9, 0.061290, 12.757422, 5.407096 ``` # Citation diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index b33c4e8fb..deb94078a 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -89,7 +89,7 @@ To build and run the example using GNU:: You should see an output including this .. csv-table:: - :header: "time", "foo", "bar", "baz" + :header: "time [s]", "foo", "bar", "baz" :widths: 6, 10, 10, 10 0, 11.843503, 5.904845, 1.907012 diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index 91d379e92..bda19f04d 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -47,7 +47,7 @@ int main(const int argc, const char *argv[]) auto result = solver.Solve(500.0, state); state.variables_ = result.result_; std::cout << std::fixed << std::setprecision(6) - << std::setw(4) << i << ", " + << std::setw(8) << i << ", " << std::setw(10) << state.variables_[0][state.variable_map_["Foo"]] << ", " << std::setw(10) << state.variables_[0][state.variable_map_["Bar"]] << ", " << std::setw(10) << state.variables_[0][state.variable_map_["Baz"]] From 4c635a13b4b0b4b59bf742532c7464070fc28a04 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:22:46 -0500 Subject: [PATCH 078/318] correcting time output --- README.md | 20 ++++++++++---------- docs/source/getting_started.rst | 20 ++++++++++---------- test/tutorial/test_README_example.cpp | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index d3298048d..ef89ba8f3 100644 --- a/README.md +++ b/README.md @@ -145,16 +145,16 @@ g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.1.0/include -std=c++20 Output: ``` time [s], foo, bar, baz - 0, 11.843503, 5.904845, 1.907012 - 1, 6.792023, 9.045965, 3.317336 - 2, 3.828700, 10.740589, 4.210461 - 3, 2.138145, 11.663685, 4.739393 - 4, 1.187934, 12.169452, 5.042503 - 5, 0.658129, 12.447502, 5.213261 - 6, 0.363985, 12.600676, 5.308597 - 7, 0.201076, 12.685147, 5.361559 - 8, 0.111028, 12.731727, 5.390884 - 9, 0.061290, 12.757422, 5.407096 +0.000000, 11.843503, 5.904845, 1.907012 +500.000000, 6.792023, 9.045965, 3.317336 +1000.000000, 3.828700, 10.740589, 4.210461 +1500.000000, 2.138145, 11.663685, 4.739393 +2000.000000, 1.187934, 12.169452, 5.042503 +2500.000000, 0.658129, 12.447502, 5.213261 +3000.000000, 0.363985, 12.600676, 5.308597 +3500.000000, 0.201076, 12.685147, 5.361559 +4000.000000, 0.111028, 12.731727, 5.390884 +4500.000000, 0.061290, 12.757422, 5.407096 ``` # Citation diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index deb94078a..8e531f5ea 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -92,14 +92,14 @@ You should see an output including this :header: "time [s]", "foo", "bar", "baz" :widths: 6, 10, 10, 10 - 0, 11.843503, 5.904845, 1.907012 - 1, 6.792023, 9.045965, 3.317336 - 2, 3.828700, 10.740589, 4.210461 - 3, 2.138145, 11.663685, 4.739393 - 4, 1.187934, 12.169452, 5.042503 - 5, 0.658129, 12.447502, 5.213261 - 6, 0.363985, 12.600676, 5.308597 - 7, 0.201076, 12.685147, 5.361559 - 8, 0.111028, 12.731727, 5.390884 - 9, 0.061290, 12.757422, 5.407096 + 0.000000, 11.843503, 5.904845, 1.907012 + 500.000000, 6.792023, 9.045965, 3.317336 + 1000.000000, 3.828700, 10.740589, 4.210461 + 1500.000000, 2.138145, 11.663685, 4.739393 + 2000.000000, 1.187934, 12.169452, 5.042503 + 2500.000000, 0.658129, 12.447502, 5.213261 + 3000.000000, 0.363985, 12.600676, 5.308597 + 3500.000000, 0.201076, 12.685147, 5.361559 + 4000.000000, 0.111028, 12.731727, 5.390884 + 4500.000000, 0.061290, 12.757422, 5.407096 diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index bda19f04d..593020e65 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -47,7 +47,7 @@ int main(const int argc, const char *argv[]) auto result = solver.Solve(500.0, state); state.variables_ = result.result_; std::cout << std::fixed << std::setprecision(6) - << std::setw(8) << i << ", " + << std::setw(8) << i * 500.0 << ", " << std::setw(10) << state.variables_[0][state.variable_map_["Foo"]] << ", " << std::setw(10) << state.variables_[0][state.variable_map_["Bar"]] << ", " << std::setw(10) << state.variables_[0][state.variable_map_["Baz"]] From 729dbab24eaaf229c8dec47520af8e0cb1eff437 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:23:24 -0500 Subject: [PATCH 079/318] Update docs/source/contributing/index.rst Co-authored-by: Matt Dawson --- docs/source/contributing/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/contributing/index.rst b/docs/source/contributing/index.rst index a444df2da..ef78029d9 100644 --- a/docs/source/contributing/index.rst +++ b/docs/source/contributing/index.rst @@ -11,7 +11,7 @@ or provide other background knowledge. Testing ------- -Any code that you contribute must include associated unit tests or an integration test of some kind. Our tests run across +Any code that you contribute must include associated unit tests and an integration test of some kind. Our tests run across various platforms and with different configurations automatically. By including tests that exercise your code, you help to minimize the amount of time debugging platform portability issues. Further, new features must include at least an example in our documentation. From 0a40c7dbedc6e122830b2ddb351f8e053f30529e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:23:30 -0500 Subject: [PATCH 080/318] Update docs/source/contributing/index.rst Co-authored-by: Matt Dawson --- docs/source/contributing/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/contributing/index.rst b/docs/source/contributing/index.rst index ef78029d9..3e5950e5b 100644 --- a/docs/source/contributing/index.rst +++ b/docs/source/contributing/index.rst @@ -53,7 +53,7 @@ Building the documentation -------------------------- All of our docs are stored in the ``docs`` directory. There are several python dependencies that need to install. To make -this easy, we provide a ``reuirements.txt`` file that you can use to install. +this easy, we provide a ``requirements.txt`` file that you can use to install. Virtualenv ^^^^^^^^^^ From affcb3b3add0f062ab04bc362426f98023c15871 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:27:24 -0500 Subject: [PATCH 081/318] hopefully copying the switcher --- .github/workflows/gh_pages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/gh_pages.yml b/.github/workflows/gh_pages.yml index fd5eaaa7a..d32d350dc 100644 --- a/.github/workflows/gh_pages.yml +++ b/.github/workflows/gh_pages.yml @@ -67,13 +67,13 @@ jobs: docker cp $id:/build/docs/sphinx tmpdocs docker rm -v $id mv tmpdocs _build/html/versions/stable - mv docs/switcher.json _build/html # Delete everything under _gh-pages/ that is from the # primary branch deployment. Excludes the other branches # _gh-pages/branch-* paths, and not including # _gh-pages itself. find _gh-pages/ -mindepth 1 ! -path '_gh-pages/branch*' ! -path '_gh-pages/versions*' -delete rsync -a _build/html/versions/stable/* _gh-pages/ + mv docs/switcher.json _gh-pages # If a push and not on default branch, then copy the build to # _gh-pages/branch/$brname (transforming '/' into '--') From 85f09d06a8d6f3d54f365de5a8a8762f438b0dda Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:31:57 -0500 Subject: [PATCH 082/318] trying to enable more tests --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7de7b08ba..ad4cefd3d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,8 +18,8 @@ jobs: - Dockerfile.memcheck - Dockerfile.no_json - Dockerfile.nvhpc - # - Dockerfile.intel # intel image is too large for GH action - # - Dockerfile.openmp + - Dockerfile.openmp + - Dockerfile.intel # intel image is too large for GH action # - Dockerfile.mpi steps: - name: Checkout code From 837b43199254a0698726370fb286a163eb764d7e Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 4 Oct 2023 12:32:19 -0700 Subject: [PATCH 083/318] add CMake argument for vector matrix size --- CMakeLists.txt | 3 +++ include/micm/process/jit_process_set.hpp | 2 +- include/micm/solver/jit_lu_decomposition.hpp | 2 +- include/micm/solver/jit_rosenbrock.hpp | 2 +- include/micm/util/sparse_matrix_vector_ordering.hpp | 7 +++++-- include/micm/util/vector_matrix.hpp | 6 +++++- 6 files changed, 16 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a74139d93..12ee0dca4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ option(BUILD_DOCS "Build the documentation" OFF) option(ENABLE_CUDA "Build with Cuda support" OFF) option(ENABLE_OPENACC "Build with OpenACC Support" OFF) option(ENABLE_LLVM "Build with LLVM support for JIT-compiling" OFF) +set(DEFAULT_VECTOR_MATRIX_SIZE "4" CACHE STRING "Default size for vectorizable matrix types") include(CMakeDependentOption) # Option to collect custom OpenACC flags @@ -51,6 +52,8 @@ if(ENABLE_GPU_TYPE) set(GPU_TYPE "" CACHE STRING "The GPU type being targeted") endif() +add_compile_definitions(DEFAULT_VECTOR_SIZE=${DEFAULT_VECTOR_MATRIX_SIZE}) + ################################################################################ # Dependencies diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 73ce47c08..ef3926beb 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -14,7 +14,7 @@ namespace micm /// @brief JIT-compiled solver function calculators for a collection of processes /// The template parameter is the number of grid cells to solve simultaneously - template + template class JitProcessSet : public ProcessSet { std::shared_ptr compiler_; diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index 61ed48194..f9a2aca5e 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -15,7 +15,7 @@ namespace micm /// /// See LuDecomposition class description for algorithm details /// The template parameter is the number of blocks (i.e. grid cells) in the block-diagonal matrix - template + template class JitLuDecomposition : public LuDecomposition { std::shared_ptr compiler_; diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 14fd8b656..3380ed713 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -30,7 +30,7 @@ namespace micm template< template class MatrixPolicy = VectorMatrix, template class SparseMatrixPolicy = VectorSparseMatrix, - class LinearSolverPolicy = JitLinearSolver<4, SparseMatrixPolicy>> + class LinearSolverPolicy = JitLinearSolver> class JitRosenbrockSolver : public RosenbrockSolver { std::shared_ptr compiler_; diff --git a/include/micm/util/sparse_matrix_vector_ordering.hpp b/include/micm/util/sparse_matrix_vector_ordering.hpp index eaa7b84d8..19f76e668 100644 --- a/include/micm/util/sparse_matrix_vector_ordering.hpp +++ b/include/micm/util/sparse_matrix_vector_ordering.hpp @@ -4,6 +4,9 @@ #pragma once #include + +#include "vector_matrix.hpp" + namespace micm { @@ -15,7 +18,7 @@ namespace micm /// /// The template argument is the number of blocks per set of blocks and should be /// approximately the size of the vector register. - template + template class SparseMatrixVectorOrdering { protected: @@ -64,6 +67,6 @@ namespace micm // Default vectorized SparseMatrix template - using VectorSparseMatrix = SparseMatrix>; + using VectorSparseMatrix = SparseMatrix>; } // namespace micm \ No newline at end of file diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index 32000cef6..a8507f6f9 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -9,6 +9,10 @@ #include #include +#ifndef DEFAULT_VECTOR_SIZE +# define DEFAULT_VECTOR_SIZE 4 +#endif + namespace micm { @@ -19,7 +23,7 @@ namespace micm /// /// The template arguments are the type of the matrix elements and the size of the number /// of rows per group. - template + template class VectorMatrix { std::vector data_; From c73dcdbf48f7fcc7ca4a8c6b2edcad3c14946284 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:37:49 -0500 Subject: [PATCH 084/318] intel is still too large --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ad4cefd3d..508ed8233 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: - Dockerfile.no_json - Dockerfile.nvhpc - Dockerfile.openmp - - Dockerfile.intel # intel image is too large for GH action + # - Dockerfile.intel # intel image is too large for GH action # - Dockerfile.mpi steps: - name: Checkout code From 9d7cff9bf56abbf648a9c9b58e318723303f8ea1 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:40:24 -0500 Subject: [PATCH 085/318] only running one version of the xcode tests --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 508ed8233..015e00bfc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -106,6 +106,7 @@ jobs: ctest -C Debug --rerun-failed --output-on-failure . --verbose xcode_1: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name runs-on: macos-12 strategy: matrix: @@ -129,6 +130,7 @@ jobs: ctest --rerun-failed --output-on-failure . --verbose -j 10 xcode_2: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name runs-on: macos-13 strategy: matrix: From 63ace0fd26cff83bccea8e2ac6beba5bc61aa66e Mon Sep 17 00:00:00 2001 From: Qina Tan <72781532+qinatan@users.noreply.github.com> Date: Wed, 4 Oct 2023 13:46:07 -0600 Subject: [PATCH 086/318] Decompose cuda (#257) * work in progress * work in progress * test first step * typo * changed template * changed template * debug * debug * added matrixParam to AddForcingTermsKernelDriver * changed header * debug * debug * debug * test * passing vector_matrix directly * debug * implemented simple class * add std::vector * debug * ahhh * passing 3 matrix data as a group * deleted const * debug * debug * debug * debug * debug * added matrix data in a group for AddJacobianFunction * debug * store d_rate_constants in struct pointer * using cudaMallocManaged * using cudaMallocManaged * using cudaMallocManaged * device strcut * get all pointers into struct for AddJacobian * debug * debug * passed? * passing struct of processSet to kernelDriver * clean up * removed header * group processSet data members into struct * minor fix * minor fix * get rid of micm from struct * debug * debug * minor fix * minor fix * minor fix * clean up * debug * test * clean up * clean up * clean up * clean up * test * test out for storing pointer to variable * store reused device pointer to variable in kernel * store reused device pointer to variable in kernel * work in progress - 8/31 * added struct for sparseMatrix * added struct for sparseMatrix * sparse matrix in struct * compiling test * compiling test * test run for decompose * test run for decompose function * edited cmake * minor fix * test * test for passing std::vector to .cu file * testing for thrust * moved all vectors * test * cleanup * minor fix * cleanup * added header file for vector * minor fix * debug * chnage ngrids in test * checking on cpu code * bug * trying for smaller thrust vector * vector reallocation failed? * small bug * test memory allocation * clean up vectors * added const * debug * no more vector * added const * removed header * added cmake * edited header file * debug * debug * change dot operator * testing * test * small bug * test * testing * testing cpu code * changed kernel config * changed kernel config * changed kernel config * checking pair reference in GPU * debug * debug * bug * transfer data from device to data * transfer data * get rid of i++ * test * bug * bug * bug * inside the kernel * debug with print statement * debug print * modified test * print * print statement * print * test * debug * debug * printing aik * debug * get rid of ++ inside [] * debug * changed * changed * changed * test * test * print test * testing for one grid * testing for one grid * print A sparseMatrix * print * print A outside of kernel * printing A with pointer * printing A with pointer * print A * check * print lf * print lf * test * added synchronization * try to print with hd * test * check * check * test * print * test * checking niLU second * test * check * test * debug * test * test * added more print * added more print * test * test * more debug * more print * test * more print * more print * more print * more print * more print * more print * more print * more print * test * more print * test * more print * more print * more print * more print * more print * test * test * test * print * print * print * pass? cleanup * changed to expect_near to expect_eq * changed back to expect_near * used constructor from base class * eliminated dervied class constructor * added underscore to all struct members * added more underscore * added more underscore * added more underscore * more modification * added time measurement * GPU test against CPU * added time measurement * added time measurement * vector * addSparseMatrix template * test * typo * added std to vector * minor change * incremental change made to processSet * modified cuda_process_set.cuh * changed to device struct --- include/micm/process/cuda_process_set.cuh | 14 +- include/micm/process/cuda_process_set.hpp | 85 +++---- include/micm/solver/cuda_lu_decomposition.cuh | 15 ++ include/micm/solver/cuda_lu_decomposition.hpp | 70 ++++++ include/micm/solver/lu_decomposition.hpp | 13 +- include/micm/util/cuda_param.hpp | 88 ++++--- src/CMakeLists.txt | 3 +- src/process/process_set.cu | 225 +++++++++--------- src/solver/CMakeLists.txt | 6 + src/solver/lu_decomposition.cu | 184 ++++++++++++++ test/unit/process/test_cuda_process_set.cpp | 3 +- test/unit/solver/CMakeLists.txt | 5 + .../solver/test_cuda_lu_decomposition.cpp | 121 ++++++++++ 13 files changed, 636 insertions(+), 196 deletions(-) create mode 100644 include/micm/solver/cuda_lu_decomposition.cuh create mode 100644 include/micm/solver/cuda_lu_decomposition.hpp create mode 100644 src/solver/CMakeLists.txt create mode 100644 src/solver/lu_decomposition.cu create mode 100644 test/unit/solver/test_cuda_lu_decomposition.cpp diff --git a/include/micm/process/cuda_process_set.cuh b/include/micm/process/cuda_process_set.cuh index 088d25f34..38cb81113 100644 --- a/include/micm/process/cuda_process_set.cuh +++ b/include/micm/process/cuda_process_set.cuh @@ -1,17 +1,21 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 #pragma once #include +#include namespace micm { namespace cuda { std::chrono::nanoseconds AddForcingTermsKernelDriver( - CUDAMatrixParam& matrixParam, - CUDAProcessSetParam& processSet); + CudaMatrixParam& matrixParam, + CudaProcessSetParam& processSet); std::chrono::nanoseconds AddJacobianTermsKernelDriver( - CUDAMatrixParam& matrixParam, - CUDASparseMatrixParam& sparseMatrix, - CUDAProcessSetParam& processSet); + CudaMatrixParam& matrixParam, + CudaSparseMatrixParam& sparseMatrix, + CudaProcessSetParam& processSet); } // namespace cuda } // namespace micm diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index 97edd9f89..f7ced87ab 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -5,6 +5,7 @@ #include #include +#include #ifdef USE_CUDA # include @@ -52,25 +53,27 @@ namespace micm const MatrixPolicy& state_variables, MatrixPolicy& forcing) const { - CUDAMatrixParam matrixParam; - matrixParam.rate_constants = rate_constants.AsVector().data(); - matrixParam.state_variables = state_variables.AsVector().data(); - matrixParam.forcing = forcing.AsVector().data(); - matrixParam.n_grids = rate_constants.size(); - matrixParam.n_reactions = rate_constants[0].size(); - matrixParam.n_species = state_variables[0].size(); + CudaMatrixParam matrix; + matrix.rate_constants_ = rate_constants.AsVector().data(); + matrix.state_variables_ = state_variables.AsVector().data(); + matrix.forcing_ = forcing.AsVector().data(); + matrix.n_grids_ = rate_constants.size(); + matrix.n_reactions_ = rate_constants[0].size(); + matrix.n_species_ = state_variables[0].size(); - CUDAProcessSetParam processSet; - processSet.number_of_reactants = number_of_reactants_.data(); - processSet.reactant_ids = reactant_ids_.data(); - processSet.reactant_ids_size = reactant_ids_.size(); - processSet.number_of_products = number_of_products_.data(); - processSet.product_ids = product_ids_.data(); - processSet.product_ids_size = product_ids_.size(); - processSet.yields = yields_.data(); - processSet.yields_size = yields_.size(); + CudaProcessSetParam processSet; + processSet.number_of_reactants_ = number_of_reactants_.data(); + processSet.reactant_ids_ = reactant_ids_.data(); + processSet.reactant_ids_size_ = reactant_ids_.size(); + processSet.number_of_products_ = number_of_products_.data(); + processSet.product_ids_ = product_ids_.data(); + processSet.product_ids_size_ = product_ids_.size(); + processSet.yields_ = yields_.data(); + processSet.yields_size_ = yields_.size(); - std::chrono::nanoseconds kernel_duration = micm::cuda::AddForcingTermsKernelDriver(matrixParam, processSet); + std::chrono::nanoseconds kernel_duration = micm::cuda::AddForcingTermsKernelDriver( + matrix, + processSet); return kernel_duration; // time performance of kernel function } template class MatrixPolicy, template class SparseMatrixPolicy> @@ -80,29 +83,31 @@ namespace micm const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const { - CUDAMatrixParam matrixParam; - matrixParam.rate_constants = rate_constants.AsVector().data(); - matrixParam.state_variables = state_variables.AsVector().data(); - matrixParam.n_grids = rate_constants.size(); - matrixParam.n_reactions = rate_constants[0].size(); - matrixParam.n_species = state_variables[0].size(); - - CUDASparseMatrixParam sparseMatrix; - sparseMatrix.jacobian = jacobian.AsVector().data(); - sparseMatrix.jacobian_size = jacobian.AsVector().size(); - - CUDAProcessSetParam processSet; - processSet.number_of_reactants = number_of_reactants_.data(); - processSet.reactant_ids = reactant_ids_.data(); - processSet.reactant_ids_size = reactant_ids_.size(); - processSet.number_of_products = number_of_products_.data(); - processSet.yields = yields_.data(); - processSet.yields_size = yields_.size(); - processSet.jacobian_flat_ids = jacobian_flat_ids_.data(); - processSet.jacobian_flat_ids_size = jacobian_flat_ids_.size(); - - std::chrono::nanoseconds kernel_duration = - micm::cuda::AddJacobianTermsKernelDriver(matrixParam, sparseMatrix, processSet); + CudaMatrixParam matrix; + matrix.rate_constants_ = rate_constants.AsVector().data(); + matrix.state_variables_ = state_variables.AsVector().data(); + matrix.n_grids_ = rate_constants.size(); + matrix.n_reactions_ = rate_constants[0].size(); + matrix.n_species_ = state_variables[0].size(); + + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.jacobian_ = jacobian.AsVector().data(); + sparseMatrix.jacobian_size_ = jacobian.AsVector().size(); + + CudaProcessSetParam processSet; + processSet.number_of_reactants_ = number_of_reactants_.data(); + processSet.reactant_ids_ = reactant_ids_.data(); + processSet.reactant_ids_size_ = reactant_ids_.size(); + processSet.number_of_products_ = number_of_products_.data(); + processSet.yields_ = yields_.data(); + processSet.yields_size_ = yields_.size(); + processSet.jacobian_flat_ids_ = jacobian_flat_ids_.data(); + processSet.jacobian_flat_ids_size_ = jacobian_flat_ids_.size(); + + std::chrono::nanoseconds kernel_duration = micm::cuda::AddJacobianTermsKernelDriver( + matrix, + sparseMatrix, + processSet); return kernel_duration; // time performance of kernel function } } // namespace micm diff --git a/include/micm/solver/cuda_lu_decomposition.cuh b/include/micm/solver/cuda_lu_decomposition.cuh new file mode 100644 index 000000000..70ff61fbe --- /dev/null +++ b/include/micm/solver/cuda_lu_decomposition.cuh @@ -0,0 +1,15 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +namespace micm +{ + namespace cuda + { + std::chrono::nanoseconds DecomposeKernelDriver( + CudaSparseMatrixParam& sparseMatrix, + CudaSolverParam& solver); + } // namespace cuda +} // namespace micm diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp new file mode 100644 index 000000000..f5aa469cd --- /dev/null +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -0,0 +1,70 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +#include +#include +#ifdef USE_CUDA +#include +#endif + +#ifdef USE_CUDA +namespace micm{ + class CudaLuDecomposition: public LuDecomposition{ + public: + CudaLuDecomposition(){}; + + template typename SparseMatrixPolicy> + requires VectorizableSparse> + std::chrono::nanoseconds Decompose( + const SparseMatrixPolicy&A, + SparseMatrixPolicy& L, + SparseMatrixPolicy& U) const; + }; + + template class SparseMatrixPolicy> + requires(VectorizableSparse>) + std::chrono::nanoseconds CudaLuDecomposition::Decompose( + const SparseMatrixPolicy& A, + SparseMatrixPolicy& L, + SparseMatrixPolicy& U) const + { + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.A_ = A.AsVector().data(); + sparseMatrix.A_size_ = A.AsVector().size(); + sparseMatrix.L_ = L.AsVector().data(); + sparseMatrix.L_size_ = L.AsVector().size(); + sparseMatrix.U_ = U.AsVector().data(); + sparseMatrix.U_size_ = U.AsVector().size(); + sparseMatrix.n_grids_ = A.size(); + + CudaSolverParam solver; + solver.do_aik_ = do_aik_.data(); + solver.do_aik_size_ = do_aik_.size(); + solver.aik_ = aik_.data(); + solver.aik_size_ = aik_.size(); + solver.do_aki_ = do_aki_.data(); + solver.do_aki_size_ = do_aki_.size(); + solver.aki_ = aki_.data(); + solver.aki_size_ = aki_.size(); + solver.uii_ = uii_.data(); + solver.uii_size_ = uii_.size(); + + solver.niLU_ = niLU_.data(); + solver.niLU_size_ = niLU_.size(); + solver.uik_nkj_ = uik_nkj_.data(); + solver.uik_nkj_size_ = uik_nkj_.size(); + solver.lij_ujk_ = lij_ujk_.data(); + solver.lij_ujk_size_ = lij_ujk_.size(); + solver.lki_nkj_ = lki_nkj_.data(); + solver.lki_nkj_size_ = lki_nkj_.size(); + solver.lkj_uji_ = lkj_uji_.data(); + solver.lkj_uji_size_ = lkj_uji_.size(); + + //calling kernelSetup function + return micm::cuda::DecomposeKernelDriver(sparseMatrix, solver); + } +}//end micm +#endif \ No newline at end of file diff --git a/include/micm/solver/lu_decomposition.hpp b/include/micm/solver/lu_decomposition.hpp index dea4cbfde..b075abe54 100644 --- a/include/micm/solver/lu_decomposition.hpp +++ b/include/micm/solver/lu_decomposition.hpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once - #include namespace micm @@ -39,8 +38,9 @@ namespace micm /// for each iteration of the outer (i) loop std::vector> niLU_; /// True when A[i][k] is non-zero for each iteration of the middle (k) loop for the upper - /// triangular matrix; False otherwise - std::vector do_aik_; + /// triangular matrix; False otherwise. Used data type char instead of bool because vector representation + ///does not suppor easy retrieval of memory address using data() function. + std::vector do_aik_; /// Index in A.data_ for A[i][k] for each iteration of the middle (k) loop for the upper /// triangular matrix when A[i][k] is non-zero std::vector aik_; @@ -52,10 +52,11 @@ namespace micm /// when L[i][j] and U[j][k] are both non-zero. std::vector> lij_ujk_; /// True when A[k][i] is non-zero for each iteration of the middle (k) loop for the lower - /// triangular matrix; False otherwise - std::vector do_aki_; + /// triangular matrix; False otherwise. Used data type char instead of bool because vector representation + ///does not suppor easy retrieval of memory address using data() function. + std::vector do_aki_; /// Index in A.data_ for A[k][i] for each iteration of the middle (k) loop for the lower - /// triangular matrix when A[k][i] is non-zero + /// triangular matrix when A[k][i] is non-zero. std::vector aki_; /// Index in L.data_ for L[k][i] for each iteration of the middle (k) loop for the lower /// triangular matrix when L[k][i] is non-zero, and the corresponding number of elements diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index a59128f4e..8db6aa01d 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -1,33 +1,63 @@ +#include + #ifndef CUDA_PARAM_HPP #define CUDA_PARAM_HPP -// member data of class CUDAProcessSet grouped in struct passing to kernel driver function -struct CUDAProcessSetParam -{ - const size_t* number_of_reactants; - const size_t* reactant_ids; - size_t reactant_ids_size; - const size_t* number_of_products; - const size_t* product_ids; - size_t product_ids_size; - const double* yields; - size_t yields_size; - const size_t* jacobian_flat_ids; - size_t jacobian_flat_ids_size; -}; -// different matrix data grouped in struct passing to kernel driver function -struct CUDAMatrixParam -{ - const double* rate_constants; - const double* state_variables; - double* forcing; - size_t n_grids; - size_t n_reactions; - size_t n_species; -}; -// sparseMatrix data grouped in struct passing to kernel driver function -struct CUDASparseMatrixParam -{ - double* jacobian; - size_t jacobian_size; + //member data of class CudaProcessSet grouped in struct passing to kernel driver function + const size_t BLOCK_SIZE = 320; + struct CudaProcessSetParam{ + const size_t* number_of_reactants_; + const size_t* reactant_ids_; + size_t reactant_ids_size_; + const size_t* number_of_products_; + const size_t* product_ids_; + size_t product_ids_size_; + const double* yields_; + size_t yields_size_; + const size_t* jacobian_flat_ids_; + size_t jacobian_flat_ids_size_; +}; + + struct CudaSolverParam{ + const std::pair* niLU_; + size_t niLU_size_; + const char* do_aik_; + size_t do_aik_size_; + const size_t* aik_; + size_t aik_size_; + const std::pair* uik_nkj_; + size_t uik_nkj_size_; + const std::pair* lij_ujk_; + size_t lij_ujk_size_; + const char* do_aki_; + size_t do_aki_size_; + const size_t* aki_; + size_t aki_size_; + const std::pair* lki_nkj_; + size_t lki_nkj_size_; + const std::pair* lkj_uji_; + size_t lkj_uji_size_; + const size_t* uii_; + size_t uii_size_; + }; + //different matrix data grouped in struct passing to kernel driver function + struct CudaMatrixParam{ + const double* rate_constants_; + const double* state_variables_; + double* forcing_; + size_t n_grids_; + size_t n_reactions_; + size_t n_species_; +}; +//sparseMatrix data grouped in struct passing to kernel driver function +struct CudaSparseMatrixParam{ + double* jacobian_; + size_t jacobian_size_; + const double* A_; + size_t A_size_; + double* L_; + size_t L_size_; + double* U_; + size_t U_size_; + size_t n_grids_; }; #endif \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3bff67130..1766b5172 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,4 +86,5 @@ if(ENABLE_CUDA) ) endif() -add_subdirectory(process) \ No newline at end of file +add_subdirectory(process) +add_subdirectory(solver) \ No newline at end of file diff --git a/src/process/process_set.cu b/src/process/process_set.cu index 386005ffe..b515a6d02 100644 --- a/src/process/process_set.cu +++ b/src/process/process_set.cu @@ -7,46 +7,45 @@ #include // device pointers passing to AddForcingTermsKernel() -typedef struct forcingDevice +typedef struct ForcingDevice { - double* rate_constants; - double* state_variables; - double* forcing; - size_t* number_of_reactants; - size_t* reactant_ids; - size_t* number_of_products; - size_t* product_ids; - double* yields; + double* rate_constants_; + double* state_variables_; + double* forcing_; + size_t* number_of_reactants_; + size_t* reactant_ids_; + size_t* number_of_products_; + size_t* product_ids_; + double* yields_; }; // device pointers passing to AddJacobianTermsKernel() -typedef struct jacobianDevice +typedef struct JacobianDevice { - double* rate_constants; - double* state_variables; - double* jacobian; - size_t* number_of_reactants; - size_t* reactant_ids; - size_t* number_of_products; - double* yields; - size_t* jacobian_flat_ids; + double* rate_constants_; + double* state_variables_; + double* jacobian_; + size_t* number_of_reactants_; + size_t* reactant_ids_; + size_t* number_of_products_; + double* yields_; + size_t* jacobian_flat_ids_; }; -const size_t BLOCK_SIZE = 320; namespace micm { namespace cuda { // flipped memory layout - __global__ void AddForcingTermsKernel(forcingDevice* device, size_t n_grids, size_t n_reactions, size_t n_species) + __global__ void AddForcingTermsKernel(ForcingDevice* device, size_t n_grids, size_t n_reactions, size_t n_species) { // define thread index size_t tid = blockIdx.x * blockDim.x + threadIdx.x; size_t react_id_offset, prod_id_offset, yield_offset; - double* forcing = device->forcing; - size_t* number_of_reactants = device->number_of_reactants; - size_t* reactant_ids = device->reactant_ids; - size_t* number_of_products = device->number_of_products; + double* forcing = device->forcing_; + size_t* number_of_reactants = device->number_of_reactants_; + size_t* reactant_ids = device->reactant_ids_; + size_t* number_of_products = device->number_of_products_; if (tid < n_grids) { react_id_offset = 0; @@ -54,35 +53,35 @@ namespace micm yield_offset = 0; for (std::size_t i_rxn = 0; i_rxn < n_reactions; ++i_rxn) { - double rate = device->rate_constants[i_rxn * n_grids + tid]; + double rate = device->rate_constants_[i_rxn * n_grids + tid]; for (std::size_t i_react = 0; i_react < number_of_reactants[i_rxn]; ++i_react) - rate *= device->state_variables[reactant_ids[react_id_offset + i_react] * n_grids + tid]; + rate *= device->state_variables_[reactant_ids[react_id_offset + i_react] * n_grids + tid]; for (std::size_t i_react = 0; i_react < number_of_reactants[i_rxn]; ++i_react) { forcing[reactant_ids[react_id_offset + i_react] * n_grids + tid] -= rate; } for (std::size_t i_prod = 0; i_prod < number_of_products[i_rxn]; ++i_prod) { - size_t index = device->product_ids[prod_id_offset + i_prod] * n_grids + tid; - forcing[index] += device->yields[yield_offset + i_prod] * rate; + size_t index = device->product_ids_[prod_id_offset + i_prod] * n_grids + tid; + forcing[index] += device->yields_[yield_offset + i_prod] * rate; } react_id_offset += number_of_reactants[i_rxn]; prod_id_offset += number_of_products[i_rxn]; yield_offset += number_of_products[i_rxn]; } // for loop over number of reactions - } // if check for valid CUDA threads + } // if check for valid Cuda threads } // end of AddForcingTerms_kernel - __global__ void AddJacobianTermsKernel(jacobianDevice* device, size_t n_grids, size_t n_reactions) + __global__ void AddJacobianTermsKernel(JacobianDevice* device, size_t n_grids, size_t n_reactions) { size_t tid = blockIdx.x * blockDim.x + threadIdx.x; size_t react_ids_offset = 0; size_t yields_offset = 0; size_t flat_id_offset = 0; - size_t* number_of_reactants = device->number_of_reactants; - size_t* jacobian_flat_ids = device->jacobian_flat_ids; - size_t* number_of_products = device->number_of_products; - double* jacobian = device->jacobian; + size_t* number_of_reactants = device->number_of_reactants_; + size_t* jacobian_flat_ids = device->jacobian_flat_ids_; + size_t* number_of_products = device->number_of_products_; + double* jacobian = device->jacobian_; if (tid < n_grids) { @@ -92,12 +91,12 @@ namespace micm // loop over reactants in a reaction for (size_t i_ind = 0; i_ind < number_of_reactants[i_rxn]; ++i_ind) { - double d_rate_d_ind = device->rate_constants[i_rxn * n_grids + tid]; + double d_rate_d_ind = device->rate_constants_[i_rxn * n_grids + tid]; for (size_t i_react = 0; i_react < number_of_reactants[i_rxn]; ++i_react) { if (i_react != i_ind) { - d_rate_d_ind *= device->state_variables[device->reactant_ids[react_ids_offset + i_react] * n_grids + tid]; + d_rate_d_ind *= device->state_variables_[device->reactant_ids_[react_ids_offset + i_react] * n_grids + tid]; } } for (size_t i_dep = 0; i_dep < number_of_reactants[i_rxn]; ++i_dep) @@ -109,7 +108,7 @@ namespace micm for (size_t i_dep = 0; i_dep < number_of_products[i_rxn]; ++i_dep) { size_t jacobian_idx = jacobian_flat_ids[flat_id_offset] + tid; - jacobian[jacobian_idx] += device->yields[yields_offset + i_dep] * d_rate_d_ind; + jacobian[jacobian_idx] += device->yields_[yields_offset + i_dep] * d_rate_d_ind; flat_id_offset++; } } // loop over reactants in a reaction @@ -120,9 +119,9 @@ namespace micm } // end of AddJacobianTerms_kernel std::chrono::nanoseconds AddJacobianTermsKernelDriver( - CUDAMatrixParam& matrixParam, - CUDASparseMatrixParam& sparseMatrix, - CUDAProcessSetParam& processSet) + CudaMatrixParam& matrixParam, + CudaSparseMatrixParam& sparseMatrix, + CudaProcessSetParam& processSet) { // create device pointers double* d_rate_constants; @@ -133,63 +132,63 @@ namespace micm size_t* d_number_of_products; double* d_yields; size_t* d_jacobian_flat_ids; - jacobianDevice* device; + JacobianDevice* device; // allocate device memory - cudaMalloc(&d_rate_constants, sizeof(double) * matrixParam.n_grids * matrixParam.n_reactions); - cudaMalloc(&d_state_variables, sizeof(double) * matrixParam.n_grids * matrixParam.n_species); - cudaMalloc(&d_jacobian, sizeof(double) * sparseMatrix.jacobian_size); - cudaMalloc(&d_number_of_reactants, sizeof(size_t) * matrixParam.n_reactions); - cudaMalloc(&d_reactant_ids, sizeof(size_t) * processSet.reactant_ids_size); - cudaMalloc(&d_number_of_products, sizeof(size_t) * matrixParam.n_reactions); - cudaMalloc(&d_yields, sizeof(double) * processSet.yields_size); - cudaMalloc(&d_jacobian_flat_ids, sizeof(size_t) * processSet.jacobian_flat_ids_size); - cudaMalloc(&device, sizeof(jacobianDevice)); + cudaMalloc(&d_rate_constants, sizeof(double) * matrixParam.n_grids_ * matrixParam.n_reactions_); + cudaMalloc(&d_state_variables, sizeof(double) * matrixParam.n_grids_ * matrixParam.n_species_); + cudaMalloc(&d_jacobian, sizeof(double) * sparseMatrix.jacobian_size_); + cudaMalloc(&d_number_of_reactants, sizeof(size_t) * matrixParam.n_reactions_); + cudaMalloc(&d_reactant_ids, sizeof(size_t) * processSet.reactant_ids_size_); + cudaMalloc(&d_number_of_products, sizeof(size_t) * matrixParam.n_reactions_); + cudaMalloc(&d_yields, sizeof(double) * processSet.yields_size_); + cudaMalloc(&d_jacobian_flat_ids, sizeof(size_t) * processSet.jacobian_flat_ids_size_); + cudaMalloc(&device, sizeof(JacobianDevice)); // transfer data from host to device cudaMemcpy( d_rate_constants, - matrixParam.rate_constants, - sizeof(double) * matrixParam.n_grids * matrixParam.n_reactions, + matrixParam.rate_constants_, + sizeof(double) * matrixParam.n_grids_ * matrixParam.n_reactions_, cudaMemcpyHostToDevice); cudaMemcpy( d_state_variables, - matrixParam.state_variables, - sizeof(double) * matrixParam.n_grids * matrixParam.n_species, + matrixParam.state_variables_, + sizeof(double) * matrixParam.n_grids_ * matrixParam.n_species_, cudaMemcpyHostToDevice); - cudaMemcpy(d_jacobian, sparseMatrix.jacobian, sizeof(double) * sparseMatrix.jacobian_size, cudaMemcpyHostToDevice); + cudaMemcpy(d_jacobian, sparseMatrix.jacobian_, sizeof(double) * sparseMatrix.jacobian_size_, cudaMemcpyHostToDevice); cudaMemcpy( d_number_of_reactants, - processSet.number_of_reactants, - sizeof(size_t) * matrixParam.n_reactions, + processSet.number_of_reactants_, + sizeof(size_t) * matrixParam.n_reactions_, cudaMemcpyHostToDevice); cudaMemcpy( - d_reactant_ids, processSet.reactant_ids, sizeof(size_t) * processSet.reactant_ids_size, cudaMemcpyHostToDevice); + d_reactant_ids, processSet.reactant_ids_, sizeof(size_t) * processSet.reactant_ids_size_, cudaMemcpyHostToDevice); cudaMemcpy( d_number_of_products, - processSet.number_of_products, - sizeof(size_t) * matrixParam.n_reactions, + processSet.number_of_products_, + sizeof(size_t) * matrixParam.n_reactions_, cudaMemcpyHostToDevice); - cudaMemcpy(d_yields, processSet.yields, sizeof(double) * processSet.yields_size, cudaMemcpyHostToDevice); + cudaMemcpy(d_yields, processSet.yields_, sizeof(double) * processSet.yields_size_, cudaMemcpyHostToDevice); cudaMemcpy( d_jacobian_flat_ids, - processSet.jacobian_flat_ids, - sizeof(size_t) * processSet.jacobian_flat_ids_size, + processSet.jacobian_flat_ids_, + sizeof(size_t) * processSet.jacobian_flat_ids_size_, cudaMemcpyHostToDevice); - cudaMemcpy(&(device->rate_constants), &d_rate_constants, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->state_variables), &d_state_variables, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->jacobian), &d_jacobian, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->number_of_reactants), &d_number_of_reactants, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->reactant_ids), &d_reactant_ids, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->number_of_products), &d_number_of_products, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->yields), &d_yields, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->jacobian_flat_ids), &d_jacobian_flat_ids, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->rate_constants_), &d_rate_constants, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->state_variables_), &d_state_variables, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->jacobian_), &d_jacobian, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->number_of_reactants_), &d_number_of_reactants, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->reactant_ids_), &d_reactant_ids, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->number_of_products_), &d_number_of_products, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->yields_), &d_yields, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->jacobian_flat_ids_), &d_jacobian_flat_ids, sizeof(size_t*), cudaMemcpyHostToDevice); // setup kernel - size_t total_blocks = (matrixParam.n_grids + BLOCK_SIZE - 1) / BLOCK_SIZE; + size_t total_blocks = (matrixParam.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - size_t n_reactions = matrixParam.n_reactions; - size_t n_grids = matrixParam.n_grids; + size_t n_reactions = matrixParam.n_reactions_; + size_t n_grids = matrixParam.n_grids_; // launch kernel and measure time performance auto startTime = std::chrono::high_resolution_clock::now(); AddJacobianTermsKernel<<>>(device, n_grids, n_reactions); @@ -197,7 +196,7 @@ namespace micm auto endTime = std::chrono::high_resolution_clock::now(); auto kernel_duration = std::chrono::duration_cast(endTime - startTime); - cudaMemcpy(sparseMatrix.jacobian, d_jacobian, sizeof(double) * sparseMatrix.jacobian_size, cudaMemcpyDeviceToHost); + cudaMemcpy(sparseMatrix.jacobian_, d_jacobian, sizeof(double) * sparseMatrix.jacobian_size_, cudaMemcpyDeviceToHost); // clean up cudaFree(d_rate_constants); cudaFree(d_state_variables); @@ -211,7 +210,7 @@ namespace micm return kernel_duration; } // end of AddJacobian_kernelSetup - std::chrono::nanoseconds AddForcingTermsKernelDriver(CUDAMatrixParam& matrixParam, CUDAProcessSetParam& processSet) + std::chrono::nanoseconds AddForcingTermsKernelDriver(CudaMatrixParam& matrixParam, CudaProcessSetParam& processSet) { // device pointer to vectorss double* d_rate_constants; @@ -222,66 +221,66 @@ namespace micm size_t* d_reactant_ids; size_t* d_number_of_products; size_t* d_product_ids; - forcingDevice* device; + ForcingDevice* device; // allocate device memory - cudaMalloc(&d_rate_constants, sizeof(double) * (matrixParam.n_grids * matrixParam.n_reactions)); - cudaMalloc(&d_state_variables, sizeof(double) * (matrixParam.n_grids * matrixParam.n_species)); - cudaMalloc(&d_forcing, sizeof(double) * (matrixParam.n_grids * matrixParam.n_species)); - cudaMalloc(&d_number_of_reactants, sizeof(size_t) * matrixParam.n_reactions); - cudaMalloc(&d_reactant_ids, sizeof(size_t) * processSet.reactant_ids_size); - cudaMalloc(&d_number_of_products, sizeof(size_t) * matrixParam.n_reactions); - cudaMalloc(&d_product_ids, sizeof(size_t) * processSet.product_ids_size); - cudaMalloc(&d_yields, sizeof(double) * processSet.yields_size); - cudaMalloc(&device, sizeof(forcingDevice)); + cudaMalloc(&d_rate_constants, sizeof(double) * (matrixParam.n_grids_ * matrixParam.n_reactions_)); + cudaMalloc(&d_state_variables, sizeof(double) * (matrixParam.n_grids_ * matrixParam.n_species_)); + cudaMalloc(&d_forcing, sizeof(double) * (matrixParam.n_grids_ * matrixParam.n_species_)); + cudaMalloc(&d_number_of_reactants, sizeof(size_t) * matrixParam.n_reactions_); + cudaMalloc(&d_reactant_ids, sizeof(size_t) * processSet.reactant_ids_size_); + cudaMalloc(&d_number_of_products, sizeof(size_t) * matrixParam.n_reactions_); + cudaMalloc(&d_product_ids, sizeof(size_t) * processSet.product_ids_size_); + cudaMalloc(&d_yields, sizeof(double) * processSet.yields_size_); + cudaMalloc(&device, sizeof(ForcingDevice)); // copy data from host memory to device memory cudaMemcpy( d_rate_constants, - matrixParam.rate_constants, - sizeof(double) * (matrixParam.n_grids * matrixParam.n_reactions), + matrixParam.rate_constants_, + sizeof(double) * (matrixParam.n_grids_ * matrixParam.n_reactions_), cudaMemcpyHostToDevice); cudaMemcpy( d_state_variables, - matrixParam.state_variables, - sizeof(double) * (matrixParam.n_grids * matrixParam.n_species), + matrixParam.state_variables_, + sizeof(double) * (matrixParam.n_grids_ * matrixParam.n_species_), cudaMemcpyHostToDevice); cudaMemcpy( d_forcing, - matrixParam.forcing, - sizeof(double) * (matrixParam.n_grids * matrixParam.n_species), + matrixParam.forcing_, + sizeof(double) * (matrixParam.n_grids_ * matrixParam.n_species_), cudaMemcpyHostToDevice); cudaMemcpy( d_number_of_reactants, - processSet.number_of_reactants, - sizeof(size_t) * matrixParam.n_reactions, + processSet.number_of_reactants_, + sizeof(size_t) * matrixParam.n_reactions_, cudaMemcpyHostToDevice); cudaMemcpy( - d_reactant_ids, processSet.reactant_ids, sizeof(size_t) * processSet.reactant_ids_size, cudaMemcpyHostToDevice); + d_reactant_ids, processSet.reactant_ids_, sizeof(size_t) * processSet.reactant_ids_size_, cudaMemcpyHostToDevice); cudaMemcpy( d_number_of_products, - processSet.number_of_products, - sizeof(size_t) * matrixParam.n_reactions, + processSet.number_of_products_, + sizeof(size_t) * matrixParam.n_reactions_, cudaMemcpyHostToDevice); cudaMemcpy( - d_product_ids, processSet.product_ids, sizeof(size_t) * processSet.product_ids_size, cudaMemcpyHostToDevice); - cudaMemcpy(d_yields, processSet.yields, sizeof(double) * processSet.yields_size, cudaMemcpyHostToDevice); - cudaMemcpy(&(device->rate_constants), &d_rate_constants, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->state_variables), &d_state_variables, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->forcing), &d_forcing, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->number_of_reactants), &d_number_of_reactants, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->reactant_ids), &d_reactant_ids, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->number_of_products), &d_number_of_products, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->product_ids), &d_product_ids, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->yields), &d_yields, sizeof(double*), cudaMemcpyHostToDevice); + d_product_ids, processSet.product_ids_, sizeof(size_t) * processSet.product_ids_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_yields, processSet.yields_, sizeof(double) * processSet.yields_size_, cudaMemcpyHostToDevice); + cudaMemcpy(&(device->rate_constants_), &d_rate_constants, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->state_variables_), &d_state_variables, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->forcing_), &d_forcing, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->number_of_reactants_), &d_number_of_reactants, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->reactant_ids_), &d_reactant_ids, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->number_of_products_), &d_number_of_products, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->product_ids_), &d_product_ids, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->yields_), &d_yields, sizeof(double*), cudaMemcpyHostToDevice); // total thread count == number of grid cells - int num_block = (matrixParam.n_grids + BLOCK_SIZE - 1) / BLOCK_SIZE; + int num_block = (matrixParam.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - size_t n_grids = matrixParam.n_grids; - size_t n_reactions = matrixParam.n_reactions; - size_t n_species = matrixParam.n_species; + size_t n_grids = matrixParam.n_grids_; + size_t n_reactions = matrixParam.n_reactions_; + size_t n_species = matrixParam.n_species_; // launch kernel and measure time performance auto startTime = std::chrono::high_resolution_clock::now(); @@ -291,7 +290,7 @@ namespace micm auto kernel_duration = std::chrono::duration_cast(endTime - startTime); // copy data from device memory to host memory - cudaMemcpy(matrixParam.forcing, d_forcing, sizeof(double) * (n_grids * n_species), cudaMemcpyDeviceToHost); + cudaMemcpy(matrixParam.forcing_, d_forcing, sizeof(double) * (n_grids * n_species), cudaMemcpyDeviceToHost); // clean up cudaFree(d_rate_constants); @@ -306,4 +305,4 @@ namespace micm return kernel_duration; } // end of AddForcingTerms_kernelSetup } // namespace cuda -} // namespace micm +} // namespace micm \ No newline at end of file diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt new file mode 100644 index 000000000..caf73c9da --- /dev/null +++ b/src/solver/CMakeLists.txt @@ -0,0 +1,6 @@ +if(ENABLE_CUDA) + target_sources(micm_cuda + PRIVATE + lu_decomposition.cu + ) +endif() \ No newline at end of file diff --git a/src/solver/lu_decomposition.cu b/src/solver/lu_decomposition.cu new file mode 100644 index 000000000..4d9a4b43b --- /dev/null +++ b/src/solver/lu_decomposition.cu @@ -0,0 +1,184 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include +#include + +//grouped parameters passing to DecomposeKernel() +struct DecomposeDevice{ + double* A_; + double* L_; + double* U_; + char* do_aik_; + size_t* aik_; + char* do_aki_; + size_t* aki_; + size_t* uii_; + std::pair* niLU_; + std::pair* uik_nkj_; + std::pair* lij_ujk_; + std::pair* lki_nkj_; + std::pair* lkj_uji_; + size_t n_grids_; + size_t niLU_size_; +}; +namespace micm{ + namespace cuda{ + __global__ void DecomposeKernel(DecomposeDevice* device) + { + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + double* A = device->A_; + double* L = device->L_; + double* U = device->U_; + std::pair* lkj_uji = device->lkj_uji_; + std::pair* uik_nkj = device->uik_nkj_; + std::pair* lij_ujk = device->lij_ujk_; + std::pair* lki_nkj = device->lki_nkj_; + size_t do_aik_offset = 0; //boolean vector + size_t aik_offset = 0; + size_t uik_nkj_offset = 0; + size_t lij_ujk_offset = 0; + size_t do_aki_offset = 0; //boolean vector + size_t aki_offset = 0; + size_t lki_nkj_offset = 0; + size_t lkj_uji_offset = 0; + size_t uii_offset = 0; + + if (tid < device->n_grids_){ + //loop through every element in niLU + for (size_t i = 0; i < device->niLU_size_; i++){ + //upper triangular matrix + auto inLU = device->niLU_[i]; + for (size_t iU = 0; iU < inLU.second; ++iU){ + if(device->do_aik_[do_aik_offset++]){ + size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; + size_t A_idx = device->aik_[aik_offset++]+ tid; + U[U_idx] = A[A_idx]; + } + + for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj){ + size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; + size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; + size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; + U[U_idx_1] -= L[L_idx] * U[U_idx_2]; + ++lij_ujk_offset; + } + ++uik_nkj_offset; + } + // lower triangular matrix + + L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; + + for (size_t iL = 0; iL do_aki_[do_aki_offset++]){ + size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; + size_t A_idx = device->aki_[aki_offset++] + tid; + L[L_idx] = A[A_idx]; + } + for(size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second;++ikj){ + size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; + size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; + size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; + L[L_idx_1] -= L[L_idx_2] * U[U_idx]; + ++lkj_uji_offset; + } + L[lki_nkj[lki_nkj_offset].first + tid]/=U[device->uii_[uii_offset] + tid]; + ++lki_nkj_offset; + ++uii_offset; + } + } + } + }// end of kernel + + std::chrono::nanoseconds DecomposeKernelDriver( + CudaSparseMatrixParam& sparseMatrix, + CudaSolverParam& solver){ + //create device pointers and allocate device memory + double* d_A; + double* d_L; + double* d_U; + bool* d_do_aik; + size_t* d_aik; + bool* d_do_aki; + size_t* d_aki; + size_t* d_uii; + std::pair* d_niLU; + std::pair* d_uik_nkj; + std::pair* d_lij_ujk; + std::pair* d_lki_nkj; + std::pair* d_lkj_uji; + DecomposeDevice* device; + + cudaMalloc(&d_A,sizeof(double)* sparseMatrix.A_size_); + cudaMalloc(&d_L,sizeof(double)* sparseMatrix.L_size_); + cudaMalloc(&d_U,sizeof(double)* sparseMatrix.U_size_); + cudaMalloc(&d_do_aik,sizeof(char)* solver.do_aik_size_); + cudaMalloc(&d_aik,sizeof(size_t)* solver.aik_size_); + cudaMalloc(&d_do_aki,sizeof(char)* solver.do_aki_size_); + cudaMalloc(&d_aki,sizeof(size_t)* solver.aki_size_); + cudaMalloc(&d_uii,sizeof(size_t)* solver.uii_size_); + cudaMalloc(&d_niLU,sizeof(std::pair)* solver.niLU_size_); + cudaMalloc(&d_uik_nkj,sizeof(std::pair)* solver.uik_nkj_size_); + cudaMalloc(&d_lij_ujk,sizeof(std::pair)* solver.lij_ujk_size_); + cudaMalloc(&d_lki_nkj,sizeof(std::pair)* solver.lki_nkj_size_); + cudaMalloc(&d_lkj_uji,sizeof(std::pair)* solver.lkj_uji_size_); + cudaMalloc(&device, sizeof(DecomposeDevice)); + + //transfer data from host to device + cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double)* sparseMatrix.A_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char)* solver.do_aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aik, solver.aik_, sizeof(size_t)* solver.aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char)* solver.do_aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aki, solver.aki_, sizeof(size_t)*solver.aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_uii, solver.uii_, sizeof(size_t)* solver.uii_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair)*solver.niLU_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_uik_nkj, solver.uik_nkj_, sizeof(std::pair)*solver.uik_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_lij_ujk, solver.lij_ujk_, sizeof(std::pair)*solver.lij_ujk_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_lki_nkj, solver.lki_nkj_, sizeof(std::pair)*solver.lki_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_lkj_uji, solver.lkj_uji_, sizeof(std::pair)*solver.lkj_uji_size_, cudaMemcpyHostToDevice); + cudaMemcpy(&(device->A_),&d_A, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->L_),&d_L, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->U_),&d_U, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aki_),&d_do_aki,sizeof(char*),cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aki_),&d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); + + //total number of threads is number of blocks in sparseMatrix A + size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + device->n_grids_ = sparseMatrix.n_grids_; + device->niLU_size_ = solver.niLU_size_; + + // call kernel + auto startTime = std::chrono::high_resolution_clock::now(); + DecomposeKernel<<>>(device); + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyDeviceToHost); + cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyDeviceToHost); + + //clean up + cudaFree(d_A); + cudaFree(d_L); + cudaFree(d_U); + cudaFree(d_do_aik); + cudaFree(d_aik); + cudaFree(d_do_aki); + cudaFree(d_aki); + cudaFree(d_uii); + cudaFree(device); + return kernel_duration; + }//end kernelDriver + }//end cuda +}//end micm \ No newline at end of file diff --git a/test/unit/process/test_cuda_process_set.cpp b/test/unit/process/test_cuda_process_set.cpp index 2fbdc9bd2..8448c7c06 100644 --- a/test/unit/process/test_cuda_process_set.cpp +++ b/test/unit/process/test_cuda_process_set.cpp @@ -1,5 +1,4 @@ #include - #include #include #include @@ -179,5 +178,5 @@ TEST(RandomCudaProcessSet, Forcing) } TEST(RandomCudaProcessSet, Jacobian) { - testRandomSystemAddJacobianTerms(10000, 500, 400); + testRandomSystemAddJacobianTerms(10000,500,400); } diff --git a/test/unit/solver/CMakeLists.txt b/test/unit/solver/CMakeLists.txt index 408214caf..c45f12645 100644 --- a/test/unit/solver/CMakeLists.txt +++ b/test/unit/solver/CMakeLists.txt @@ -12,6 +12,11 @@ create_standard_test(NAME lu_decomposition SOURCES test_lu_decomposition.cpp) create_standard_test(NAME rosenbrock SOURCES test_rosenbrock.cpp) create_standard_test(NAME state SOURCES test_state.cpp) +# GPU tests +if(ENABLE_CUDA) + create_standard_test(NAME cuda_lu_decomposition SOURCES test_cuda_lu_decomposition.cpp LIBRARIES musica::micm_cuda) +endif() + if(ENABLE_LLVM) create_standard_test(NAME jit_linear_solver SOURCES test_jit_linear_solver.cpp) create_standard_test(NAME jit_lu_decomposition SOURCES test_jit_lu_decomposition.cpp) diff --git a/test/unit/solver/test_cuda_lu_decomposition.cpp b/test/unit/solver/test_cuda_lu_decomposition.cpp new file mode 100644 index 000000000..fa1cf4ed7 --- /dev/null +++ b/test/unit/solver/test_cuda_lu_decomposition.cpp @@ -0,0 +1,121 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +template class SparseMatrixPolicy> +void check_results( + const SparseMatrixPolicy& A, + const SparseMatrixPolicy& L, + const SparseMatrixPolicy& U, + const std::function f) +{ + EXPECT_EQ(A.size(), L.size()); + EXPECT_EQ(A.size(), U.size()); + for (std::size_t i_block = 0; i_block < A.size(); ++i_block) + { + for (std::size_t i = 0; i < A[i_block].size(); ++i) + { + for (std::size_t j = 0; j < A[i_block].size(); ++j) + { + T result{}; + for (std::size_t k = 0; k < A[i_block].size(); ++k) + { + if (!(L.IsZero(i, k) || U.IsZero(k, j))) + { + result += L[i_block][i][k] * U[i_block][k][j]; + } + } + // Make sure these are actually triangular matrices + EXPECT_TRUE(i >= j || L.IsZero(i, j)); + EXPECT_TRUE(j >= i || U.IsZero(i, j)); + if (A.IsZero(i, j)) + { + f(result, T{}); + } + else + { + f(result, A[i_block][i][j]); + } + } + } + } +} + +template class SparseMatrixPolicy> +void gpu_validation( + const SparseMatrixPolicy& gpu_L, + const SparseMatrixPolicy& cpu_L, + const SparseMatrixPolicy& gpu_U, + const SparseMatrixPolicy& cpu_U) +{ + size_t L_size = cpu_L.AsVector().size(); + size_t U_size = cpu_U.AsVector().size(); + std::vector gpu_L_vector = gpu_L.AsVector(); + std::vector cpu_L_vector = cpu_L.AsVector(); + std::vector gpu_U_vector = gpu_U.AsVector(); + std::vector cpu_U_vector = cpu_U.AsVector(); + for (int i = 0; i < L_size; i++){ + EXPECT_EQ(gpu_L_vector[i], cpu_L_vector[i]); + }; + for (int j = 0; j < U_size; j++){ + EXPECT_EQ(gpu_U_vector[j], cpu_U_vector[j]); + }; +} + +template class SparseMatrixPolicy> +void testRandomMatrix(size_t n_grids) +{ + auto gen_bool = std::bind(std::uniform_int_distribution<>(0, 1), std::default_random_engine()); + auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); + + auto builder = SparseMatrixPolicy::create(10).number_of_blocks(n_grids).initial_value(1.0e-30); + for (std::size_t i = 0; i < 10; ++i) + for (std::size_t j = 0; j < 10; ++j) + if (i == j || gen_bool()) + builder = builder.with_element(i, j); + + SparseMatrixPolicy A(builder); + + for (std::size_t i = 0; i < 10; ++i) + for (std::size_t j = 0; j < 10; ++j) + if (!A.IsZero(i, j)) + for (std::size_t i_block = 0; i_block < n_grids; ++i_block) + A[i_block][i][j] = get_double(); + + micm::LuDecomposition gpu_lud(A); + auto gpu_LU = micm::CudaLuDecomposition::GetLUMatrices(A, 1.0e-30); + gpu_lud.Decompose(A, gpu_LU.first, gpu_LU.second); + check_results( + A, gpu_LU.first, gpu_LU.second, [&](const double a, const double b) -> void {EXPECT_NEAR(a, b, 1.0e-5); }); + + micm::LuDecomposition cpu_lud(A); + auto cpu_LU = micm::LuDecomposition::GetLUMatrices(A, 1.0e-30); + cpu_lud.Decompose(A, cpu_LU.first, cpu_LU.second); + + //checking GPU result again CPU + gpu_validation(gpu_LU.first, cpu_LU.first, gpu_LU.second, cpu_LU.second); +} + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; + +TEST(CudaLuDecomposition, RandomMatrixVectorOrdering) +{ + testRandomMatrix(10); + testRandomMatrix(100); + testRandomMatrix(1000); + testRandomMatrix(100000); +} \ No newline at end of file From c9b0f611e7ed4e31b21dc00cabd0de6ea8fe6df4 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 14:48:25 -0500 Subject: [PATCH 087/318] removing stat no longer tracked --- test/tutorial/test_solver_configuration.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp index 533a7eb64..7f208005b 100644 --- a/test/tutorial/test_solver_configuration.cpp +++ b/test/tutorial/test_solver_configuration.cpp @@ -94,7 +94,6 @@ void test_solver_type(T solver) total_stats.decompositions += result.stats_.decompositions; total_stats.solves += result.stats_.solves; total_stats.singular += result.stats_.singular; - total_stats.total_steps += result.stats_.total_steps; total_stats.total_forcing_time += result.stats_.total_forcing_time; total_stats.total_jacobian_time += result.stats_.total_jacobian_time; total_stats.total_linear_factor_time += result.stats_.total_linear_factor_time; From 53b06ef68795a81d74c9e3bd382e9898f46da77e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 4 Oct 2023 15:01:07 -0500 Subject: [PATCH 088/318] only running docker build and test for pushes, the rest run on pull requests only --- .github/workflows/test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 015e00bfc..237890f32 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,7 +50,7 @@ jobs: files: coverage.info multiplatform: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + if: github.event_name == 'pull_request' runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -106,7 +106,7 @@ jobs: ctest -C Debug --rerun-failed --output-on-failure . --verbose xcode_1: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + if: github.event_name == 'pull_request' runs-on: macos-12 strategy: matrix: @@ -130,7 +130,7 @@ jobs: ctest --rerun-failed --output-on-failure . --verbose -j 10 xcode_2: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + if: github.event_name == 'pull_request' runs-on: macos-13 strategy: matrix: From 02966b61b32dd8f90597fa0507a05a1d52acc127 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 4 Oct 2023 13:30:19 -0700 Subject: [PATCH 089/318] fix template default arg --- include/micm/solver/rosenbrock.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index e8c6650db..b692d1151 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -152,7 +152,7 @@ namespace micm /// The template parameter is the type of matrix to use template< template class MatrixPolicy = Matrix, - template class SparseMatrixPolicy = SparseMatrix, + template class SparseMatrixPolicy = StandardSparseMatrix, class LinearSolverPolicy = LinearSolver> class RosenbrockSolver { From 85e325f5e269fdc9fae1c470ca839287e1f848c6 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 5 Oct 2023 14:11:55 -0500 Subject: [PATCH 090/318] disabling clang tidy for gtest in a better way --- cmake/dependencies.cmake | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 423ca7c69..75cd0d00c 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -74,17 +74,9 @@ endif() # google test if(PROJECT_IS_TOP_LEVEL) - # if google test isn't installed, fetch content will download and build what is needed - # but, we don't want to run clang tidy on google test, save those variables and reset them later - foreach (lang IN ITEMS C CXX) - set("CMAKE_${lang}_CLANG_TIDY_save" "${CMAKE_${lang}_CLANG_TIDY}") - set("CMAKE_${lang}_CLANG_TIDY" "") - endforeach () - FetchContent_Declare(googletest GIT_REPOSITORY https://github.com/google/googletest.git GIT_TAG be03d00f5f0cc3a997d1a368bee8a1fe93651f48 - # FIND_PACKAGE_ARGS GTest ) set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) @@ -92,9 +84,11 @@ if(PROJECT_IS_TOP_LEVEL) FetchContent_MakeAvailable(googletest) - foreach (lang IN ITEMS C CXX) - set("CMAKE_${lang}_CLANG_TIDY" "${CMAKE_${lang}_CLANG_TIDY_save}") - endforeach () + # don't run clang-tidy on google test + set_target_properties(gtest PROPERTIES CXX_CLANG_TIDY "") + set_target_properties(gtest_main PROPERTIES CXX_CLANG_TIDY "") + # set_target_properties(gmock PROPERTIES CXX_CLANG_TIDY "") + # set_target_properties(gmock_main PROPERTIES CXX_CLANG_TIDY "") endif() ################################################################################ From 8bfcaa52ffd5155167a864b7d283a32269805e44 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 5 Oct 2023 17:46:50 -0500 Subject: [PATCH 091/318] removing documentation dependencies from readme (#288) --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 25b4beb45..aac60f036 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,6 @@ Copyright (C) 2018-2023 National Center for Atmospheric Research ## Installing MICM locally To build and install MICM locally, you must have CMake installed on your machine. -If you plan to build the documentation, you must also have: - -- [sphinx](https://github.com/sphinx-doc/sphinx) -- [sphinx-book-theme](https://github.com/executablebooks/sphinx-book-theme) -- [sphinx-design](https://github.com/executablebooks/sphinx-design) -- [breathe](https://github.com/breathe-doc/breathe) - Open a terminal window, navigate to a folder where you would like the MICM files to exist, and run the following commands: From d3a494e4610d89fe72ddb6193ffcd1524c294c0f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 5 Oct 2023 17:47:54 -0500 Subject: [PATCH 092/318] putting docker files into their own folder (#289) --- .github/workflows/gh_pages.yml | 6 +++--- .github/workflows/test.yml | 2 +- README.md | 2 +- Dockerfile => docker/Dockerfile | 0 Dockerfile.coverage => docker/Dockerfile.coverage | 0 Dockerfile.docs => docker/Dockerfile.docs | 0 Dockerfile.intel => docker/Dockerfile.intel | 0 Dockerfile.llvm => docker/Dockerfile.llvm | 0 Dockerfile.memcheck => docker/Dockerfile.memcheck | 0 Dockerfile.mpi => docker/Dockerfile.mpi | 0 Dockerfile.no_json => docker/Dockerfile.no_json | 0 Dockerfile.nvhpc => docker/Dockerfile.nvhpc | 0 Dockerfile.openmp => docker/Dockerfile.openmp | 0 docs/source/getting_started.rst | 2 +- 14 files changed, 6 insertions(+), 6 deletions(-) rename Dockerfile => docker/Dockerfile (100%) rename Dockerfile.coverage => docker/Dockerfile.coverage (100%) rename Dockerfile.docs => docker/Dockerfile.docs (100%) rename Dockerfile.intel => docker/Dockerfile.intel (100%) rename Dockerfile.llvm => docker/Dockerfile.llvm (100%) rename Dockerfile.memcheck => docker/Dockerfile.memcheck (100%) rename Dockerfile.mpi => docker/Dockerfile.mpi (100%) rename Dockerfile.no_json => docker/Dockerfile.no_json (100%) rename Dockerfile.nvhpc => docker/Dockerfile.nvhpc (100%) rename Dockerfile.openmp => docker/Dockerfile.openmp (100%) diff --git a/.github/workflows/gh_pages.yml b/.github/workflows/gh_pages.yml index d32d350dc..4fa77e084 100644 --- a/.github/workflows/gh_pages.yml +++ b/.github/workflows/gh_pages.yml @@ -53,7 +53,7 @@ jobs: # create two copies of the documentaiton # 1. the frozen version, represented as vX.X in the version switcher - docker build -t micm -f Dockerfile.docs . + docker build -t micm -f docker/Dockerfile.docs . id=$(docker create micm) docker cp $id:/build/docs/sphinx tmpdocs docker rm -v $id @@ -62,7 +62,7 @@ jobs: # 2. stable, represented as vX.X (stable) in the version switcher # edit conf.py to produce a version string that looks like vX.X (stable) - docker build -t micm -f Dockerfile.docs --build-arg SUFFIX=" (stable)" . + docker build -t micm -f docker/Dockerfile.docs --build-arg SUFFIX=" (stable)" . id=$(docker create micm) docker cp $id:/build/docs/sphinx tmpdocs docker rm -v $id @@ -83,7 +83,7 @@ jobs: !contains(github.ref, env.DEFAULT_BRANCH) run: | set -x - docker build -t micm -f Dockerfile.docs . + docker build -t micm -f docker/Dockerfile.docs . id=$(docker create micm) docker cp $id:/build/docs/sphinx tmpdocs docker rm -v $id diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 237890f32..6196422ef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: submodules: recursive - name: Build Docker image - run: docker build -t micm -f ${{ matrix.dockerfile }} . + run: docker build -t micm -f docker/${{ matrix.dockerfile }} . - name: Run tests in container if: matrix.dockerfile != 'Dockerfile.coverage' diff --git a/README.md b/README.md index aac60f036..ba1895a51 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ To build the latest pre-release version of MICM, instead run: ``` git clone https://github.com/NCAR/micm.git cd micm -docker build -t micm . +docker build -t micm -f docker/Dockerfile . docker run -it micm bash ``` diff --git a/Dockerfile b/docker/Dockerfile similarity index 100% rename from Dockerfile rename to docker/Dockerfile diff --git a/Dockerfile.coverage b/docker/Dockerfile.coverage similarity index 100% rename from Dockerfile.coverage rename to docker/Dockerfile.coverage diff --git a/Dockerfile.docs b/docker/Dockerfile.docs similarity index 100% rename from Dockerfile.docs rename to docker/Dockerfile.docs diff --git a/Dockerfile.intel b/docker/Dockerfile.intel similarity index 100% rename from Dockerfile.intel rename to docker/Dockerfile.intel diff --git a/Dockerfile.llvm b/docker/Dockerfile.llvm similarity index 100% rename from Dockerfile.llvm rename to docker/Dockerfile.llvm diff --git a/Dockerfile.memcheck b/docker/Dockerfile.memcheck similarity index 100% rename from Dockerfile.memcheck rename to docker/Dockerfile.memcheck diff --git a/Dockerfile.mpi b/docker/Dockerfile.mpi similarity index 100% rename from Dockerfile.mpi rename to docker/Dockerfile.mpi diff --git a/Dockerfile.no_json b/docker/Dockerfile.no_json similarity index 100% rename from Dockerfile.no_json rename to docker/Dockerfile.no_json diff --git a/Dockerfile.nvhpc b/docker/Dockerfile.nvhpc similarity index 100% rename from Dockerfile.nvhpc rename to docker/Dockerfile.nvhpc diff --git a/Dockerfile.openmp b/docker/Dockerfile.openmp similarity index 100% rename from Dockerfile.openmp rename to docker/Dockerfile.openmp diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 8ffb89280..aaebdcfbf 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -47,7 +47,7 @@ Docker Container Build and run the image:: - $ docker build -t micm -f Dockerfile.nvhpc . + $ docker build -t micm -f docker/Dockerfile.nvhpc . $ docker run --rm -it micm If you would like, you can ssh into a running docker container and edit the files there. From d1f27f319e0a21f732765aa327ac6300d01a68b8 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 5 Oct 2023 17:51:59 -0500 Subject: [PATCH 093/318] Switcher again (#291) * copying switcher even for branch builds * trying to correct versions * trying to fix switcher * trying to fix the switcher * switching tab system * putting back branch build --- .github/workflows/gh_pages.yml | 6 ++++-- docs/requirements.txt | 1 - docs/source/_static/switcher.json | 17 +++++++++++++++++ docs/source/conf.py | 9 ++++----- docs/source/user_guide/but_how_fast_is_it.rst | 4 ++-- docs/source/user_guide/multiple_grid_cells.rst | 4 ++-- docs/source/user_guide/openmp.rst | 4 ++-- .../user_guide/rate_constant_tutorial.rst | 18 +++++++++--------- .../user_guide/solver_configurations.rst | 4 ++-- .../user_defined_rate_constant_tutorial.rst | 18 +++++++++--------- docs/switcher.json | 12 ------------ 11 files changed, 51 insertions(+), 46 deletions(-) create mode 100644 docs/source/_static/switcher.json delete mode 100644 docs/switcher.json diff --git a/.github/workflows/gh_pages.yml b/.github/workflows/gh_pages.yml index 4fa77e084..06f69afd5 100644 --- a/.github/workflows/gh_pages.yml +++ b/.github/workflows/gh_pages.yml @@ -51,7 +51,7 @@ jobs: set -x mkdir -p _build/html/versions - # create two copies of the documentaiton + # create two copies of the documentation # 1. the frozen version, represented as vX.X in the version switcher docker build -t micm -f docker/Dockerfile.docs . id=$(docker create micm) @@ -73,7 +73,9 @@ jobs: # _gh-pages itself. find _gh-pages/ -mindepth 1 ! -path '_gh-pages/branch*' ! -path '_gh-pages/versions*' -delete rsync -a _build/html/versions/stable/* _gh-pages/ - mv docs/switcher.json _gh-pages + mkdir -p _gh-pages/versions + rsync -a _build/html/versions/* _gh-pages/versions + # mv docs/switcher.json _gh-pages # If a push and not on default branch, then copy the build to # _gh-pages/branch/$brname (transforming '/' into '--') diff --git a/docs/requirements.txt b/docs/requirements.txt index 54661a4bb..321eb0b5f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -3,5 +3,4 @@ sphinx sphinx-book-theme sphinx-copybutton sphinx-design -sphinx-tabs sphinxcontrib-bibtex \ No newline at end of file diff --git a/docs/source/_static/switcher.json b/docs/source/_static/switcher.json new file mode 100644 index 000000000..290f60623 --- /dev/null +++ b/docs/source/_static/switcher.json @@ -0,0 +1,17 @@ +[ + { + "name": "v3.2.0 (stable)", + "version": "v3.2.0 (stable)", + "url": "https://ncar.github.io/micm" + }, + { + "name": "v3.2.0", + "version": "3.2.0", + "url": "https://ncar.github.io/micm/versions/3.2.0" + }, + { + "name": "dev", + "version": "dev", + "url": "https://ncar.github.io/micm/branch/main/" + } +] \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index b6bd108b6..5c753c8c5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,9 +21,9 @@ copyright = f'2022-{datetime.datetime.now().year}, NCAR/UCAR' author = 'NCAR/UCAR' -# The full version, including alpha/beta/rc tags -release = '3.2.0' - +suffix = os.getenv("SWITCHER_SUFFIX", "") +# the suffix is required. This is controlled by the dockerfile that builds the docs +release = f'v3.2.0{suffix}' # -- General configuration --------------------------------------------------- @@ -34,7 +34,6 @@ 'breathe', 'sphinx_copybutton', 'sphinx_design', - 'sphinx_tabs.tabs', 'sphinxcontrib.bibtex', ] @@ -64,7 +63,7 @@ "github_url": "https://github.com/NCAR/micm", "navbar_end": ["version-switcher", "navbar-icon-links"], "switcher": { - "json_url": "https://ncar.github.io/micm/switcher.json", + "json_url": "https://ncar.github.io/micm/_static/switcher.json", "version_match": release, }, } diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst index ee3e41dc1..de67d03c0 100644 --- a/docs/source/user_guide/but_how_fast_is_it.rst +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -18,9 +18,9 @@ the solver. If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp :language: cpp diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index de01b7e04..245911f4c 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -20,9 +20,9 @@ The third grid cell will have concentrations half as large as the first grid cel If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp diff --git a/docs/source/user_guide/openmp.rst b/docs/source/user_guide/openmp.rst index fe20c65d3..4d620d655 100644 --- a/docs/source/user_guide/openmp.rst +++ b/docs/source/user_guide/openmp.rst @@ -14,9 +14,9 @@ We will use a simple 3-reaction 3-species mechanism. The setup here is the same If you're looking for a copy and paste, copy below and be on your way! Otherwise, stick around for a line by line explanation. -.. tabs:: +.. tab-set:: - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading .. raw:: html diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 2e99d6ffa..3ac2f6624 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -41,14 +41,14 @@ If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading .. raw:: html @@ -81,9 +81,9 @@ rosenbrock solver. To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these. -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API To do this by hand, we have to define all of the chemical species in the system. This allows us to set any properties of the species that may be necessary for rate constanta calculations, like molecular weights @@ -108,7 +108,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will :language: cpp :lines: 135-136 - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading After defining a valid OpenAtmos configuration with reactions that ``micm`` supports, configuring the chemical system and the processes is as simple as using the :cpp:class:`micm::SolverConfig` class @@ -133,15 +133,15 @@ custom rate parameters, and temperature and pressure Initializing the state ^^^^^^^^^^^^^^^^^^^^^^ -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp :lines: 141-155 - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index 47bd81905..37b68039b 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -16,9 +16,9 @@ We will use a simple 3-reaction 3-species mechanism. The setup here is the same If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index f6b577da6..089f6aa12 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -29,14 +29,14 @@ To show how this works, we'll add one photolysis reaction, one first-order loss If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. literalinclude:: ../../../test/tutorial/test_rate_constants_user_defined_by_hand.cpp :language: cpp - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading .. raw:: html @@ -70,9 +70,9 @@ Then setup the reaction which will use this rate constant: -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. code-block:: diff @@ -104,7 +104,7 @@ Then setup the reaction which will use this rate constant: - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading In this case, you only need to add the configuration to the reactions.json file in the configuration directory. @@ -137,9 +137,9 @@ Finally, set and upate the rate constants as needed: -.. tabs:: +.. tab-set:: - .. tab:: Build the Mechanism with the API + .. tab-item:: Build the Mechanism with the API .. code-block:: diff @@ -171,7 +171,7 @@ Finally, set and upate the rate constants as needed: + photo_rate *= 1.5; } - .. tab:: OpenAtmos Configuration reading + .. tab-item:: OpenAtmos Configuration reading In this case, you only need to add the configuration to the reactions.json file in the configuration directory. When reading in from a configuration file, the loss, emissions, and photolysis rates are prefixed with diff --git a/docs/switcher.json b/docs/switcher.json deleted file mode 100644 index b7ee4c723..000000000 --- a/docs/switcher.json +++ /dev/null @@ -1,12 +0,0 @@ -[ - { - "name": "v3.2.0 (stable)", - "version": "stable", - "url": "https://ncar.github.io/micm/versions/stable" - }, - { - "name": "dev", - "version": "dev", - "url": "https://ncar.github.io/micm/branch/main/" - } -] \ No newline at end of file From 5b0d1ba0314995c173aa6e5a5ce827f6fe945c80 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 10 Oct 2023 11:09:38 -0600 Subject: [PATCH 094/318] Copied solver_config.hpp to species_mechanism_config.hpp. --- .../configure/species_mechanism_config.hpp | 818 ++++++++++++++++++ 1 file changed, 818 insertions(+) create mode 100644 include/micm/configure/species_mechanism_config.hpp diff --git a/include/micm/configure/species_mechanism_config.hpp b/include/micm/configure/species_mechanism_config.hpp new file mode 100644 index 000000000..ea004b40e --- /dev/null +++ b/include/micm/configure/species_mechanism_config.hpp @@ -0,0 +1,818 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace micm +{ + enum class ConfigParseStatus + { + Success, + None, + InvalidSpeciesFilePath, + InvalidReactionsFilePath, + InvalidKey, + UnknownKey, + InvalidSpecies, + CAMPFilesSectionNotFound, + InvalidCAMPFileCount, + CAMPDataSectionNotFound, + InvalidMechanism, + ObjectTypeNotFound, + RequiredKeyNotFound + }; + + inline std::string configParseStatusToString(const ConfigParseStatus& status) + { + switch (status) + { + case ConfigParseStatus::Success: return "Success"; + case ConfigParseStatus::None: return "None"; + case ConfigParseStatus::InvalidSpeciesFilePath: return "InvalidSpeciesFilePath"; + case ConfigParseStatus::InvalidReactionsFilePath: return "InvalidReactionsFilePath"; + case ConfigParseStatus::InvalidKey: return "InvalidKey"; + case ConfigParseStatus::UnknownKey: return "UnknownKey"; + case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; + case ConfigParseStatus::CAMPFilesSectionNotFound: return "CAMPFilesSectionNotFound"; + case ConfigParseStatus::InvalidCAMPFileCount: return "InvalidCAMPFileCount"; + case ConfigParseStatus::CAMPDataSectionNotFound: return "CAMPDataSectionNotFound"; + case ConfigParseStatus::InvalidMechanism: return "InvalidMechanism"; + case ConfigParseStatus::ObjectTypeNotFound: return "ObjectTypeNotFound"; + case ConfigParseStatus::RequiredKeyNotFound: return "RequiredKeyNotFound"; + default: return "Unknown"; + } + } + + // Solver parameters + struct SolverParameters + { + System system_; + std::vector processes_; + + SolverParameters(const System& system, std::vector&& processes) + : system_(system), + processes_(std::move(processes)) + { + } + + SolverParameters(System&& system, std::vector&& processes) + : system_(std::move(system)), + processes_(std::move(processes)) + { + } + }; + + // JSON Configure paser + class JsonReaderPolicy + { + using json = nlohmann::json; + + public: + // Read from species configure + std::vector species_arr_; + + // Read from reaction configure + std::vector user_defined_rate_arr_; + std::vector arrhenius_rate_arr_; + std::vector branched_rate_arr_; + std::vector surface_rate_arr_; + std::vector troe_rate_arr_; + std::vector ternary_rate_arr_; + std::vector tunneling_rate_arr_; + + // Specific for solver parameters + Phase gas_phase_; + std::unordered_map phases_; + std::vector processes_; + + // Constants + // Configure files + static const inline std::string CAMP_CONFIG = "config.json"; + static const inline std::string SPECIES_CONFIG = "species.json"; + static const inline std::string MECHANISM_CONFIG = "mechanism.json"; + static const inline std::string REACTIONS_CONFIG = "reactions.json"; + static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; + + // Common JSON + static const inline std::string CAMP_DATA = "camp-data"; + static const inline std::string CAMP_FILES = "camp-files"; + static const inline std::string TYPE = "type"; + + // Functions + + /// @brief Parse configures + /// @return True for successful parsing + ConfigParseStatus Parse(const std::filesystem::path& config_dir) + { + // Create configure paths + std::filesystem::path species_config(config_dir / SPECIES_CONFIG); + std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); + std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); + // Note tolerance_config is defined here but not used + std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); + + // Look for CAMP config file + std::filesystem::path camp_config(config_dir / CAMP_CONFIG); + if (std::filesystem::exists(camp_config)) + { + json camp_data = json::parse(std::ifstream(camp_config)); + if (!camp_data.contains(CAMP_FILES)) + return ConfigParseStatus::CAMPFilesSectionNotFound; + + std::vector camp_files; + for (const auto& element : camp_data[CAMP_FILES]) + { + camp_files.push_back(element.get()); + } + if (camp_files.size() != 2) + { + std::string err_msg = "CAMP file list should contain two files [species.json, mechanism.json]"; + std::cerr << err_msg << std::endl; + return ConfigParseStatus::InvalidCAMPFileCount; + } + // As a temporary implementation, assume camp files are ordered + species_config = config_dir / camp_files[0]; + mechanism_config = config_dir / camp_files[1]; + } + + // Current reaction configs should be either mechanism_config or reactions_config + std::filesystem::path cur_reactions_config; + + // Check if species config exists + if (!std::filesystem::exists(species_config)) + { + std::string err_msg = "Species configuration file at path " + species_config.string() + " does not exist\n"; + std::cerr << err_msg << std::endl; + return ConfigParseStatus::InvalidSpeciesFilePath; + } + + // Check if a reaction configure exists and decide which one + if (std::filesystem::exists(mechanism_config)) + { + cur_reactions_config = mechanism_config; + } + else if (std::filesystem::exists(reactions_config)) + { + cur_reactions_config = reactions_config; + } + else + { + std::string err_msg = "Reaction configuration file at path " + mechanism_config.string() + " or " + + reactions_config.string() + " does not exist\n"; + std::cerr << err_msg << std::endl; + return ConfigParseStatus::InvalidReactionsFilePath; + } + + // Read species file to create Species and Phase that needs to be known to System and Process + + auto species_status = ConfigureSpecies(species_config); + if (species_status != ConfigParseStatus::Success) + return species_status; + + // Assign the parsed 'Species' to 'Phase' + gas_phase_ = Phase(species_arr_); + + // Read reactions file + json reaction_data = json::parse(std::ifstream(cur_reactions_config)); + + if (!reaction_data.contains(CAMP_DATA)) + return ConfigParseStatus::CAMPDataSectionNotFound; + + std::vector reaction_objects; + for (const auto& element : reaction_data[CAMP_DATA]) + { + reaction_objects.push_back(element); + } + + return ParseObjectArray(reaction_objects); + } + + private: + /// @brief Create 'Species' and 'Phase' + /// @param path to 'Species' file + /// @return True at success + ConfigParseStatus ConfigureSpecies(const std::filesystem::path& file) + { + ConfigParseStatus status = ConfigParseStatus::None; + json file_data = json::parse(std::ifstream(file)); + + if (!file_data.contains(CAMP_DATA)) + return ConfigParseStatus::CAMPDataSectionNotFound; + + std::vector objects; + for (const auto& element : file_data[CAMP_DATA]) + objects.push_back(element); + + for (const auto& object : objects) + { + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + break; + } + + std::string type = object[TYPE].get(); + + if (type == "CHEM_SPEC") + { + status = ParseChemicalSpecies(object); + } + else if (type == "RELATIVE_TOLERANCE") + { + status = ParseRelativeTolerance(object); + } + + if (status != ConfigParseStatus::Success) + break; + } + + return status; + } + + bool ValidateJsonWithKey(const json& object, const std::string& key) + { + if (!object.contains(key)) + { + std::string msg = "Key " + key + " was not found in the config file"; + std::cerr << msg << std::endl; + return false; + } + return true; + } + + ConfigParseStatus ParseObjectArray(const std::vector& objects) + { + ConfigParseStatus status = ConfigParseStatus::None; + + for (const auto& object : objects) + { + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + break; + } + + std::string type = object[TYPE].get(); + + if (type == "MECHANISM") + { + status = ParseMechanism(object); + } + else if (type == "PHOTOLYSIS") + { + status = ParsePhotolysis(object); + } + else if (type == "ARRHENIUS") + { + status = ParseArrhenius(object); + } + else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") + { + status = ParseBranched(object); + } + else if (type == "TERNARY_CHEMICAL_ACTIVATION") + { + status = ParseTernaryChemicalActivation(object); + } + else if (type == "TROE") + { + status = ParseTroe(object); + } + else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") + { + status = ParseTunneling(object); + } + else if (type == "EMISSION") + { + status = ParseEmission(object); + } + else if (type == "FIRST_ORDER_LOSS") + { + status = ParseFirstOrderLoss(object); + } + else if (type == "SURFACE") + { + status = ParseSurface(object); + } + else + { + status = ConfigParseStatus::UnknownKey; + } + if (status != ConfigParseStatus::Success) + break; + } + + return status; + } + + ConfigParseStatus ParseChemicalSpecies(const json& object) + { + // required keys + const std::string NAME = "name"; + + std::array required_keys = { NAME }; + + // Check if it contains the required key(s) + for (const auto& key : required_keys) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + std::string name = object[NAME].get(); + + // Load remaining keys as properties + std::map properties{}; + for (auto& [key, value] : object.items()) + { + if (value.is_number_float()) + properties[key] = value; + } + species_arr_.push_back(Species(name, properties)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseRelativeTolerance(const json& object) + { + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseMechanism(const json& object) + { + std::vector required_keys = { "name", "reactions" }; + for (const auto& key : required_keys) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + std::vector objects; + for (const auto& element : object["reactions"]) + { + objects.push_back(element); + } + + return ParseObjectArray(objects); + } + + std::vector ParseReactants(const json& object) + { + const std::string QTY = "qty"; + std::vector reactants; + for (auto& [key, value] : object.items()) + { + std::size_t qty = 1; + if (value.contains(QTY)) + qty = value[QTY]; + for (std::size_t i = 0; i < qty; ++i) + reactants.push_back(Species(key)); + } + return reactants; + } + + std::vector> ParseProducts(const json& object) + { + const std::string YIELD = "yield"; + constexpr double DEFAULT_YEILD = 1.0; + std::vector> products; + for (auto& [key, value] : object.items()) + { + if (value.contains(YIELD)) + { + products.push_back(std::make_pair(Species(key), value[YIELD])); + } + else + { + products.push_back(std::make_pair(Species(key), DEFAULT_YEILD)); + } + } + return products; + } + + ConfigParseStatus ParsePhotolysis(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + const std::string MUSICA_NAME = "MUSICA name"; + + for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + std::string name = "PHOTO." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseArrhenius(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + ArrheniusRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } + if (object.contains("D")) + { + parameters.D_ = object["D"].get(); + } + if (object.contains("E")) + { + parameters.E_ = object["E"].get(); + } + if (object.contains("Ea")) + { + // Calculate 'C' using 'Ea' + parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; + } + + arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseBranched(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string ALKOXY_PRODUCTS = "alkoxy products"; + const std::string NITRATE_PRODUCTS = "nitrate products"; + const std::string X = "X"; + const std::string Y = "Y"; + const std::string A0 = "a0"; + const std::string N = "n"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); + auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + + BranchedRateConstantParameters parameters; + parameters.X_ = object[X].get(); + parameters.Y_ = object[Y].get(); + parameters.a0_ = object[A0].get(); + parameters.n_ = object[N].get(); + + // Alkoxy branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); + + // Nitrate branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseTroe(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + TroeRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } + + troe_rate_arr_.push_back(TroeRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseTernaryChemicalActivation(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + TernaryChemicalActivationRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } + + ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); + + std::unique_ptr rate_ptr = + std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseTunneling(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + TunnelingRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } + + tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseEmission(const json& object) + { + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; + for (const auto& key : { SPECIES, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + products_object[species] = { { "YIELD", 1.0 } }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); + + std::string name = "EMIS." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseFirstOrderLoss(const json& object) + { + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; + for (const auto& key : { SPECIES, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + reactants_object[species] = { {} }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); + + std::string name = "LOSS." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseSurface(const json& object) + { + const std::string REACTANTS = "gas-phase reactant"; + const std::string PRODUCTS = "gas-phase products"; + const std::string MUSICA_NAME = "MUSICA name"; + const std::string PROBABILITY = "reaction probability"; + for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::string species_name = object[REACTANTS].get(); + json reactants_object{}; + reactants_object[species_name] = { {} }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(object[PRODUCTS]); + + Species reactant_species = Species(""); + for (auto& species : species_arr_) + { + if (species.name_ == species_name) + { + reactant_species = species; + break; + } + } + SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), + .species_ = reactant_species }; + + if (object.contains(PROBABILITY)) + { + parameters.reaction_probability_ = object[PROBABILITY].get(); + } + + surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + }; + + /// @brief Public interface to read and parse config + template + class SolverConfig : public ConfigTypePolicy + { + private: + ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; + + public: + /// @brief Reads and parses configures + /// @param config_dir A path to a configuration file + /// @return an enum indicating the success or failure of the parse + [[nodiscard]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) + { + last_parse_status_ = this->Parse(config_dir); + return last_parse_status_; + } + + /// @brief Creates and returns SolverParameters + /// @return SolverParameters that contains 'System' and a collection of 'Process' + SolverParameters GetSolverParams() + { + if (last_parse_status_ != ConfigParseStatus::Success) + { + std::string msg = "Parsing configuration files failed. The parsing failed with error: " + + configParseStatusToString(last_parse_status_); + throw std::runtime_error(msg); + } + + return SolverParameters( + std::move(System(std::move(this->gas_phase_), std::move(this->phases_))), std::move(this->processes_)); + } + }; + +} // namespace micm From 79a3e42e3dac40dd66a42aa57441d2ba49d8c069 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 10 Oct 2023 11:12:08 -0600 Subject: [PATCH 095/318] New file camp_config.hpp. --- .../configure/{species_mechanism_config.hpp => camp_config.hpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/micm/configure/{species_mechanism_config.hpp => camp_config.hpp} (100%) diff --git a/include/micm/configure/species_mechanism_config.hpp b/include/micm/configure/camp_config.hpp similarity index 100% rename from include/micm/configure/species_mechanism_config.hpp rename to include/micm/configure/camp_config.hpp From d1ccdf75fe16811717ab042040c5b8ad3a03aa12 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 10 Oct 2023 12:17:11 -0600 Subject: [PATCH 096/318] Start on test_camp_config. --- test/unit/configure/CMakeLists.txt | 3 ++- test/unit/configure/test_camp_config.cpp | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 test/unit/configure/test_camp_config.cpp diff --git a/test/unit/configure/CMakeLists.txt b/test/unit/configure/CMakeLists.txt index a76f870af..d8a571056 100644 --- a/test/unit/configure/CMakeLists.txt +++ b/test/unit/configure/CMakeLists.txt @@ -7,8 +7,9 @@ include(test_util) # Tests create_standard_test(NAME solver_config SOURCES test_solver_config.cpp) +create_standard_test(NAME camp_config SOURCES test_camp_config.cpp) ################################################################################ # Tests -add_subdirectory(process) \ No newline at end of file +add_subdirectory(process) diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp new file mode 100644 index 000000000..883cd1cf3 --- /dev/null +++ b/test/unit/configure/test_camp_config.cpp @@ -0,0 +1,10 @@ +#include + +#include + +TEST(SolverConfig, DetectsInvalidConfigFile) +{ + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); + EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); +} From b90bb758bd74d3eb7869251df8810e3334cd89df Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 10 Oct 2023 12:40:54 -0600 Subject: [PATCH 097/318] Start on camp_config.hpp. --- include/micm/configure/camp_config.hpp | 746 +---------------------- test/unit/configure/test_camp_config.cpp | 6 +- 2 files changed, 5 insertions(+), 747 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index ea004b40e..ebfc8daff 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -8,20 +8,12 @@ #include #include #include -#include -#include +#include #include -#include -#include -#include -#include -#include +#include #include #include -#include #include -#include -#include namespace micm { @@ -81,738 +73,4 @@ namespace micm { } }; - - // JSON Configure paser - class JsonReaderPolicy - { - using json = nlohmann::json; - - public: - // Read from species configure - std::vector species_arr_; - - // Read from reaction configure - std::vector user_defined_rate_arr_; - std::vector arrhenius_rate_arr_; - std::vector branched_rate_arr_; - std::vector surface_rate_arr_; - std::vector troe_rate_arr_; - std::vector ternary_rate_arr_; - std::vector tunneling_rate_arr_; - - // Specific for solver parameters - Phase gas_phase_; - std::unordered_map phases_; - std::vector processes_; - - // Constants - // Configure files - static const inline std::string CAMP_CONFIG = "config.json"; - static const inline std::string SPECIES_CONFIG = "species.json"; - static const inline std::string MECHANISM_CONFIG = "mechanism.json"; - static const inline std::string REACTIONS_CONFIG = "reactions.json"; - static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; - - // Common JSON - static const inline std::string CAMP_DATA = "camp-data"; - static const inline std::string CAMP_FILES = "camp-files"; - static const inline std::string TYPE = "type"; - - // Functions - - /// @brief Parse configures - /// @return True for successful parsing - ConfigParseStatus Parse(const std::filesystem::path& config_dir) - { - // Create configure paths - std::filesystem::path species_config(config_dir / SPECIES_CONFIG); - std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); - std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); - // Note tolerance_config is defined here but not used - std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); - - // Look for CAMP config file - std::filesystem::path camp_config(config_dir / CAMP_CONFIG); - if (std::filesystem::exists(camp_config)) - { - json camp_data = json::parse(std::ifstream(camp_config)); - if (!camp_data.contains(CAMP_FILES)) - return ConfigParseStatus::CAMPFilesSectionNotFound; - - std::vector camp_files; - for (const auto& element : camp_data[CAMP_FILES]) - { - camp_files.push_back(element.get()); - } - if (camp_files.size() != 2) - { - std::string err_msg = "CAMP file list should contain two files [species.json, mechanism.json]"; - std::cerr << err_msg << std::endl; - return ConfigParseStatus::InvalidCAMPFileCount; - } - // As a temporary implementation, assume camp files are ordered - species_config = config_dir / camp_files[0]; - mechanism_config = config_dir / camp_files[1]; - } - - // Current reaction configs should be either mechanism_config or reactions_config - std::filesystem::path cur_reactions_config; - - // Check if species config exists - if (!std::filesystem::exists(species_config)) - { - std::string err_msg = "Species configuration file at path " + species_config.string() + " does not exist\n"; - std::cerr << err_msg << std::endl; - return ConfigParseStatus::InvalidSpeciesFilePath; - } - - // Check if a reaction configure exists and decide which one - if (std::filesystem::exists(mechanism_config)) - { - cur_reactions_config = mechanism_config; - } - else if (std::filesystem::exists(reactions_config)) - { - cur_reactions_config = reactions_config; - } - else - { - std::string err_msg = "Reaction configuration file at path " + mechanism_config.string() + " or " + - reactions_config.string() + " does not exist\n"; - std::cerr << err_msg << std::endl; - return ConfigParseStatus::InvalidReactionsFilePath; - } - - // Read species file to create Species and Phase that needs to be known to System and Process - - auto species_status = ConfigureSpecies(species_config); - if (species_status != ConfigParseStatus::Success) - return species_status; - - // Assign the parsed 'Species' to 'Phase' - gas_phase_ = Phase(species_arr_); - - // Read reactions file - json reaction_data = json::parse(std::ifstream(cur_reactions_config)); - - if (!reaction_data.contains(CAMP_DATA)) - return ConfigParseStatus::CAMPDataSectionNotFound; - - std::vector reaction_objects; - for (const auto& element : reaction_data[CAMP_DATA]) - { - reaction_objects.push_back(element); - } - - return ParseObjectArray(reaction_objects); - } - - private: - /// @brief Create 'Species' and 'Phase' - /// @param path to 'Species' file - /// @return True at success - ConfigParseStatus ConfigureSpecies(const std::filesystem::path& file) - { - ConfigParseStatus status = ConfigParseStatus::None; - json file_data = json::parse(std::ifstream(file)); - - if (!file_data.contains(CAMP_DATA)) - return ConfigParseStatus::CAMPDataSectionNotFound; - - std::vector objects; - for (const auto& element : file_data[CAMP_DATA]) - objects.push_back(element); - - for (const auto& object : objects) - { - if (!ValidateJsonWithKey(object, TYPE)) - { - status = ConfigParseStatus::ObjectTypeNotFound; - break; - } - - std::string type = object[TYPE].get(); - - if (type == "CHEM_SPEC") - { - status = ParseChemicalSpecies(object); - } - else if (type == "RELATIVE_TOLERANCE") - { - status = ParseRelativeTolerance(object); - } - - if (status != ConfigParseStatus::Success) - break; - } - - return status; - } - - bool ValidateJsonWithKey(const json& object, const std::string& key) - { - if (!object.contains(key)) - { - std::string msg = "Key " + key + " was not found in the config file"; - std::cerr << msg << std::endl; - return false; - } - return true; - } - - ConfigParseStatus ParseObjectArray(const std::vector& objects) - { - ConfigParseStatus status = ConfigParseStatus::None; - - for (const auto& object : objects) - { - if (!ValidateJsonWithKey(object, TYPE)) - { - status = ConfigParseStatus::ObjectTypeNotFound; - break; - } - - std::string type = object[TYPE].get(); - - if (type == "MECHANISM") - { - status = ParseMechanism(object); - } - else if (type == "PHOTOLYSIS") - { - status = ParsePhotolysis(object); - } - else if (type == "ARRHENIUS") - { - status = ParseArrhenius(object); - } - else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") - { - status = ParseBranched(object); - } - else if (type == "TERNARY_CHEMICAL_ACTIVATION") - { - status = ParseTernaryChemicalActivation(object); - } - else if (type == "TROE") - { - status = ParseTroe(object); - } - else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") - { - status = ParseTunneling(object); - } - else if (type == "EMISSION") - { - status = ParseEmission(object); - } - else if (type == "FIRST_ORDER_LOSS") - { - status = ParseFirstOrderLoss(object); - } - else if (type == "SURFACE") - { - status = ParseSurface(object); - } - else - { - status = ConfigParseStatus::UnknownKey; - } - if (status != ConfigParseStatus::Success) - break; - } - - return status; - } - - ConfigParseStatus ParseChemicalSpecies(const json& object) - { - // required keys - const std::string NAME = "name"; - - std::array required_keys = { NAME }; - - // Check if it contains the required key(s) - for (const auto& key : required_keys) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - std::string name = object[NAME].get(); - - // Load remaining keys as properties - std::map properties{}; - for (auto& [key, value] : object.items()) - { - if (value.is_number_float()) - properties[key] = value; - } - species_arr_.push_back(Species(name, properties)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseRelativeTolerance(const json& object) - { - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseMechanism(const json& object) - { - std::vector required_keys = { "name", "reactions" }; - for (const auto& key : required_keys) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - std::vector objects; - for (const auto& element : object["reactions"]) - { - objects.push_back(element); - } - - return ParseObjectArray(objects); - } - - std::vector ParseReactants(const json& object) - { - const std::string QTY = "qty"; - std::vector reactants; - for (auto& [key, value] : object.items()) - { - std::size_t qty = 1; - if (value.contains(QTY)) - qty = value[QTY]; - for (std::size_t i = 0; i < qty; ++i) - reactants.push_back(Species(key)); - } - return reactants; - } - - std::vector> ParseProducts(const json& object) - { - const std::string YIELD = "yield"; - constexpr double DEFAULT_YEILD = 1.0; - std::vector> products; - for (auto& [key, value] : object.items()) - { - if (value.contains(YIELD)) - { - products.push_back(std::make_pair(Species(key), value[YIELD])); - } - else - { - products.push_back(std::make_pair(Species(key), DEFAULT_YEILD)); - } - } - return products; - } - - ConfigParseStatus ParsePhotolysis(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - const std::string MUSICA_NAME = "MUSICA name"; - - for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - std::string name = "PHOTO." + object[MUSICA_NAME].get(); - - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseArrhenius(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - ArrheniusRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } - if (object.contains("D")) - { - parameters.D_ = object["D"].get(); - } - if (object.contains("E")) - { - parameters.E_ = object["E"].get(); - } - if (object.contains("Ea")) - { - // Calculate 'C' using 'Ea' - parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; - } - - arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseBranched(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string ALKOXY_PRODUCTS = "alkoxy products"; - const std::string NITRATE_PRODUCTS = "nitrate products"; - const std::string X = "X"; - const std::string Y = "Y"; - const std::string A0 = "a0"; - const std::string N = "n"; - - // Check required json objects exist - for (const auto& key : { REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); - auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); - - BranchedRateConstantParameters parameters; - parameters.X_ = object[X].get(); - parameters.Y_ = object[Y].get(); - parameters.a0_ = object[A0].get(); - parameters.n_ = object[N].get(); - - // Alkoxy branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); - - // Nitrate branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTroe(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - TroeRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } - - troe_rate_arr_.push_back(TroeRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTernaryChemicalActivation(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - TernaryChemicalActivationRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } - - ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); - - std::unique_ptr rate_ptr = - std::make_unique(parameters); - - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTunneling(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - TunnelingRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } - - tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseEmission(const json& object) - { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { SPECIES, MUSICA_NAME }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - products_object[species] = { { "YIELD", 1.0 } }; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); - - std::string name = "EMIS." + object[MUSICA_NAME].get(); - - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseFirstOrderLoss(const json& object) - { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { SPECIES, MUSICA_NAME }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - reactants_object[species] = { {} }; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); - - std::string name = "LOSS." + object[MUSICA_NAME].get(); - - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseSurface(const json& object) - { - const std::string REACTANTS = "gas-phase reactant"; - const std::string PRODUCTS = "gas-phase products"; - const std::string MUSICA_NAME = "MUSICA name"; - const std::string PROBABILITY = "reaction probability"; - for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - std::string species_name = object[REACTANTS].get(); - json reactants_object{}; - reactants_object[species_name] = { {} }; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(object[PRODUCTS]); - - Species reactant_species = Species(""); - for (auto& species : species_arr_) - { - if (species.name_ == species_name) - { - reactant_species = species; - break; - } - } - SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), - .species_ = reactant_species }; - - if (object.contains(PROBABILITY)) - { - parameters.reaction_probability_ = object[PROBABILITY].get(); - } - - surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - }; - - /// @brief Public interface to read and parse config - template - class SolverConfig : public ConfigTypePolicy - { - private: - ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; - - public: - /// @brief Reads and parses configures - /// @param config_dir A path to a configuration file - /// @return an enum indicating the success or failure of the parse - [[nodiscard]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) - { - last_parse_status_ = this->Parse(config_dir); - return last_parse_status_; - } - - /// @brief Creates and returns SolverParameters - /// @return SolverParameters that contains 'System' and a collection of 'Process' - SolverParameters GetSolverParams() - { - if (last_parse_status_ != ConfigParseStatus::Success) - { - std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_); - throw std::runtime_error(msg); - } - - return SolverParameters( - std::move(System(std::move(this->gas_phase_), std::move(this->phases_))), std::move(this->processes_)); - } - }; - } // namespace micm diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 883cd1cf3..60c88d4f0 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -4,7 +4,7 @@ TEST(SolverConfig, DetectsInvalidConfigFile) { - micm::SolverConfig solverConfig{}; - auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); + // micm::SolverConfig solverConfig{}; + // auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); + // EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); } From 688fdaeff047e1710329bc7e8b746aa6e497fbfd Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 10 Oct 2023 16:50:19 -0500 Subject: [PATCH 098/318] 292 add a docker image publish (#294) * trying to publish an image * trying to build exactly one dockerfile for publish * newer build and push * maybe now * removing unused package * trying to do a multiplatform build * just arm and amd then? * merging main * trying only arm64 * adding qemu to try to use more platforms * just publishing one package * publishing only on release --- .github/workflows/publish-package.yml | 50 +++++++++++++++++++++++++++ docker/Dockerfile.publish | 27 +++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 .github/workflows/publish-package.yml create mode 100644 docker/Dockerfile.publish diff --git a/.github/workflows/publish-package.yml b/.github/workflows/publish-package.yml new file mode 100644 index 000000000..af6e219fe --- /dev/null +++ b/.github/workflows/publish-package.yml @@ -0,0 +1,50 @@ +name: Create and publish a Docker image + +on: + push: + branches: ['release'] + tags: + - '*' + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Login to Container Registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + platforms: linux/amd64 + file: docker/Dockerfile.publish + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/docker/Dockerfile.publish b/docker/Dockerfile.publish new file mode 100644 index 000000000..1edf539f0 --- /dev/null +++ b/docker/Dockerfile.publish @@ -0,0 +1,27 @@ +FROM fedora:37 + +RUN dnf -y update \ + && dnf -y install \ + cmake \ + gcc-c++ \ + gdb \ + git \ + make \ + zlib-devel \ + llvm-devel \ + && dnf clean all + +# copy the MICM code +COPY . /micm/ + +# build the library and run the tests +RUN mkdir /build \ + && cd /build \ + && cmake \ + -D CMAKE_BUILD_TYPE=release \ + -D ENABLE_LLVM:BOOL=TRUE \ + -D ENABLE_JSON:BOOL=TRUE \ + ../micm \ + && make install -j 8 + +WORKDIR /build \ No newline at end of file From a87f1b78f6e1b604df4c9cb73faf5ace7f8fffbb Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 10 Oct 2023 16:06:14 -0600 Subject: [PATCH 099/318] Save. --- include/micm/configure/camp_config.hpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index ebfc8daff..9c3007b0f 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -21,14 +21,11 @@ namespace micm { Success, None, - InvalidSpeciesFilePath, - InvalidReactionsFilePath, InvalidKey, UnknownKey, - InvalidSpecies, CAMPFilesSectionNotFound, - InvalidCAMPFileCount, CAMPDataSectionNotFound, + InvalidSpecies, InvalidMechanism, ObjectTypeNotFound, RequiredKeyNotFound @@ -40,14 +37,11 @@ namespace micm { case ConfigParseStatus::Success: return "Success"; case ConfigParseStatus::None: return "None"; - case ConfigParseStatus::InvalidSpeciesFilePath: return "InvalidSpeciesFilePath"; - case ConfigParseStatus::InvalidReactionsFilePath: return "InvalidReactionsFilePath"; case ConfigParseStatus::InvalidKey: return "InvalidKey"; case ConfigParseStatus::UnknownKey: return "UnknownKey"; - case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; case ConfigParseStatus::CAMPFilesSectionNotFound: return "CAMPFilesSectionNotFound"; - case ConfigParseStatus::InvalidCAMPFileCount: return "InvalidCAMPFileCount"; 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"; @@ -73,4 +67,9 @@ namespace micm { } }; + + class JsonReaderPolicy + { + using json = nlohmann::json; + }; } // namespace micm From b159f663c0564efc790ad7d37348f96fe23aaa80 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 11 Oct 2023 10:31:00 -0500 Subject: [PATCH 100/318] adding icon to readme and favicon to docs --- README.md | 4 + docs/README.md | 1 + .../favicon/android-chrome-192x192.png | Bin 0 -> 12118 bytes .../favicon/android-chrome-512x512.png | Bin 0 -> 34095 bytes .../_static/favicon/apple-touch-icon.png | Bin 0 -> 11311 bytes docs/source/_static/favicon/browserconfig.xml | 9 + docs/source/_static/favicon/favicon-16x16.png | Bin 0 -> 1186 bytes docs/source/_static/favicon/favicon-32x32.png | Bin 0 -> 2167 bytes docs/source/_static/favicon/favicon.ico | Bin 0 -> 15086 bytes .../source/_static/favicon/mstile-144x144.png | Bin 0 -> 9029 bytes .../source/_static/favicon/mstile-150x150.png | Bin 0 -> 8759 bytes .../source/_static/favicon/mstile-310x150.png | Bin 0 -> 9400 bytes .../source/_static/favicon/mstile-310x310.png | Bin 0 -> 18882 bytes docs/source/_static/favicon/mstile-70x70.png | Bin 0 -> 6327 bytes .../_static/favicon/safari-pinned-tab.svg | 78 ++++ docs/source/_static/favicon/site.webmanifest | 19 + docs/source/_static/micm.png | Bin 0 -> 24795 bytes docs/source/_static/micm.svg | 340 ++++++++++++++++++ docs/source/conf.py | 4 +- 19 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 docs/README.md create mode 100644 docs/source/_static/favicon/android-chrome-192x192.png create mode 100644 docs/source/_static/favicon/android-chrome-512x512.png create mode 100644 docs/source/_static/favicon/apple-touch-icon.png create mode 100644 docs/source/_static/favicon/browserconfig.xml create mode 100644 docs/source/_static/favicon/favicon-16x16.png create mode 100644 docs/source/_static/favicon/favicon-32x32.png create mode 100644 docs/source/_static/favicon/favicon.ico create mode 100644 docs/source/_static/favicon/mstile-144x144.png create mode 100644 docs/source/_static/favicon/mstile-150x150.png create mode 100644 docs/source/_static/favicon/mstile-310x150.png create mode 100644 docs/source/_static/favicon/mstile-310x310.png create mode 100644 docs/source/_static/favicon/mstile-70x70.png create mode 100644 docs/source/_static/favicon/safari-pinned-tab.svg create mode 100644 docs/source/_static/favicon/site.webmanifest create mode 100644 docs/source/_static/micm.png create mode 100644 docs/source/_static/micm.svg diff --git a/README.md b/README.md index ba1895a51..8bb209990 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ Model Independent Chemical Module. MICM can be used to configure and solve atmos Copyright (C) 2018-2023 National Center for Atmospheric Research +

+ +

+ # Getting Started ## Installing MICM locally diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..2affd9078 --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +Facvicons generated at [realfavicongenerator.net](https://realfavicongenerator.net/) \ No newline at end of file diff --git a/docs/source/_static/favicon/android-chrome-192x192.png b/docs/source/_static/favicon/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..4f10117b300a6e117bedfb78d23e109d80c370be GIT binary patch literal 12118 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/favicon/apple-touch-icon.png b/docs/source/_static/favicon/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5418af68b4d68535b6baa3a83dc3441eb5b85784 GIT binary patch literal 11311 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/favicon/browserconfig.xml b/docs/source/_static/favicon/browserconfig.xml new file mode 100644 index 000000000..b3930d0f0 --- /dev/null +++ b/docs/source/_static/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/docs/source/_static/favicon/favicon-16x16.png b/docs/source/_static/favicon/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..c5bb975a349af416c389b75bb9b7bef436295a2b GIT binary patch literal 1186 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/favicon/favicon.ico b/docs/source/_static/favicon/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..7905e965d61fbcabc9567e5cfa0bafc8d1b8e25a GIT binary patch literal 15086 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 W)HuT^8E+WdcOn%b9tvt=mH#J_=q)Dz literal 0 HcmV?d00001 diff --git a/docs/source/_static/favicon/mstile-144x144.png b/docs/source/_static/favicon/mstile-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..d6ae2c4fd92b841523b3eba1294c5be9502c803f GIT binary patch literal 9029 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 E2MQuu(f|Me literal 0 HcmV?d00001 diff --git a/docs/source/_static/favicon/mstile-310x150.png b/docs/source/_static/favicon/mstile-310x150.png new file mode 100644 index 0000000000000000000000000000000000000000..2638fda6f1f4338f818d744a84d174c896dad780 GIT binary patch literal 9400 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 DuD};` literal 0 HcmV?d00001 diff --git a/docs/source/_static/favicon/mstile-310x310.png b/docs/source/_static/favicon/mstile-310x310.png new file mode 100644 index 0000000000000000000000000000000000000000..f99fbc831951af0e5c0a5effe410460649370fe8 GIT binary patch literal 18882 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%0A8k<=v2 znY*^_k;iNiVMo*@#xFULEaQW%5MBW{eE8AL*UgaxX+5)#3Z!ke8&r5WVh9y@!f&3f zsyCH%2Tk$>cGU~Q$0WZ3(w<*#o1w?vR=>8mQjawIzE9SVypalVIqkA`DRXO8VDluW zq6u#j4v>b~Iu!ycY9e+AKJ@L}?t=Fho zef$#0`S0H{HqMiWYf)J0MjQfSR4aG*k;CHPr#;w7Xpb0|D=Qbz5TnQX#Q&!RMBL-y zdBKecY{jP@%I=&#swO^m);{*qwqEuR3J`&chzbZp1%!nSg~g>sM5KkK_@Gc}DD + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/docs/source/_static/favicon/site.webmanifest b/docs/source/_static/favicon/site.webmanifest new file mode 100644 index 000000000..b20abb7cb --- /dev/null +++ b/docs/source/_static/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/docs/source/_static/micm.png b/docs/source/_static/micm.png new file mode 100644 index 0000000000000000000000000000000000000000..002f408a6ae7906f01e2f21d43ca5da7e6c21554 GIT binary patch literal 24795 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 + + + diff --git a/docs/source/conf.py b/docs/source/conf.py index 5c753c8c5..15a34ab77 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -75,4 +75,6 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] \ No newline at end of file +html_static_path = ['_static'] + +html_favicon = '_static/favicon/favicon.ico' \ No newline at end of file From 0996e5848b097e77ccbb2819f22c49844154f3e3 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 11 Oct 2023 09:53:37 -0700 Subject: [PATCH 101/318] Fix set concentrations mapping error (#297) * update set concentrations logic * remove system arg from set concentrations function --- include/micm/solver/state.hpp | 1 - include/micm/solver/state.inl | 63 +++---------------- test/integration/chapman.cpp | 2 +- ..._constants_no_user_defined_with_config.cpp | 2 +- ...ate_constants_user_defined_with_config.cpp | 2 +- test/unit/solver/test_state.cpp | 14 +---- 6 files changed, 14 insertions(+), 70 deletions(-) diff --git a/include/micm/solver/state.hpp b/include/micm/solver/state.hpp index a2a3b1dee..da59c2fa0 100644 --- a/include/micm/solver/state.hpp +++ b/include/micm/solver/state.hpp @@ -49,7 +49,6 @@ namespace micm /// @brief Set species' concentrations /// @param species_to_concentration void SetConcentrations( - const System& system, const std::unordered_map>& species_to_concentration); /// @brief Set a single species concentration diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index a9e85b02d..dbd2c2321 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -51,67 +51,19 @@ namespace micm template class MatrixPolicy> inline void State::SetConcentrations( - const System& system, const std::unordered_map>& species_to_concentration) { - int num_set_grid_cells = 0; - unsigned num_species = system.gas_phase_.species_.size(); - - std::vector num_concentrations_per_species; - num_concentrations_per_species.reserve(num_species); - - // Iterate map to store the number of concentration values corresponding to the number of set of grid cells - for (auto& species : system.gas_phase_.species_) - { - auto species_ptr = species_to_concentration.find(species.name_); - if (species_ptr == species_to_concentration.end()) - { - throw std::runtime_error("Concentration value(s) for '" + species.name_ + "' must be given."); - } - num_concentrations_per_species.push_back(species_ptr->second.size()); - } - - // Check if number of concentraiton inputs are the same for all species - if (!std::all_of( - num_concentrations_per_species.begin(), - num_concentrations_per_species.end(), - [&](int& i) { return i == num_concentrations_per_species.front(); })) - { - throw std::runtime_error("Concentration value must be given to all sets of grid cells."); - } - - num_set_grid_cells = num_concentrations_per_species[0]; - - // Find species and iterate through the keys to store concentrations for each set of grid cells - // 'concentrations' represents an N-D array in contiguous memory (N = num_set_grid_cells) - std::vector concentrations; - concentrations.resize(num_species * num_set_grid_cells); - - for (int i = 0; i < num_species; i++) - { - auto species_ptr = species_to_concentration.find(system.gas_phase_.species_[i].name_); - - for (int j = 0; j < num_set_grid_cells; j++) - { - concentrations[i + num_species * j] = species_ptr->second[j]; - } - } - - // Extract sub vector to assign to the corresponding set of grid cells. - std::vector sub_concentrations; - sub_concentrations.reserve(num_species); - - for (int i = 0; i < num_set_grid_cells; i++) - { - sub_concentrations = { concentrations.begin() + (num_species * i), - concentrations.begin() + (num_species * i) + num_species }; - variables_[i] = sub_concentrations; - } + const int num_grid_cells = conditions_.size(); + for (const auto& pair : species_to_concentration) + SetConcentration({ pair.first }, pair.second); } template class MatrixPolicy> inline void State::SetConcentration(const Species& species, double concentration) { + auto var = variable_map_.find(species.name_); + if (var == variable_map_.end()) + throw std::invalid_argument("Unknown variable '" + species.name_ + "'"); if (variables_.size() != 1) throw std::invalid_argument("Incorrect number of concentration values passed to multi-gridcell State"); variables_[0][variable_map_[species.name_]] = concentration; @@ -120,6 +72,9 @@ namespace micm template class MatrixPolicy> inline void State::SetConcentration(const Species& species, const std::vector& concentration) { + auto var = variable_map_.find(species.name_); + if (var == variable_map_.end()) + throw std::invalid_argument("Unknown variable '" + species.name_ + "'"); if (variables_.size() != concentration.size()) throw std::invalid_argument("Incorrect number of concentration values passed to multi-gridcell State"); std::size_t i_species = variable_map_[species.name_]; diff --git a/test/integration/chapman.cpp b/test/integration/chapman.cpp index f385279d6..01da9b6e7 100644 --- a/test/integration/chapman.cpp +++ b/test/integration/chapman.cpp @@ -46,7 +46,7 @@ TEST(ChapmanIntegration, CanBuildChapmanSystemUsingConfig) { "Ar", { 0.2 } }, { "N2", { 0.3 } }, { "H2O", { 0.3 } }, { "CO2", { 0.3 } } }; - state.SetConcentrations(solver_params.system_, concentrations); + state.SetConcentrations(concentrations); // User gives an input of photolysis rate constants std::unordered_map> photo_rates = { { "PHOTO.O2_1", { 0.1 } }, diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index ca5cc06d9..b4842c179 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -79,7 +79,7 @@ int main(const int argc, const char* argv[]) { "G", { 0.0 } }, // mol m-3 }; - state.SetConcentrations(solver_params.system_, intial_concentration); + state.SetConcentrations(intial_concentration); state.SetCustomRateParameter("SURF.C surface.effective radius [m]", 1e-7); state.SetCustomRateParameter("SURF.C surface.particle number concentration [# m-3]", 2.5e6); diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index 203242a61..2d0218ecf 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -87,7 +87,7 @@ int main(const int argc, const char* argv[]) { "G", { 0.0 } }, // mol m-3 }; - state.SetConcentrations(solver_params.system_, intial_concentration); + state.SetConcentrations(intial_concentration); state.SetCustomRateParameter("SURF.C surface.effective radius [m]", 1e-7); state.SetCustomRateParameter("SURF.C surface.particle number concentration [# m-3]", 2.5e6); diff --git a/test/unit/solver/test_state.cpp b/test/unit/solver/test_state.cpp index 1aa586b6f..053ea1bfd 100644 --- a/test/unit/solver/test_state.cpp +++ b/test/unit/solver/test_state.cpp @@ -69,12 +69,7 @@ TEST(State, SettingConcentrationsWithInvalidArguementsThrowsException) { "FUU", { 0.1 } }, { "bar", { 0.2 } }, { "baz", { 0.3 } }, { "quz", { 0.4 } } }; - // Build system - micm::Phase gas_phase( - std::vector{ micm::Species("foo"), micm::Species("bar"), micm::Species("baz"), micm::Species("quz") }); - micm::System system{ micm::SystemParameters{ gas_phase } }; - - EXPECT_ANY_THROW(state.SetConcentrations(system, concentrations)); + EXPECT_ANY_THROW(state.SetConcentrations(concentrations)); } TEST(State, SetConcentrations) @@ -87,17 +82,12 @@ TEST(State, SetConcentrations) .number_of_grid_cells_ = num_grid_cells, .number_of_rate_constants_ = 10 } }; - // Build system - micm::Phase gas_phase( - std::vector{ micm::Species("foo"), micm::Species("bar"), micm::Species("baz"), micm::Species("quz") }); - micm::System system{ micm::SystemParameters{ gas_phase } }; - std::unordered_map> concentrations = { { "bar", { 0.2, 0.22, 0.222 } }, { "baz", { 0.3, 0.33, 0.333 } }, { "foo", { 0.9, 0.99, 0.999 } }, { "quz", { 0.4, 0.44, 0.444 } } }; - state.SetConcentrations(system, concentrations); + state.SetConcentrations(concentrations); // Compare concentration values std::vector concentrations_in_order{ From 6e2ad195fd73642b28b37b852e11e5b2c39ce739 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 11 Oct 2023 16:46:12 -0700 Subject: [PATCH 102/318] add parameterized species option --- include/micm/process/process.hpp | 33 +++++----- include/micm/process/process_set.hpp | 49 +++++++++------ include/micm/system/phase.hpp | 16 +++++ include/micm/system/species.hpp | 21 ++++++- include/micm/system/system.hpp | 16 ++--- test/unit/process/test_process.cpp | 9 ++- test/unit/process/test_process_set_policy.hpp | 6 +- test/unit/system/test_phase.cpp | 22 +++++++ test/unit/system/test_species.cpp | 61 +++++++++++++++---- test/unit/system/test_system.cpp | 39 ++++++++++++ 10 files changed, 210 insertions(+), 62 deletions(-) diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index cd7aee650..786f4bda8 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -36,13 +36,11 @@ namespace micm /// @param processes The set of processes being solved /// @param state The solver state to update template class MatrixPolicy> - requires(!VectorizableDense>) static void UpdateState( - const std::vector& processes, - State& state); + requires(!VectorizableDense>) + static void UpdateState(const std::vector& processes, State& state); template class MatrixPolicy> - requires(VectorizableDense>) static void UpdateState( - const std::vector& processes, - State& state); + requires(VectorizableDense>) + static void UpdateState(const std::vector& processes, State& state); friend class ProcessBuilder; static ProcessBuilder create(); @@ -92,9 +90,8 @@ namespace micm }; template class MatrixPolicy> - requires(!VectorizableDense>) void Process::UpdateState( - const std::vector& processes, - State& state) + requires(!VectorizableDense>) + void Process::UpdateState(const std::vector& processes, State& state) { for (std::size_t i{}; i < state.custom_rate_parameters_.size(); ++i) { @@ -103,17 +100,20 @@ namespace micm std::size_t i_rate_constant = 0; for (auto& process : processes) { + double fixed_reactants = 1.0; + for (auto& reactant : process.reactants_) + if (reactant.IsParameterized()) + fixed_reactants *= reactant.parameterize_(state.conditions_[i]); state.rate_constants_[i][(i_rate_constant++)] = - process.rate_constant_->calculate(state.conditions_[i], custom_parameters_iter); + process.rate_constant_->calculate(state.conditions_[i], custom_parameters_iter) * fixed_reactants; custom_parameters_iter += process.rate_constant_->SizeCustomParameters(); } } } template class MatrixPolicy> - requires(VectorizableDense>) void Process::UpdateState( - const std::vector& processes, - State& state) + requires(VectorizableDense>) + void Process::UpdateState(const std::vector& processes, State& state) { const auto& v_custom_parameters = state.custom_rate_parameters_.AsVector(); auto& v_rate_constants = state.rate_constants_.AsVector(); @@ -133,8 +133,13 @@ namespace micm params[i_param] = v_custom_parameters[offset_params + i_param * L + i_cell]; } std::vector::const_iterator custom_parameters_iter = params.begin(); + double fixed_reactants = 1.0; + for (auto& reactant : process.reactants_) + if (reactant.IsParameterized()) + fixed_reactants *= reactant.parameterize_(state.conditions_[i_group * L + i_cell]); v_rate_constants[offset_rc + i_cell] = - process.rate_constant_->calculate(state.conditions_[i_group * L + i_cell], custom_parameters_iter); + process.rate_constant_->calculate(state.conditions_[i_group * L + i_cell], custom_parameters_iter) * + fixed_reactants; } offset_params += params.size() * L; offset_rc += L; diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 74e04eb64..39d1e5126 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -47,12 +47,13 @@ namespace micm /// @param state_variables Current state variable values (grid cell, state variable) /// @param forcing Forcing terms for each state variable (grid cell, state variable) template typename MatrixPolicy> - requires(!VectorizableDense>) void AddForcingTerms( + requires(!VectorizableDense>) + void AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, MatrixPolicy& forcing) const; template typename MatrixPolicy> - requires VectorizableDense> + requires VectorizableDense> void AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, @@ -63,12 +64,14 @@ namespace micm /// @param state_variables Current state variable values (grid cell, state variable) /// @param jacobian Jacobian matrix for the system (grid cell, dependent variable, independent variable) template class MatrixPolicy, template class SparseMatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) void AddJacobianTerms( + requires(!VectorizableDense> || !VectorizableSparse>) + void AddJacobianTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const; template class MatrixPolicy, template class SparseMatrixPolicy> - requires(VectorizableDense>&& VectorizableSparse>) void AddJacobianTerms( + requires(VectorizableDense> && VectorizableSparse>) + void AddJacobianTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const; @@ -84,17 +87,25 @@ namespace micm { for (auto& process : processes) { - number_of_reactants_.push_back(process.reactants_.size()); - number_of_products_.push_back(process.products_.size()); + std::size_t number_of_reactants = 0; + std::size_t number_of_products = 0; for (auto& reactant : process.reactants_) { + if (reactant.IsParameterized()) + continue; // Skip reactants that are parameterizations reactant_ids_.push_back(state.variable_map_.at(reactant.name_)); + ++number_of_reactants; } for (auto& product : process.products_) { + if (product.first.IsParameterized()) + continue; // Skip products that are parameterizations product_ids_.push_back(state.variable_map_.at(product.first.name_)); yields_.push_back(product.second); + ++number_of_products; } + number_of_reactants_.push_back(number_of_reactants); + number_of_products_.push_back(number_of_products); } }; @@ -147,7 +158,8 @@ namespace micm } template typename MatrixPolicy> - requires(!VectorizableDense>) inline void ProcessSet::AddForcingTerms( + requires(!VectorizableDense>) + inline void ProcessSet::AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, MatrixPolicy& forcing) const @@ -183,7 +195,7 @@ namespace micm }; template typename MatrixPolicy> - requires VectorizableDense> + requires VectorizableDense> inline void ProcessSet::AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, @@ -224,12 +236,11 @@ namespace micm } template class MatrixPolicy, template class SparseMatrixPolicy> - requires( - !VectorizableDense> || !VectorizableSparse>) inline void ProcessSet:: - AddJacobianTerms( - const MatrixPolicy& rate_constants, - const MatrixPolicy& state_variables, - SparseMatrixPolicy& jacobian) const + requires(!VectorizableDense> || !VectorizableSparse>) + inline void ProcessSet::AddJacobianTerms( + const MatrixPolicy& rate_constants, + const MatrixPolicy& state_variables, + SparseMatrixPolicy& jacobian) const { auto cell_jacobian = jacobian.AsVector().begin(); @@ -271,11 +282,11 @@ namespace micm } template class MatrixPolicy, template class SparseMatrixPolicy> - requires(VectorizableDense>&& VectorizableSparse>) inline void ProcessSet:: - AddJacobianTerms( - const MatrixPolicy& rate_constants, - const MatrixPolicy& state_variables, - SparseMatrixPolicy& jacobian) const + requires(VectorizableDense> && VectorizableSparse>) + inline void ProcessSet::AddJacobianTerms( + const MatrixPolicy& rate_constants, + const MatrixPolicy& state_variables, + SparseMatrixPolicy& jacobian) const { const auto& v_rate_constants = rate_constants.AsVector(); const auto& v_state_variables = state_variables.AsVector(); diff --git a/include/micm/system/phase.hpp b/include/micm/system/phase.hpp index 00ee36053..3928a5c2e 100644 --- a/include/micm/system/phase.hpp +++ b/include/micm/system/phase.hpp @@ -49,5 +49,21 @@ namespace micm species_ = other.species_; return *this; } + + /// @brief Returns the number of non-parameterized species + std::size_t StateSize() const + { + return std::count_if(species_.begin(), species_.end(), [](const Species& s) { return !s.IsParameterized(); }); + } + + /// @brief Returns a set of unique names for each non-parameterized species + std::vector UniqueNames() const + { + std::vector names{}; + for (const auto& species : species_) + if (!species.IsParameterized()) + names.push_back(species.name_); + return names; + } }; } // namespace micm \ No newline at end of file diff --git a/include/micm/system/species.hpp b/include/micm/system/species.hpp index e64adcef4..912ac2de8 100644 --- a/include/micm/system/species.hpp +++ b/include/micm/system/species.hpp @@ -3,9 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include +#include "conditions.hpp" + namespace micm { @@ -19,6 +22,12 @@ namespace micm /// @brief A list of properties of this species std::map properties_; + /// @brief A function that if provided will be used to parameterize + /// the concentration of this species during solving. + /// Species with this function defined will be excluded from + /// the solver state. + std::function parameterize_{ nullptr }; + /// @brief Copy assignment /// @param other species to copy Species& operator=(const Species& other); @@ -35,6 +44,9 @@ namespace micm /// @param name The name of the species /// @param properties The properties of the species Species(const std::string& name, const std::map& properties); + + /// @brief Returns whether a species is parameterized + bool IsParameterized() const; }; inline Species& Species::operator=(const Species& other) @@ -46,13 +58,15 @@ namespace micm name_ = other.name_; properties_ = other.properties_; // This performs a shallow copy + parameterize_ = other.parameterize_; return *this; } inline Species::Species(const Species& other) : name_(other.name_), - properties_(other.properties_){}; + properties_(other.properties_), + parameterize_(other.parameterize_){}; inline Species::Species(const std::string& name) : name_(name){}; @@ -61,4 +75,9 @@ namespace micm : name_(name), properties_(properties){}; + inline bool Species::IsParameterized() const + { + return parameterize_ != nullptr; + } + } // namespace micm diff --git a/include/micm/system/system.hpp b/include/micm/system/system.hpp index 843345617..c0b881b0f 100644 --- a/include/micm/system/system.hpp +++ b/include/micm/system/system.hpp @@ -92,10 +92,10 @@ namespace micm inline size_t System::StateSize() const { - size_t state_size = gas_phase_.species_.size(); + size_t state_size = gas_phase_.StateSize(); for (const auto& phase : phases_) { - state_size += phase.second.species_.size(); + state_size += phase.second.StateSize(); } return state_size; } @@ -108,17 +108,11 @@ namespace micm inline std::vector System::UniqueNames( const std::function& variables, const std::size_t i)> f) const { - std::vector names{}; - for (auto& species : gas_phase_.species_) - { - names.push_back(species.name_); - } + std::vector names = gas_phase_.UniqueNames(); for (auto& phase : phases_) { - for (auto& species : phase.second.species_) - { - names.push_back(phase.first + "." + species.name_); - } + for (auto& species_name : phase.second.UniqueNames()) + names.push_back(phase.first + "." + species_name); } if (f) { diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index 8a1b88a45..8e29b1752 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -14,11 +14,14 @@ template class MatrixPolicy> void testProcessUpdateState(const std::size_t number_of_grid_cells) { micm::Species foo("foo", { { "molecular weight [kg mol-1]", 0.025 }, { "diffusion coefficient [m2 s-1]", 2.3e2 } }); + micm::Species bar("bar"); + bar.parameterize_ = [](const micm::Conditions& c) { return c.air_density_ * 0.82; }; + micm::ArrheniusRateConstant rc1({ .A_ = 12.2, .C_ = 300.0 }); micm::SurfaceRateConstant rc2({ .label_ = "foo_surf", .species_ = foo }); micm::UserDefinedRateConstant rc3({ .label_ = "bar_user" }); - micm::Process r1 = micm::Process::create().rate_constant(rc1); + micm::Process r1 = micm::Process::create().rate_constant(rc1).reactants({ foo, bar }); micm::Process r2 = micm::Process::create().rate_constant(rc2); micm::Process r3 = micm::Process::create().rate_constant(rc3); std::vector processes = { r1, r2, r3 }; @@ -39,6 +42,7 @@ void testProcessUpdateState(const std::size_t number_of_grid_cells) { state.conditions_[i_cell].temperature_ = get_double() * 285.0; state.conditions_[i_cell].pressure_ = get_double() * 101100.0; + state.conditions_[i_cell].air_density_ = get_double() * 10.0; params[0] = get_double() * 1.0e-8; params[1] = get_double() * 1.0e5; params[2] = get_double() * 1.0e-2; @@ -48,7 +52,8 @@ void testProcessUpdateState(const std::size_t number_of_grid_cells) params[1]; state.custom_rate_parameters_[i_cell][state.custom_rate_parameter_map_["bar_user"]] = params[2]; std::vector::const_iterator param_iter = params.begin(); - expected_rate_constants[i_cell][0] = rc1.calculate(state.conditions_[i_cell], param_iter); + expected_rate_constants[i_cell][0] = + rc1.calculate(state.conditions_[i_cell], param_iter) * (state.conditions_[i_cell].air_density_ * 0.82); param_iter += rc1.SizeCustomParameters(); expected_rate_constants[i_cell][1] = rc2.calculate(state.conditions_[i_cell], param_iter); param_iter += rc2.SizeCustomParameters(); diff --git a/test/unit/process/test_process_set_policy.hpp b/test/unit/process/test_process_set_policy.hpp index 1e050b006..38cb54ed4 100644 --- a/test/unit/process/test_process_set_policy.hpp +++ b/test/unit/process/test_process_set_policy.hpp @@ -21,8 +21,10 @@ void testProcessSet( auto baz = micm::Species("baz"); auto quz = micm::Species("quz"); auto quuz = micm::Species("quuz"); + auto qux = micm::Species("qux"); + qux.parameterize_ = [](const micm::Conditions& c) { return c.air_density_ * 0.72; }; - micm::Phase gas_phase{ std::vector{ foo, bar, baz, quz, quuz } }; + micm::Phase gas_phase{ std::vector{ foo, bar, qux, baz, quz, quuz } }; micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz", "quuz" }, .number_of_grid_cells_ = 2, @@ -32,7 +34,7 @@ void testProcessSet( micm::Process::create().reactants({ foo, baz }).products({ yields(bar, 1), yields(quuz, 2.4) }).phase(gas_phase); micm::Process r2 = - micm::Process::create().reactants({ bar }).products({ yields(foo, 1), yields(quz, 1.4) }).phase(gas_phase); + micm::Process::create().reactants({ bar, qux }).products({ yields(foo, 1), yields(quz, 1.4) }).phase(gas_phase); micm::Process r3 = micm::Process::create().reactants({ quz }).products({}).phase(gas_phase); diff --git a/test/unit/system/test_phase.cpp b/test/unit/system/test_phase.cpp index 284433129..0565af37d 100644 --- a/test/unit/system/test_phase.cpp +++ b/test/unit/system/test_phase.cpp @@ -8,4 +8,26 @@ TEST(Phase, ConstructorWithVector) micm::Phase phase(std::vector({ micm::Species("species1"), micm::Species("species2") })); EXPECT_EQ(phase.species_.size(), 2); + EXPECT_EQ(phase.StateSize(), 2); + + auto names = phase.UniqueNames(); + EXPECT_EQ(names[0], "species1"); + EXPECT_EQ(names[1], "species2"); +} + +TEST(Phase, ConstructorWithParameterizedSpecies) +{ + auto foo = micm::Species("foo"); + auto bar = micm::Species("bar"); + auto baz = micm::Species("baz"); + + bar.parameterize_ = [](const micm::Conditions& c) { return 42.0; }; + micm::Phase phase(std::vector({ foo, bar, baz })); + + EXPECT_EQ(phase.species_.size(), 3); + EXPECT_EQ(phase.StateSize(), 2); + + auto names = phase.UniqueNames(); + EXPECT_EQ(names[0], "foo"); + EXPECT_EQ(names[1], "baz"); } \ No newline at end of file diff --git a/test/unit/system/test_species.cpp b/test/unit/system/test_species.cpp index a4f4fcc51..5adff1b30 100644 --- a/test/unit/system/test_species.cpp +++ b/test/unit/system/test_species.cpp @@ -5,8 +5,11 @@ TEST(Species, StringConstructor) { micm::Species species("thing"); - EXPECT_EQ(species.name_, "thing"); + EXPECT_FALSE(species.IsParameterized()); + species.parameterize_ = [](const micm::Conditions& c) { return c.temperature_ * 100.0; }; + EXPECT_TRUE(species.IsParameterized()); + EXPECT_EQ(species.parameterize_({ .temperature_ = 12.5 }), 12.5 * 100.0); } TEST(Species, StringAndVectorConstructor) @@ -21,24 +24,56 @@ TEST(Species, StringAndVectorConstructor) TEST(Species, CopyConstructor) { - micm::Species species("thing", { { "name [units]", 1.0 }, { "name2 [units2]", 2.0 } }); + { + micm::Species species("thing", { { "name [units]", 1.0 }, { "name2 [units2]", 2.0 } }); + + micm::Species species2(species); + + EXPECT_EQ(species2.name_, "thing"); + EXPECT_EQ(species2.properties_.size(), 2); + EXPECT_EQ(species2.properties_["name [units]"], 1.0); + EXPECT_EQ(species2.properties_["name2 [units2]"], 2.0); + EXPECT_FALSE(species2.IsParameterized()); + } + { + micm::Species species("thing", { { "name [units]", 1.0 }, { "name2 [units2]", 2.0 } }); + species.parameterize_ = [](const micm::Conditions& c) { return 15.4; }; - micm::Species species2(species); + micm::Species species2(species); - EXPECT_EQ(species2.name_, "thing"); - EXPECT_EQ(species2.properties_.size(), 2); - EXPECT_EQ(species2.properties_["name [units]"], 1.0); - EXPECT_EQ(species2.properties_["name2 [units2]"], 2.0); + EXPECT_EQ(species2.name_, "thing"); + EXPECT_EQ(species2.properties_.size(), 2); + EXPECT_EQ(species2.properties_["name [units]"], 1.0); + EXPECT_EQ(species2.properties_["name2 [units2]"], 2.0); + EXPECT_TRUE(species2.IsParameterized()); + EXPECT_EQ(species.parameterize_({}), 15.4); + } } TEST(Species, CopyAssignment) { - micm::Species species("thing", { { "name [units]", 1.0 }, { "name2 [units2]", 2.0 } }); + { + micm::Species species("thing", { { "name [units]", 1.0 }, { "name2 [units2]", 2.0 } }); + + micm::Species species2 = species; + + EXPECT_EQ(species2.name_, "thing"); + EXPECT_EQ(species2.properties_.size(), 2); + EXPECT_EQ(species2.properties_["name [units]"], 1.0); + EXPECT_EQ(species2.properties_["name2 [units2]"], 2.0); + EXPECT_FALSE(species2.IsParameterized()); + } + { + micm::Species species("thing", { { "name [units]", 1.0 }, { "name2 [units2]", 2.0 } }); + species.parameterize_ = [](const micm::Conditions& c) { return 15.4; }; - micm::Species species2 = species; + micm::Species species2 = species; - EXPECT_EQ(species2.name_, "thing"); - EXPECT_EQ(species2.properties_.size(), 2); - EXPECT_EQ(species2.properties_["name [units]"], 1.0); - EXPECT_EQ(species2.properties_["name2 [units2]"], 2.0); + EXPECT_EQ(species2.name_, "thing"); + EXPECT_EQ(species2.properties_.size(), 2); + EXPECT_EQ(species2.properties_["name [units]"], 1.0); + EXPECT_EQ(species2.properties_["name2 [units2]"], 2.0); + EXPECT_TRUE(species2.IsParameterized()); + EXPECT_EQ(species.parameterize_({}), 15.4); + } } diff --git a/test/unit/system/test_system.cpp b/test/unit/system/test_system.cpp index 750a17b38..ec60f3a20 100644 --- a/test/unit/system/test_system.cpp +++ b/test/unit/system/test_system.cpp @@ -27,6 +27,45 @@ TEST(System, ConstructorWithAllParameters) EXPECT_NE(std::find(names.begin(), names.end(), "phase2.species3"), names.end()); EXPECT_NE(std::find(names.begin(), names.end(), "phase2.species4"), names.end()); + std::vector reorder{ 3, 2, 1, 0, 5, 4 }; + auto reordered_names = system.UniqueNames([&](const std::vector variables, const std::size_t i) + { return variables[reorder[i]]; }); + EXPECT_EQ(reordered_names.size(), 6); + EXPECT_EQ(reordered_names[0], names[3]); + EXPECT_EQ(reordered_names[1], names[2]); + EXPECT_EQ(reordered_names[2], names[1]); + EXPECT_EQ(reordered_names[3], names[0]); + EXPECT_EQ(reordered_names[4], names[5]); + EXPECT_EQ(reordered_names[5], names[4]); +} + +TEST(System, ConstructorWithParameterizedSpecies) +{ + std::vector speciesA = { micm::Species("species1"), micm::Species("species2") }; + std::vector speciesB = { micm::Species("species3"), micm::Species("species4") }; + auto param_species = micm::Species("paramSpecies"); + param_species.parameterize_ = [](const micm::Conditions& c) { return 64.2; }; + speciesA.push_back(param_species); + + micm::Phase phase = speciesA; + std::unordered_map phases = { { "phase1", speciesA }, { "phase2", speciesB } }; + + micm::System system = { micm::SystemParameters{ .gas_phase_ = phase, .phases_ = phases } }; + + EXPECT_EQ(system.gas_phase_.species_.size(), 3); + EXPECT_EQ(system.phases_.size(), 2); + EXPECT_EQ(system.StateSize(), 6); + + auto names = system.UniqueNames(); + + EXPECT_EQ(names.size(), 6); + EXPECT_NE(std::find(names.begin(), names.end(), "species1"), names.end()); + EXPECT_NE(std::find(names.begin(), names.end(), "species2"), names.end()); + EXPECT_NE(std::find(names.begin(), names.end(), "phase1.species1"), names.end()); + EXPECT_NE(std::find(names.begin(), names.end(), "phase1.species2"), names.end()); + EXPECT_NE(std::find(names.begin(), names.end(), "phase2.species3"), names.end()); + EXPECT_NE(std::find(names.begin(), names.end(), "phase2.species4"), names.end()); + std::vector reorder{ 3, 2, 1, 0, 5, 4 }; auto reordered_names = system.UniqueNames([&](const std::vector variables, const std::size_t i) { return variables[reorder[i]]; }); From 800d96ef26636525fa16f0ce02dadd5d9766aa25 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 12 Oct 2023 11:08:24 -0500 Subject: [PATCH 103/318] added all icons --- .../assets/analysis-microchip-svgrepo-com.svg | 85 +++++ .../icons/assets/chemistry-svgrepo-com.svg | 34 ++ .../assets/flask-chemistry-svgrepo-com.svg | 52 +++ .../icons/assets/microchip-svgrepo-com.svg | 48 +++ docs/source/_static/icons/m.svg | 61 ++++ docs/source/_static/icons/micm-1.png | Bin 0 -> 48026 bytes docs/source/_static/icons/micm-1.svg | 138 ++++++++ docs/source/_static/icons/micm-2.png | Bin 0 -> 87929 bytes docs/source/_static/icons/micm-2.svg | 77 ++++ docs/source/_static/icons/micm-3.png | Bin 0 -> 55369 bytes docs/source/_static/icons/micm-3.svg | 66 ++++ docs/source/_static/icons/micm-4.png | Bin 0 -> 52047 bytes docs/source/_static/icons/micm-4.svg | 329 ++++++++++++++++++ docs/source/_static/{ => icons}/micm.png | Bin docs/source/_static/{ => icons}/micm.svg | 0 15 files changed, 890 insertions(+) create mode 100644 docs/source/_static/icons/assets/analysis-microchip-svgrepo-com.svg create mode 100644 docs/source/_static/icons/assets/chemistry-svgrepo-com.svg create mode 100644 docs/source/_static/icons/assets/flask-chemistry-svgrepo-com.svg create mode 100644 docs/source/_static/icons/assets/microchip-svgrepo-com.svg create mode 100644 docs/source/_static/icons/m.svg create mode 100644 docs/source/_static/icons/micm-1.png create mode 100644 docs/source/_static/icons/micm-1.svg create mode 100644 docs/source/_static/icons/micm-2.png create mode 100644 docs/source/_static/icons/micm-2.svg create mode 100644 docs/source/_static/icons/micm-3.png create mode 100644 docs/source/_static/icons/micm-3.svg create mode 100644 docs/source/_static/icons/micm-4.png create mode 100644 docs/source/_static/icons/micm-4.svg rename docs/source/_static/{ => icons}/micm.png (100%) rename docs/source/_static/{ => icons}/micm.svg (100%) diff --git a/docs/source/_static/icons/assets/analysis-microchip-svgrepo-com.svg b/docs/source/_static/icons/assets/analysis-microchip-svgrepo-com.svg new file mode 100644 index 000000000..19c6339fd --- /dev/null +++ b/docs/source/_static/icons/assets/analysis-microchip-svgrepo-com.svg @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/source/_static/icons/assets/chemistry-svgrepo-com.svg b/docs/source/_static/icons/assets/chemistry-svgrepo-com.svg new file mode 100644 index 000000000..87a65dfd6 --- /dev/null +++ b/docs/source/_static/icons/assets/chemistry-svgrepo-com.svg @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/docs/source/_static/icons/assets/flask-chemistry-svgrepo-com.svg b/docs/source/_static/icons/assets/flask-chemistry-svgrepo-com.svg new file mode 100644 index 000000000..6bf69354e --- /dev/null +++ b/docs/source/_static/icons/assets/flask-chemistry-svgrepo-com.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/source/_static/icons/assets/microchip-svgrepo-com.svg b/docs/source/_static/icons/assets/microchip-svgrepo-com.svg new file mode 100644 index 000000000..280671ff8 --- /dev/null +++ b/docs/source/_static/icons/assets/microchip-svgrepo-com.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/source/_static/icons/m.svg b/docs/source/_static/icons/m.svg new file mode 100644 index 000000000..4ab235986 --- /dev/null +++ b/docs/source/_static/icons/m.svg @@ -0,0 +1,61 @@ + + + + diff --git a/docs/source/_static/icons/micm-1.png b/docs/source/_static/icons/micm-1.png new file mode 100644 index 0000000000000000000000000000000000000000..f02b19106876958f7a7b71a0d06444d947b1631a GIT binary patch literal 48026 zcmeFZcR1C5{6BmsltN`@RumDk=RuKK_B>XRdCZK1W2Q*;Cip=l9Rul}?BAevRjTJf6=AQCC$UBc&&WKp@jgtkB z<2$RQ4M}0lZuyO8V>ky6xXmCGKLEJMy7MfJqsL4k2X z?rV+r1cv{*8h=R#VNAaHmj{*Wm%J&tc^&q~LSq{>_E7APQ;@7`M2gFT(PT^!R|oP` z!HvR7lxbzkt6xmSTUsXYN2*l+Npk8&;Ysq2R6m@t<8~BB7+*N*Tpd@1T5-JCEzeF< zJ{F0Xzj+dqL|f;#VLBmw(0}G~@z~=+ktc}oYRGrGopIE;rY08Z+nJa>?Z&v&IAyWG zw2^i~``-sAE+!xDov|LN``^1yXP)|Xw=nzH72oBW>^!Cp&+ff!$u_Xs+-O}$Em)?l%Y|(?oLQj^?4}!oSUsleelR0E=9fa|fpu94h zibtK32}HL4_lL56fHjqzFBDA(S|}m=GuAhU8LLu1`qf?hb-w5fJc(pJNR!k&JCDxh zkcqlRYF3x~(~IVO_B^ZOjGbab+H1mZTXFA10hI}P_TbY(zYYJ{=;Ar1{*j6E#gq|c zS)HF_X^38d?}eh~>3t%g*O|Mdx4;V|H3LHM|G)kJrSbp47_J|sK<-5HJ<`$)%(hi} zSnEfn()IN%8@0Ykr9cq;ynt`D_^c0PC9Ov1hwt0N&-Al&p2wg zjLj$QIU8EtpDkhhOYylP?tBQgUo(IoZB(h?+sV3c?I%WCw;Mc&#vBH9aV?a=NVDp_xD-BkI(NDIbQgI z#UYFehK7b?Q&YJ8{fL^{S~MD65FSp(&C6|T*FiMG3$bySM`4t5Q=smKot}b`;>)-@tj2#0#yI+HXi3P>^ixt`Cu>8R6I-HX?>u)n| zWI{r;u<>T=s=gk--Pf9$ywM0|ZF^%(*j7#?Fs*EVG+JlR)pYomOd7a}eQ>08E&Cre zdy*g^%DE`MTq7J8Dq)ARfE-2@OB?>h9;G8Zdp+*(gqZ~4WLbFy^x z-CG;!kPu32{2iq-6>)1;6C0a^2qr&8&!Z#Z@4%<@MI;6DRcQNc%*`itfBG$p_FY(V z(M>!fxI+@gS3l;nGk~7f2Rj8vx)$Le}^$a2D zt+c!5=SH~AO%c^s_9+o*-XCPIf~$N_OijfFZ?r39_#ghH z+nH85ZrNv8`Z>UZu0g;sGOcYY5n2liK@gP`;|-qXW-S?0Uv{(SMTaO@o~?1nc)t#N{KZvQ z)2)~fIGQSSUkq6En95C{8dbRmw^N{J0l0Gu`_Cyz@)!sLaDtgNdWyLaB`X!FL` zw}1F5AptcNO+j6qT>HZu&a!-M4yt#2-KGt$v&mi!W$Wy1)!D2O^ZH49p(7gp9zW>@ zIhUKVi}gz>t`d69?Wh%07f(2tfty(XTm8FK)?AFo$N02gCk`ykFV=K>l~Pz(M18Zt zFwA?m6-j@0YS_J)MKoduUbB5EDM_<1K9ot!?#r;@)mg2`qTHH32ea504{n!~mRg(p z7&9=e!VC=u_xEdRgb~P=nwr`_a)$yW5JkxhC;j?*Pq2r!rRB^1JM-V`t5|H(%Z6=x zs;gavglqG2a$em{NYFK(z8xaBJ0;<`QOkG65HWv>DM`M?Kzb{Z&K$&#=?<^6fl^9m zM8gpdm$_(Z<+eKXK9Z*M61<>g@x4& zt;mA(^k23U%gc{3d&lsKjfPK(UL(%Q0=i$mr)v1u_0YM<;E0qo5SAaGzk%-LEPefw z1edNj(TBl)|NE6LA4H6BHITiv{eDcIEB{@#@(>63yyI58msleo8b z$$E%fgZ1E*xt4&u!-tk?{dUckE)9O1&r@pD$V2XZMD$Ig3hFs<>uVZBoRqN zN{GLlJJ67@h)7Iq>>`2_iAstqlew1;!trQgw4xj>Z}Q;jX_Kmych^{HL_}X;VnQnJ z_uH3W+|u~5O@BrERCH0#wC@zTaJJH!GsjR3nnz=EY5E_Tghz(2p!H0Lfa;Rxu0Z@b z*0X3^T{6@aV|4J%#ZYnn?AqzmkH88`+61tFGZdJ$U@))Z`{y}I*~F=A&k5#AFQBoZ zxIqEezd!lL>%Fn36UkGD?fnica)L|8UO*bG(yAoWk%7UHoK&PImHR2oUrV?pwCaG- zfJX-Od8O}%xIFWgA(%srJ4!dxb)$nLnO*jDHWn9i`@DCXeYeKcKmuGC69;QGzJ8kB zQ`{y_$tEJA775+*oURGy?&)8hteyVV*B24vK^ET2$;lJ4w|&vBeCHD>(I@0NM{KCF zva-(fueTC$k(W8N-}l&@jEvmL1Qa(X+8PE4PF4Ymnl=XYsXpgLE0Ru%L?2(Iw%kqt z5+vEgm@ia);*&S-P{WFoAJy>%S-M8@5p@$`kws7x<*c-Vk|sh8?etO8yMLI16< zu%))c%MJq3)VO!+*Wl$(j`)~%sN;a@7DR8Cl$6?9SiDeC2~A8(!mq7`2@egIlp+59 z(*Sw1`p)X<=-|p`=(0;+B!F@Q%6CR6b6?lb`>B6z53U~f*pT0<00qjyw2#El@{x0zqJ zK^=g_nQk}nuZ;f3URoo}8&qG^)NyO)9Gtqo&jKndE6wc*uK>=WjV>z)YpJ8HyV|3$xw;r0 z5^uieZmK0KJJ?L|T2)O=Pu9HZ*eh%5mCl1@E>v@E+0}CPCW4BebXxaqUTW<7X%GKi z`E8z7725$~xyi**Q3Lv^BljYcfx##!TXZ~Mv@SrBMLJr3}(xv*>edY z{%r%evC{PtZsU4yz7;rAhlbjL5U3LVHSgp*T8HI8I#Iis*)7uu!aClD{3m=^DrGOp zvDFoN`jUn6=9*j=E2#-YGDuiVV(v!$n14d(8aH)sfJC6HqE3ob%qn|Wku>rKDr4k*&&Y3DyV*0Nn)*<0J;Eku_Do|8i$ zx4uwPKr@Sr*YeB8nYJVvHuu8$3QLM27jR2qeBM&-cUxNbC27}*fJ+SZon40TU$6ao zhJ&Bqhi?C+^g8@KudLQzj-rG>6kh=dq0p$>=DPm(XL$_c3PYkJi zqp=&d_RVK?-WnCEg56zxLt6ckMTui0CUJxi$Z28W3Vlp$jDpYP?(b}2;e9_w1ruN2 z;@}tZX9?s38IX!^bXx2M!egslT%0AvEfp0RAzKykrM*2pxTQ32dC$jQiJ)+CQd6+n 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 + + + diff --git a/docs/source/_static/icons/micm-2.png b/docs/source/_static/icons/micm-2.png new file mode 100644 index 0000000000000000000000000000000000000000..abc72abbcb4a3145459a239a775bd83576c98c86 GIT binary patch literal 87929 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 iA%qY@2qA<- + + + diff --git a/docs/source/_static/icons/micm-3.png b/docs/source/_static/icons/micm-3.png new file mode 100644 index 0000000000000000000000000000000000000000..7308a7905e1a06a95f3f354429c6caeda83d40ae GIT binary patch literal 55369 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 literal 0 HcmV?d00001 diff --git a/docs/source/_static/icons/micm-3.svg b/docs/source/_static/icons/micm-3.svg new file mode 100644 index 000000000..d98451454 --- /dev/null +++ b/docs/source/_static/icons/micm-3.svg @@ -0,0 +1,66 @@ + + + + diff --git a/docs/source/_static/icons/micm-4.png b/docs/source/_static/icons/micm-4.png new file mode 100644 index 0000000000000000000000000000000000000000..16f1f804d1088377889c310237ebf47e619041aa GIT binary patch literal 52047 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 Xv2pT}yb0d22SK+rbbw^hHthca3kR*k literal 0 HcmV?d00001 diff --git a/docs/source/_static/icons/micm-4.svg b/docs/source/_static/icons/micm-4.svg new file mode 100644 index 000000000..be6cc841e --- /dev/null +++ b/docs/source/_static/icons/micm-4.svg @@ -0,0 +1,329 @@ + + + + diff --git a/docs/source/_static/micm.png b/docs/source/_static/icons/micm.png similarity index 100% rename from docs/source/_static/micm.png rename to docs/source/_static/icons/micm.png diff --git a/docs/source/_static/micm.svg b/docs/source/_static/icons/micm.svg similarity index 100% rename from docs/source/_static/micm.svg rename to docs/source/_static/icons/micm.svg From 88d6b5b337f0d9521d252df87448b14456fc6859 Mon Sep 17 00:00:00 2001 From: David Fillmore <1524012+dwfncar@users.noreply.github.com> Date: Thu, 12 Oct 2023 10:50:55 -0600 Subject: [PATCH 104/318] Test kpp micm (#295) * Start on test_kpp_to_micm program. * Start on CMakeLists for test_kpp_to_micm. * Added kpp test subdir. * Renamed test. * Set config path relative to build dir. * Added kpp_to_micm.py generated JSON. * Copy kpp_to_micm JSON to builds/configs for testing. * Check ConfigParseStatus. * Include regression/RosenbrockChapman/chapman_ode_solver. * Instantiate micm::RosenbrockSolver. * Get solver state and initialize T and p. * Added initial concentrations for Chapman test from Seinfeld and Pandis. * Added solver.Solve iteration. * Added --to_si_units option to kpp_to_micm.py. * Updated Chapman reactions JSON to SI units. * Use SI units. * Use SI units. * Added print_state to test_kpp_to_micm. * Improved print_state format. * Made a KPP config for Chapman without NOx, for comparison to RosenbrockChapman. * Removed NOx from Chapman KPP test. * Added M to print_state. * Set photolysis rates. * Print custom param labels. * Use state.SetCustomRateParameter to set photolysis rates. * Use state.SetConcentration for each species. * Changed print precision to 3 decimal places. * Changed print precision to 3 decimal places. * Set initial O3 concentration to 0. * Cleaned up comments. * Change to SI conversion factor to (N_Avogradro 1e-6)^(N_reactants-1). --- etc/configs/kpp/chapman.eqn | 10 ++ etc/configs/kpp/chapman.spc | 11 ++ etc/scripts/kpp_to_micm.py | 39 ++++-- test/CMakeLists.txt | 1 + test/kpp/CMakeLists.txt | 21 +++ test/kpp/configs/kpp_chapman/reactions.json | 97 ++++++++++++++ test/kpp/configs/kpp_chapman/species.json | 29 ++++ test/kpp/test_kpp_to_micm.cpp | 141 ++++++++++++++++++++ 8 files changed, 341 insertions(+), 8 deletions(-) create mode 100644 etc/configs/kpp/chapman.eqn create mode 100644 etc/configs/kpp/chapman.spc create mode 100644 test/kpp/CMakeLists.txt create mode 100644 test/kpp/configs/kpp_chapman/reactions.json create mode 100644 test/kpp/configs/kpp_chapman/species.json create mode 100644 test/kpp/test_kpp_to_micm.cpp diff --git a/etc/configs/kpp/chapman.eqn b/etc/configs/kpp/chapman.eqn new file mode 100644 index 000000000..c15640dd5 --- /dev/null +++ b/etc/configs/kpp/chapman.eqn @@ -0,0 +1,10 @@ +#EQUATIONS { Small Stratospheric Mechanism } + + O2 + hv = 2O : (2.643E-10) * SUN*SUN*SUN; + O + O2 = O3 : (8.018E-17); + O3 + hv = O + O2 : (6.120E-04) * SUN; + O + O3 = 2O2 : (1.576E-15); + O3 + hv = O1D + O2 : (1.070E-03) * SUN*SUN; + O1D + M = O + M : (7.110E-11); + O1D + O3 = 2O2 : (1.200E-10); + diff --git a/etc/configs/kpp/chapman.spc b/etc/configs/kpp/chapman.spc new file mode 100644 index 000000000..0dab2f343 --- /dev/null +++ b/etc/configs/kpp/chapman.spc @@ -0,0 +1,11 @@ +#INCLUDE atoms.kpp + +#DEFVAR +O = O; { Oxygen atomic ground state } +O1D = O; { Oxygen atomic excited state } +O3 = O + O + O; { Ozone } + +#DEFFIX +M = O + O + N + N; { Atmospheric generic molecule } +O2 = O + O; { Molecular oxygen } + diff --git a/etc/scripts/kpp_to_micm.py b/etc/scripts/kpp_to_micm.py index 004ac88ea..956f88151 100644 --- a/etc/scripts/kpp_to_micm.py +++ b/etc/scripts/kpp_to_micm.py @@ -31,6 +31,7 @@ Revision History: v1.00 2023/08/03 Initial implementation v1.01 2023/08/16 Added method parse_kpp_arrhenius + v1.02 2023/10/05 Added unit conversion option """ import os @@ -40,7 +41,12 @@ import json from glob import glob -__version__ = 'v1.01' +__version__ = 'v1.02' + +""" +Physical Constants +""" +N_Avogadro = 6.02214076e23 def read_kpp_config(kpp_dir, kpp_name): @@ -129,17 +135,21 @@ def micm_species_json(lines, fixed=False, tolerance=1.0e-12): return species_json -def parse_kpp_arrhenius(kpp_str): +def parse_kpp_arrhenius(kpp_str, to_si_units=False, + N_reactants=2): """ Parse KPP Arrhenius reaction Parameters (str) kpp_str: Arrhenius reaction string + (bool) to_si_units: convert A coefficient + from (cm^3 molecule-1)^(N-1) s-1 to (m^3 mol-1)^(N-1) s-1 + where N is the number of reactants + (int) N_reactants: number of reactants Returns (dict): MICM Arrhenius reaction coefficients - Arrhenius formula from KPP -------------------------- @@ -188,16 +198,20 @@ def parse_kpp_arrhenius(kpp_str): arr_dict['D'] = 300.0 else: logging.error('unrecognized KPP Arrhenius syntax') + if to_si_units: + arr_dict['A'] *= (N_Avogadro * 1.0e-6)**(N_reactants - 1) logging.debug(arr_dict) return arr_dict -def micm_equation_json(lines): +def micm_equation_json(lines, to_si_units=False): """ Generate MICM equation JSON Parameters (list of str) lines: lines of equation section + (bool) to_si_units: convert A coefficient + from cm^3 molecule-1 s-1 to m^3 mol-1 s-1 Returns (list of dict): list of MICM equation entries @@ -227,17 +241,22 @@ def micm_equation_json(lines): reactants = [reactant.strip().lstrip() for reactant in reactants] products = [product.strip().lstrip() for product in products] + N_reactants = len(reactants) + equation_dict = dict() if 'SUN' in coeffs: - equation_dict['type'] = 'PHOTOLYSIS' + equation_dict['type'] = 'PHOTOLYSIS' elif 'ARR' in coeffs: - equation_dict = parse_kpp_arrhenius(coeffs) + equation_dict = parse_kpp_arrhenius(coeffs, + to_si_units=to_si_units, N_reactants=N_reactants) else: # default to Arrhenius with a single coefficient coeffs = coeffs.replace('(', '').replace(')', '') - equation_dict['type'] = 'ARRHENIUS' + equation_dict['type'] = 'ARRHENIUS' equation_dict['A'] = float(coeffs) + if to_si_units: + equation_dict['A'] *= (N_Avogadro * 1.0e-6)**(N_reactants - 1) equation_dict['reactants'] = dict() equation_dict['products'] = dict() @@ -286,6 +305,9 @@ def micm_equation_json(lines): parser.add_argument('--mechanism', type=str, default='Chapman', help='mechanism name') + parser.add_argument('--to_si_units', action='store_true', + default=False, + help='convert units from molecules cm-3 to mol m-3') parser.add_argument('--debug', action='store_true', help='set logging level to debug') args = parser.parse_args() @@ -324,7 +346,8 @@ def micm_equation_json(lines): """ Generate MICM equations JSON from KPP #EQUATIONS section """ - equations_json = micm_equation_json(sections['#EQUATIONS']) + equations_json = micm_equation_json(sections['#EQUATIONS'], + to_si_units=args.to_si_units) """ Assemble MICM species JSON diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index cfc56e8ef..a87f8b2f7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -5,3 +5,4 @@ add_subdirectory(unit) add_subdirectory(regression) add_subdirectory(integration) add_subdirectory(tutorial) +add_subdirectory(kpp) diff --git a/test/kpp/CMakeLists.txt b/test/kpp/CMakeLists.txt new file mode 100644 index 000000000..420b18ca0 --- /dev/null +++ b/test/kpp/CMakeLists.txt @@ -0,0 +1,21 @@ +################################################################################ +# Test utilities + +include(test_util) + +################################################################################ +# Tests + +if(ENABLE_JSON) + create_standard_test(NAME kpp_to_micm SOURCES test_kpp_to_micm.cpp) +endif() + +################################################################################ + +################################################################################ +# Copy test data + +add_custom_target(copy_kpp_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) + +################################################################################ diff --git a/test/kpp/configs/kpp_chapman/reactions.json b/test/kpp/configs/kpp_chapman/reactions.json new file mode 100644 index 000000000..85129afe1 --- /dev/null +++ b/test/kpp/configs/kpp_chapman/reactions.json @@ -0,0 +1,97 @@ +{ + "camp-data": [ + { + "name": "Chapman", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "O2": {} + }, + "products": { + "O": { + "yield": 2.0 + } + }, + "MUSICA name": "R1" + }, + { + "type": "ARRHENIUS", + "A": 48.28552461368, + "reactants": { + "O": {}, + "O2": {} + }, + "products": { + "O3": {} + }, + "MUSICA name": "R2" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "O3": {} + }, + "products": { + "O": {}, + "O2": {} + }, + "MUSICA name": "R3" + }, + { + "type": "ARRHENIUS", + "A": 949.089383776, + "reactants": { + "O": {}, + "O3": {} + }, + "products": { + "O2": { + "yield": 2.0 + } + }, + "MUSICA name": "R4" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "O3": {} + }, + "products": { + "O1D": {}, + "O2": {} + }, + "MUSICA name": "R5" + }, + { + "type": "ARRHENIUS", + "A": 42817420.803600006, + "reactants": { + "O1D": {}, + "M": {} + }, + "products": { + "O": {}, + "M": {} + }, + "MUSICA name": "R6" + }, + { + "type": "ARRHENIUS", + "A": 72265689.12, + "reactants": { + "O1D": {}, + "O3": {} + }, + "products": { + "O2": { + "yield": 2.0 + } + }, + "MUSICA name": "R7" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/kpp/configs/kpp_chapman/species.json b/test/kpp/configs/kpp_chapman/species.json new file mode 100644 index 000000000..c962907ff --- /dev/null +++ b/test/kpp/configs/kpp_chapman/species.json @@ -0,0 +1,29 @@ +{ + "camp-data": [ + { + "name": "M", + "type": "CHEM_SPEC", + "tracer type": "CONSTANT" + }, + { + "name": "O2", + "type": "CHEM_SPEC", + "tracer type": "CONSTANT" + }, + { + "name": "O", + "type": "CHEM_SPEC", + "absolute tolerance": 1e-12 + }, + { + "name": "O1D", + "type": "CHEM_SPEC", + "absolute tolerance": 1e-12 + }, + { + "name": "O3", + "type": "CHEM_SPEC", + "absolute tolerance": 1e-12 + } + ] +} \ No newline at end of file diff --git a/test/kpp/test_kpp_to_micm.cpp b/test/kpp/test_kpp_to_micm.cpp new file mode 100644 index 000000000..bcff1d90a --- /dev/null +++ b/test/kpp/test_kpp_to_micm.cpp @@ -0,0 +1,141 @@ +#include +#include +#include +#include + +template +using SparseMatrixPolicy = micm::SparseMatrix; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(10) << "M" + << "," << std::setw(11) << "O2" + << "," << std::setw(11) << "O3" + << "," << std::setw(11) << "O" + << "," << std::setw(11) << "O1D" << std::endl; +} + +template class T> +void print_state(double time, micm::State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << "," << std::flush; + + std::cout << std::scientific << std::setw(10) << std::setprecision(3) + << state.variables_[0][state.variable_map_["M"]] + << "," << std::setw(11) << std::setprecision(3) + << state.variables_[0][state.variable_map_["O2"]] + << "," << std::setw(11) << std::setprecision(3) + << state.variables_[0][state.variable_map_["O3"]] + << "," << std::setw(11) << std::setprecision(3) + << state.variables_[0][state.variable_map_["O"]] + << "," << std::setw(11) << std::setprecision(3) + << state.variables_[0][state.variable_map_["O1D"]] << std::endl; + + std::cout.copyfmt(oldState); +} + +int main(const int argc, const char *argv[]) +{ + + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse( + "./configs/kpp_chapman"); + + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // Print reactions from reactions configuration + for (int i = 0; i < process_vector.size(); i++) { + + int n_reactants = process_vector[i].reactants_.size(); + for (int j = 0; j < n_reactants - 1; j++) { + std::cout << process_vector[i].reactants_[j].name_ << " + "; + } + std::cout << process_vector[i].reactants_[n_reactants - 1].name_; + + std::cout << " --> "; + + int n_products = process_vector[i].products_.size(); + for (int j = 0; j < n_products - 1; j++) { + std::cout << process_vector[i].products_[j].first.name_ << " + "; + } + std::cout << process_vector[i].products_[n_products - 1].first.name_; + + std::vector param_labels = process_vector[i].rate_constant_->CustomParameters(); + for (int k = 0; k < param_labels.size(); k++) { + std::cout << " " << param_labels[k]; + } + + std::cout << std::endl; + } + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + micm::RosenbrockSolver solver{ + chemical_system, reactions, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + + micm::State state = solver.GetState(); + + state.conditions_[0].temperature_ = 227.0; // K + state.conditions_[0].pressure_ = 1200.0; // Pa + + state.SetCustomRateParameter("PHOTO.R1", 6.0e-11); // s^-1 j_O2 + state.SetCustomRateParameter("PHOTO.R3", 1.0e-3); // s^-1 j_O3 + state.SetCustomRateParameter("PHOTO.R5", 1.0e-3); // s^-1 j_O3 + + // Define initial concentrations from Seinfeld & Pandis 3e + // and convert to SI units + double N_Avogadro = 6.02214076e23; + // molecules cm-3 -> mol m-3, z = 30 km, S&P3e table 5.1 p. 121 + double n_M = 3.1e17 * 1.0e6 / N_Avogadro; + double n_O2 = 0.21 * n_M; // [O2] ~ 0.21 [M] + // typical [O3] mid-latitude z ~ 30 km + double n_O3 = 2.0e12 * 1.0e6 / N_Avogadro; + // [O] / [O3] ~ 3e-5, S&P3e p. 124 + + micm::Species M("M"); + micm::Species O2("O2"); + micm::Species O3("O3"); + micm::Species O("O"); + micm::Species O1D("O1D"); + + state.SetConcentration(M, n_M); + state.SetConcentration(O2, n_O2); + state.SetConcentration(O3, n_O3); + state.SetConcentration(O, 0.0); + state.SetConcentration(O1D, 0.0); + + double time_step = 3600; // s + int nstep = 24; + + print_header(); + print_state(0, state); + + for (int i = 0; i < nstep; ++i) { + + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_[0] = result.result_.AsVector(); + } + print_state(time_step * (i + 1), state); + } + + return 0; +} From 63e26dfa16421a28277190b766133248929b8d10 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 12 Oct 2023 15:32:03 -0500 Subject: [PATCH 105/318] svg sources --- docs/source/_static/icons/assets/README.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 docs/source/_static/icons/assets/README.md diff --git a/docs/source/_static/icons/assets/README.md b/docs/source/_static/icons/assets/README.md new file mode 100644 index 000000000..ad6464ac5 --- /dev/null +++ b/docs/source/_static/icons/assets/README.md @@ -0,0 +1,7 @@ +URLs + +- [analysis microchip](https://www.svgrepo.com/svg/198352/analysis-microchip) +- [chemistry](https://www.svgrepo.com/svg/84257/chemistry) +- [flask chemistry](https://www.svgrepo.com/svg/200963/flask-chemistry) +- [chip microchip](https://www.svgrepo.com/svg/278922/chip-microchip) +- [microchip](https://www.svgrepo.com/svg/217555/microchip) \ No newline at end of file From 9d4a65184f23f371d998a22d1d5a827a77d1f771 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 12 Oct 2023 15:15:21 -0700 Subject: [PATCH 106/318] add terminator test --- include/micm/system/species.hpp | 9 +++ test/integration/CMakeLists.txt | 1 + test/integration/terminator.cpp | 42 ++++++++++++++ test/integration/terminator.hpp | 100 ++++++++++++++++++++++++++++++++ 4 files changed, 152 insertions(+) create mode 100644 test/integration/terminator.cpp create mode 100644 test/integration/terminator.hpp diff --git a/include/micm/system/species.hpp b/include/micm/system/species.hpp index 912ac2de8..f4b4f0dc1 100644 --- a/include/micm/system/species.hpp +++ b/include/micm/system/species.hpp @@ -47,6 +47,9 @@ namespace micm /// @brief Returns whether a species is parameterized bool IsParameterized() const; + + /// @brief Return a Species instance parameterized on air density + static Species ThirdBody(); }; inline Species& Species::operator=(const Species& other) @@ -80,4 +83,10 @@ namespace micm return parameterize_ != nullptr; } + inline Species Species::ThirdBody() + { + Species third_body{ "M" }; + third_body.parameterize_ = [](const Conditions& c) { return c.air_density_; }; + return third_body; + } } // namespace micm diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 463e70d86..77ccb7d86 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -8,6 +8,7 @@ include(test_util) create_standard_test(NAME chapman_integration SOURCES chapman.cpp) create_standard_test(NAME analytical_rosenbrock_integration SOURCES analytical_rosenbrock.cpp) +create_standard_test(NAME terminator_integration SOURCES terminator.cpp) if(ENABLE_LLVM) create_standard_test(NAME analytical_jit_rosenbrock_integration SOURCES analytical_jit_rosenbrock.cpp) diff --git a/test/integration/terminator.cpp b/test/integration/terminator.cpp new file mode 100644 index 000000000..07467b5be --- /dev/null +++ b/test/integration/terminator.cpp @@ -0,0 +1,42 @@ +#include "terminator.hpp" + +#include + +#include +#include +#include +#include + +TEST(RosenbrockSolver, Terminator) +{ + TestTerminator>(1); + TestTerminator>(2); + TestTerminator>(3); + TestTerminator>(4); +} + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; + +TEST(RosenbrockSolver, VectorTerminator) +{ + TestTerminator>(1); + TestTerminator>(4); + TestTerminator>(3); + TestTerminator>(2); +} \ No newline at end of file diff --git a/test/integration/terminator.hpp b/test/integration/terminator.hpp new file mode 100644 index 000000000..9a1c226c7 --- /dev/null +++ b/test/integration/terminator.hpp @@ -0,0 +1,100 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/// @brief A test of the "Terminator" mechanism: +/// +/// Cl2 --hv--> 2 Cl +/// Cl + Cl + M --> Cl2 + M +/// +/// More details including analytical solution can be found here: +/// https://github.com/ESCOMP/CAM/blob/8cd44c50fe107c0b93ccd48b61eaa3d10a5b4e2f/src/chemistry/pp_terminator/chemistry.F90#L1-L434 +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +void TestTerminator(std::size_t number_of_grid_cells) +{ + auto cl2 = micm::Species("Cl2"); + auto cl = micm::Species("Cl"); + auto m = micm::Species::ThirdBody(); + + micm::Phase gas_phase{ std::vector{ cl2, cl, m } }; + + micm::Process toy_r1 = micm::Process::create() + .reactants({ cl2 }) + .products({ micm::Yield(cl, 2.0) }) + .phase(gas_phase) + .rate_constant(micm::UserDefinedRateConstant({ .label_ = "toy_k1" })); + + constexpr double k2 = 1.0e-6; // 1.0; + micm::Process toy_r2 = micm::Process::create() + .reactants({ cl, cl, m }) + .products({ micm::Yield(cl2, 1.0) }) + .phase(gas_phase) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = k2 })); + + micm::RosenbrockSolver solver{ + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ toy_r1, toy_r2 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true) + }; + + micm::State state = solver.GetState(); + + auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); + std::unordered_map> concentrations{ { "Cl2", {} }, { "Cl", {} } }; + for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) + { + concentrations["Cl2"].push_back(get_double() * 1.0e-6); + concentrations["Cl"].push_back(get_double() * 1.0e-10); + } + state.SetConcentrations(concentrations); + + std::unordered_map> custom_rate_constants{ + { "toy_k1", std::vector(number_of_grid_cells) } + }; + for (double lon = 0.0; lon < 2.0 * M_PI; lon += 0.3) + { + for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) + { + constexpr double k1_lat_center = M_PI * 20.0 / 180.0; + constexpr double k1_lon_center = M_PI * 300.0 / 180.0; + double lat = M_PI / 180.0 * (i_cell * (90.0 / number_of_grid_cells)); + + double k1 = std::max( + 0.0, + std::sin(lat) * std::sin(k1_lat_center) + std::cos(lat) * std::cos(k1_lat_center) * std::cos(lon - k1_lon_center)); + custom_rate_constants["toy_k1"][i_cell] = 1.0e-6; // k1; + state.conditions_[i_cell].temperature_ = 298.0; + state.conditions_[i_cell].pressure_ = 101300.0; + } + state.SetCustomRateParameters(custom_rate_constants); + + double dt = 30.0; + auto result = solver.Solve(dt, state); + for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) + { + double r = custom_rate_constants["toy_k1"][i_cell] / (4.0 * k2); + double cl_i = concentrations["Cl"][i_cell]; + double cl2_i = concentrations["Cl2"][i_cell]; + double cly = cl_i + 2.0 * cl2_i; + double det = std::sqrt(r * r + 2.0 * r * cly); + double e = std::exp(-4.0 * k2 * det * dt); + double l = (det * k2 * dt) > 1.0e-16 ? (1.0 - e) / det / dt : 4.0 * k2; + double cl_f = -l * (cl_i - det + r) * (cl_i + det + r) / (1.0 + e + dt * l * (cl_i + r)); + double cl2_f = -cl_f / 2.0; + EXPECT_NEAR( + result.result_[i_cell][state.variable_map_["Cl"]], cl_i + dt * cl_f, (cl_i + dt * cl_f) * 1.0e-8 + 1.0e-30); + EXPECT_NEAR( + result.result_[i_cell][state.variable_map_["Cl2"]], cl2_i + dt * cl2_f, (cl2_i + dt * cl2_f) * 1.0e-8 + 1.0e-30); + } + } +} From 36ecd6302f4d05bd5e502676aaf45dbade416cf0 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 12 Oct 2023 15:37:48 -0700 Subject: [PATCH 107/318] update README example with time output --- README.md | 35 +++++++++++++++------------ test/tutorial/test_README_example.cpp | 11 ++++----- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index ba1895a51..9fe46beb4 100644 --- a/README.md +++ b/README.md @@ -114,15 +114,20 @@ int main(const int argc, const char *argv[]) state.conditions_[0].pressure_ = 101319.9; // Pa state.SetConcentration(foo, 20.0); // mol m-3 - std::cout << "foo, bar, baz" << std::endl; + std::cout << std::setw(5) << "time [s]," + << std::setw(13) << "foo, " + << std::setw(12) << "bar, " + << std::setw(10) << "baz" << std::endl; for (int i = 0; i < 10; ++i) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - std::cout << std::fixed << std::setprecision(6) - << state.variables_[0][state.variable_map_["Foo"]] << ", " - << state.variables_[0][state.variable_map_["Bar"]] << ", " - << state.variables_[0][state.variable_map_["Baz"]] << std::endl; + std::cout << std::setfill(' ') << std::fixed + << std::setw(8) << std::setprecision(2) << i * 500.0 << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Foo"]] << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Bar"]] << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Baz"]] + << std::endl; } return 0; @@ -138,16 +143,16 @@ g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.1.0/include -std=c++20 Output: ``` time [s], foo, bar, baz -0.000000, 11.843503, 5.904845, 1.907012 -500.000000, 6.792023, 9.045965, 3.317336 -1000.000000, 3.828700, 10.740589, 4.210461 -1500.000000, 2.138145, 11.663685, 4.739393 -2000.000000, 1.187934, 12.169452, 5.042503 -2500.000000, 0.658129, 12.447502, 5.213261 -3000.000000, 0.363985, 12.600676, 5.308597 -3500.000000, 0.201076, 12.685147, 5.361559 -4000.000000, 0.111028, 12.731727, 5.390884 -4500.000000, 0.061290, 12.757422, 5.407096 + 0.00, 11.8435, 5.9048, 1.9070 + 500.00, 6.7920, 9.0460, 3.3173 + 1000.00, 3.8287, 10.7406, 4.2105 + 1500.00, 2.1381, 11.6637, 4.7394 + 2000.00, 1.1879, 12.1695, 5.0425 + 2500.00, 0.6581, 12.4475, 5.2133 + 3000.00, 0.3640, 12.6007, 5.3086 + 3500.00, 0.2011, 12.6851, 5.3616 + 4000.00, 0.1110, 12.7317, 5.3909 + 4500.00, 0.0613, 12.7574, 5.4071 ``` # Citation diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index 593020e65..fba9cefb2 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -30,7 +30,6 @@ int main(const int argc, const char *argv[]) std::vector reactions{ r1, r2 }; RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - solver.parameters_.print(); State state = solver.GetState(); @@ -46,11 +45,11 @@ int main(const int argc, const char *argv[]) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - std::cout << std::fixed << std::setprecision(6) - << std::setw(8) << i * 500.0 << ", " - << std::setw(10) << state.variables_[0][state.variable_map_["Foo"]] << ", " - << std::setw(10) << state.variables_[0][state.variable_map_["Bar"]] << ", " - << std::setw(10) << state.variables_[0][state.variable_map_["Baz"]] + std::cout << std::setfill(' ') << std::fixed + << std::setw(8) << std::setprecision(2) << i * 500.0 << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Foo"]] << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Bar"]] << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Baz"]] << std::endl; } From f77e582af645c86e5fceb124b9c7a4925e5caed8 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 12 Oct 2023 17:39:10 -0500 Subject: [PATCH 108/318] adding micm optional dependency pictures (#311) --- README.md | 7 +++++++ docs/source/_static/options.png | Bin 0 -> 57957 bytes docs/source/getting_started.rst | 6 ++++++ 3 files changed, 13 insertions(+) create mode 100644 docs/source/_static/options.png diff --git a/README.md b/README.md index ba1895a51..3a255dad6 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,13 @@ make test If you would later like to uninstall MICM, you can run `sudo make uninstall` from the `build/` directory. +## Options + +There are multiple options for running micm. Please [read our docs](https://ncar.github.io/micm/getting_started.html) +to learn how to configure these options. + +![Options for running micm](docs/source/_static/options.png) + ## Running a MICM Docker container You must have [Docker Desktop](https://www.docker.com/get-started) installed and running. diff --git a/docs/source/_static/options.png b/docs/source/_static/options.png new file mode 100644 index 0000000000000000000000000000000000000000..700cf40f9ef9aaad151833ede719544f7a5a8fb2 GIT binary patch literal 57957 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¬Ozn@8+*Cc>s@`gL`5FyX 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 Y_o!H>j8WJOs%g^R-DU;_dM@Yw7j{Yds{jB1 literal 0 HcmV?d00001 diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index aaebdcfbf..1483c0697 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -28,6 +28,12 @@ step will simply copy the header files into the normal location required by your CMake will allow for setting options such as the installation directory with CMAKE_INSTALL_PREFIX, or various build flags such as BUILD_DOCS, ENABLE_CUDA, etc. +Options +------- + +.. image:: _static/options.png + + MICM can optionally include support for json configuration reading, OpenMP, JIT-compiled chemistry functions, and GPUs. Each of these requires an additional library. Some of these libraries can be included automatically with cmake build options, From d5946ac05905adcf00a590e1160a26efbe5cd9a0 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 12 Oct 2023 17:45:43 -0500 Subject: [PATCH 109/318] Actions (#307) * organizing actions * not building json tests for clang on windows * trying to get linux to run * debug and release types on mac and unbuntu * readme badges and docker and coverage test name * adding gcc and clang tests for debug and release on mac * adding concurrency check * trying to prevent duplicates from running * another duplicate prevention * removing x86 because it takes too long * using homebrew gcc compilers --- .github/workflows/docker_and_coverage.yml | 54 ++++++++ .github/workflows/mac.yml | 86 ++++++++++++ .github/workflows/test.yml | 154 ---------------------- .github/workflows/ubuntu.yml | 56 ++++++++ .github/workflows/windows.yml | 140 ++++++++++++++++++++ README.md | 5 +- 6 files changed, 340 insertions(+), 155 deletions(-) create mode 100644 .github/workflows/docker_and_coverage.yml create mode 100644 .github/workflows/mac.yml delete mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/ubuntu.yml create mode 100644 .github/workflows/windows.yml diff --git a/.github/workflows/docker_and_coverage.yml b/.github/workflows/docker_and_coverage.yml new file mode 100644 index 000000000..53270c370 --- /dev/null +++ b/.github/workflows/docker_and_coverage.yml @@ -0,0 +1,54 @@ +name: Docker + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + docker-build-and-test: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + name: Build and Test - ${{ matrix.dockerfile }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + dockerfile: + - Dockerfile + - Dockerfile.coverage + - Dockerfile.llvm + - Dockerfile.memcheck + - Dockerfile.no_json + - Dockerfile.nvhpc + - Dockerfile.openmp + # - Dockerfile.intel # intel image is too large for GH action + # - Dockerfile.mpi + steps: + - name: Checkout code + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Build Docker image + run: docker build -t micm -f docker/${{ matrix.dockerfile }} . + + - name: Run tests in container + if: matrix.dockerfile != 'Dockerfile.coverage' + run: docker run --name test-container -t micm bash -c 'make test ARGS="--rerun-failed --output-on-failure -j8"' + + - name: Run coverage tests in container + if: matrix.dockerfile == 'Dockerfile.coverage' + run: docker run --name test-container -t micm bash -c 'make coverage ARGS="--rerun-failed --output-on-failure -j8"' + + - name: Copy coverage from container + if: matrix.dockerfile == 'Dockerfile.coverage' + run: docker cp test-container:build/coverage.info . + + - name: Upload coverage report + if: matrix.dockerfile == 'Dockerfile.coverage' + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: coverage.info \ No newline at end of file diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml new file mode 100644 index 000000000..9e9bc0eb6 --- /dev/null +++ b/.github/workflows/mac.yml @@ -0,0 +1,86 @@ +name: Mac + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + xcode_macos_12: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: macos-12 + strategy: + matrix: + # all available versions of xcode: https://github.com/actions/runner-images/blob/main/images/macos/macos-12-Readme.md#xcode + xcode: ['13.1', '14.1'] + build_type: [Debug, Release] + env: + DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer + + steps: + - uses: actions/checkout@v3 + + - name: Run Cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --parallel 10 + + - name: Run tests + run: | + cd build + ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure . --verbose -j 10 + + xcode_macos_13: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: macos-13 + strategy: + matrix: + # all available versions of xcode: https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode + xcode: ['14.1', '15.0'] + build_type: [Debug, Release] + env: + DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer + + steps: + - uses: actions/checkout@v3 + + - name: Run Cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --parallel 10 + + - name: Run tests + run: | + cd build + ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure . --verbose -j 10 + + macos_lateset: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: macos-latest + strategy: + matrix: + compiler: + - { cpp: g++-11, c: gcc-11} + - { cpp: g++-12, c: gcc-12} + - { cpp: clang++, c: clang} + build_type: [Debug, Release] + env: + CC: ${{ matrix.compiler.c }} + CXX: ${{ matrix.compiler.cpp }} + + steps: + - uses: actions/checkout@v3 + + - name: Run Cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --parallel 10 + + - name: Run tests + run: | + cd build + ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure . --verbose -j 10 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 6196422ef..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,154 +0,0 @@ -name: build - -on: [push, pull_request] - -jobs: - docker-build-and-test: - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name - name: Build and Test - ${{ matrix.dockerfile }} - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - dockerfile: - - Dockerfile - - Dockerfile.coverage - - Dockerfile.llvm - - Dockerfile.memcheck - - Dockerfile.no_json - - Dockerfile.nvhpc - - Dockerfile.openmp - # - Dockerfile.intel # intel image is too large for GH action - # - Dockerfile.mpi - steps: - - name: Checkout code - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Build Docker image - run: docker build -t micm -f docker/${{ matrix.dockerfile }} . - - - name: Run tests in container - if: matrix.dockerfile != 'Dockerfile.coverage' - run: docker run --name test-container -t micm bash -c 'make test ARGS="--rerun-failed --output-on-failure -j8"' - - - name: Run coverage tests in container - if: matrix.dockerfile == 'Dockerfile.coverage' - run: docker run --name test-container -t micm bash -c 'make coverage ARGS="--rerun-failed --output-on-failure -j8"' - - - name: Copy coverage from container - if: matrix.dockerfile == 'Dockerfile.coverage' - run: docker cp test-container:build/coverage.info . - - - name: Upload coverage report - if: matrix.dockerfile == 'Dockerfile.coverage' - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: coverage.info - - multiplatform: - if: github.event_name == 'pull_request' - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - os: - - macos-latest - - ubuntu-latest - - windows-latest - compiler: - - { cpp: g++, c: gcc} - - { cpp: clang++, c: clang} - - { cpp: cl, c: cl } - exclude: - - os: windows-latest - compiler: { cpp: clang++, c: clang } - - os: windows-latest - compiler: { cpp: g++, c: gcc } - - os: ubuntu-latest - compiler: { cpp: cl, c: cl } - - os: macos-latest - compiler: { cpp: cl, c: cl } - env: - CC: ${{ matrix.compiler.c }} - CXX: ${{ matrix.compiler.cpp }} - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Install CMake - if: matrix.os == 'windows-latest' - run: choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' - - - name: Install CMake - if: matrix.os != 'windows-latest' - run: | - if [[ "${{ matrix.os }}" == "ubuntu-latest" ]]; then - sudo apt-get update - sudo apt-get install -y cmake - elif [[ "${{ matrix.os }}" == "macos-latest" ]]; then - brew install cmake - fi - - - name: Configure and build - run: | - mkdir build - cd build - cmake .. - cmake --build . --parallel 10 - - - name: Run tests - run: | - cd build - ctest -C Debug --rerun-failed --output-on-failure . --verbose - - xcode_1: - if: github.event_name == 'pull_request' - runs-on: macos-12 - strategy: - matrix: - xcode: ['13.1', '13.2.1', '13.3.1', '13.4.1', '14.0', '14.0.1', '14.1'] - env: - DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer - - steps: - - uses: actions/checkout@v3 - - - name: Configure and build - run: | - mkdir build - cd build - cmake .. - cmake --build . --parallel 10 - - - name: Run tests - run: | - cd build - ctest --rerun-failed --output-on-failure . --verbose -j 10 - - xcode_2: - if: github.event_name == 'pull_request' - runs-on: macos-13 - strategy: - matrix: - xcode: ['14.1', '14.2', '14.3.1', '15.0'] - env: - DEVELOPER_DIR: /Applications/Xcode_${{ matrix.xcode }}.app/Contents/Developer - - steps: - - uses: actions/checkout@v3 - - - name: Configure and build - run: | - mkdir build - cd build - cmake .. - cmake --build . --parallel 10 - - - name: Run tests - run: | - cd build - ctest --rerun-failed --output-on-failure . --verbose -j 10 \ No newline at end of file diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 000000000..3cd9aad7c --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,56 @@ +name: Ubuntu + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + gcc: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: ubuntu-latest + strategy: + matrix: + build_type: [Debug, Release] + env: + CC: gcc + CXX: g++ + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run Cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --parallel 10 + + - name: Run tests + run: | + cd build + ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure . --verbose -j 10 + + clang: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: ubuntu-latest + strategy: + matrix: + build_type: [Debug, Release] + env: + CC: clang + CXX: clang++ + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Run Cmake + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} + + - name: Build + run: cmake --build build --parallel 10 + + - name: Run tests + run: | + cd build + ctest -C ${{ matrix.build_type }} --rerun-failed --output-on-failure . --verbose -j 10 \ No newline at end of file diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 000000000..f72b4b427 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,140 @@ +name: Windows + +on: [push, pull_request] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref || github.run_id }} + cancel-in-progress: true + +jobs: + mingw: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2019 + strategy: + matrix: + architecture: [x64] + + steps: + - uses: actions/checkout@v3 + - name: Set up MinGW + uses: egor-tensin/setup-mingw@v2 + with: + platform: ${{ matrix.architecture }} + version: 12.2.0 # https://github.com/egor-tensin/setup-mingw/issues/14 + + - name: Run Cmake + run: cmake -S . -B build -G "MinGW Makefiles" + + - name: Build + run: cmake --build build --parallel 10 + + - name: Run tests + run: | + cd build + ctest -C Debug --rerun-failed --output-on-failure . --verbose + + msvc2019: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2019 + strategy: + matrix: + build_type: [Debug, Release] + architecture: [Win32, x64] + + steps: + - uses: actions/checkout@v3 + + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} + if: matrix.build_type == 'Release' + + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} + if: matrix.build_type == 'Debug' + + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 + + - name: Test + run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure + + msvc2019_latest: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2019 + + steps: + - uses: actions/checkout@v3 + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 16 2019" + - name: Build + run: cmake --build build --config Release --parallel 10 + - name: Test + run: cd build ; ctest -j 10 -C Release --output-on-failure + + msvc2022: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2022 + strategy: + matrix: + build_type: [Debug, Release] + architecture: [Win32, x64] + + steps: + - uses: actions/checkout@v3 + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} + if: matrix.build_type == 'Release' + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} + if: matrix.build_type == 'Debug' + - name: Build + run: cmake --build build --config ${{ matrix.build_type }} --parallel 10 + - name: Test + run: cd build ; ctest -j 10 -C ${{ matrix.build_type }} --output-on-failure + + msvc2022_latest: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2022 + + steps: + - uses: actions/checkout@v3 + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 17 2022" + - name: Build + run: cmake --build build --config Release --parallel 10 + - name: Test + run: cd build ; ctest -j 10 -C Release --output-on-failure + + clang: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2019 + strategy: + matrix: + version: [11, 12, 13, 14, 15] + + steps: + - uses: actions/checkout@v3 + - name: Install Clang + run: curl -fsSL -o LLVM${{ matrix.version }}.exe https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ matrix.version }}.0.0/LLVM-${{ matrix.version }}.0.0-win64.exe ; 7z x LLVM${{ matrix.version }}.exe -y -o"C:/Program Files/LLVM" + - name: Run CMake + run: cmake -S . -B build -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" -G"MinGW Makefiles" -DCMAKE_BUILD_TYPE=Debug + - name: Build + run: cmake --build build --parallel 10 + - name: Test + run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure + + clang-cl-11: + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name != github.event.pull_request.base.repo.full_name + runs-on: windows-2019 + strategy: + matrix: + architecture: [Win32, x64] + + steps: + - uses: actions/checkout@v3 + - name: Run CMake + run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -T ClangCL + - name: Build + run: cmake --build build --config Debug --parallel 10 + - name: Test + run: cd build ; ctest -j 10 -C Debug --exclude-regex "test-unicode" --output-on-failure \ No newline at end of file diff --git a/README.md b/README.md index 3a255dad6..5b8218eac 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ MICM Chemistry Model Independent Chemical Module. MICM can be used to configure and solve atmospheric chemistry systems. [![License](https://img.shields.io/github/license/NCAR/micm.svg)](https://github.com/NCAR/micm/blob/master/LICENSE) -[![CI Status](https://github.com/NCAR/micm/actions/workflows/test.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/test.yml) +[![Docker builds](https://github.com/NCAR/micm/actions/workflows/docker_and_coverage.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/docker_and_coverage.yml) +[![Windows](https://github.com/NCAR/micm/actions/workflows/windows.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/windows.yml) +[![Mac](https://github.com/NCAR/micm/actions/workflows/mac.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/mac.yml) +[![Ubuntu](https://github.com/NCAR/micm/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/ubuntu.yml) [![codecov](https://codecov.io/gh/NCAR/micm/branch/main/graph/badge.svg?token=ATGO4DKTMY)](https://codecov.io/gh/NCAR/micm) [![DOI](https://zenodo.org/badge/294492778.svg)](https://zenodo.org/badge/latestdoi/294492778) From 7d5730d0a759b51346d0c02f59a3fb6d18e9ceec Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Thu, 12 Oct 2023 16:37:48 -0700 Subject: [PATCH 110/318] update README --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5b8218eac..954cac94b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ Model Independent Chemical Module. MICM can be used to configure and solve atmos Copyright (C) 2018-2023 National Center for Atmospheric Research +> **Note** +> MICM 3.x.x is part of a refactor and may include breaking changes across minor revision numbers +and partially implemented features + # Getting Started ## Installing MICM locally @@ -41,10 +45,11 @@ If you would later like to uninstall MICM, you can run ## Options -There are multiple options for running micm. Please [read our docs](https://ncar.github.io/micm/getting_started.html) -to learn how to configure these options. - -![Options for running micm](docs/source/_static/options.png) +There are multiple options for running micm. You can use [json](https://github.com/nlohmann/json) +to configure a solver, [llvm](https://llvm.org/) to JIT-compile +solvers on CPUs or [cuda](https://developer.nvidia.com/cuda-zone)-based solvers to solve chemistry on GPUs. +Please [read our docs](https://ncar.github.io/micm/getting_started.html) +to learn how to enable these options. ## Running a MICM Docker container @@ -204,7 +209,3 @@ installation and usage instructions. - [Apache 2.0](/LICENSE) Copyright (C) 2018-2023 National Center for Atmospheric Research - - -> **Note** -> MICM 3.x.x is part of a refactor and may include breaking changes across minor revision numbers From dcefba53999371ab557c9f573397646985c88cc7 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 13 Oct 2023 09:54:25 -0700 Subject: [PATCH 111/318] update Rosenbrock parameters --- include/micm/solver/rosenbrock.hpp | 11 ++++++--- include/micm/solver/rosenbrock.inl | 39 +++++++++++++++++------------- test/integration/terminator.hpp | 18 ++++++++++---- 3 files changed, 42 insertions(+), 26 deletions(-) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index b692d1151..99f95af55 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -50,9 +50,11 @@ namespace micm double rejection_factor_decrease_{ 0.1 }; // used to decrease the step after 2 successive rejections double safety_factor_{ 0.9 }; // safety factor in new step size computation - double h_min_{ 0 }; // step size min - double h_max_{ 0.5 }; // step size max - double h_start_{ 0.005 }; // step size start + double h_min_{ 0.0 }; // step size min [s] + double h_max_{ + 0.0 + }; // step size max [s] (if zero or greater than the solver time-step, the time-step passed to the solver will be used) + double h_start_{ 0.0 }; // step size start [s] (if zero, 1.0e-6 will be used, if greater than h_max, h_max will be used) // Does the stage i require a new function evaluation (ros_NewF(i)=TRUE) // or does it re-use the function evaluation from stage i-1 (ros_NewF(i)=FALSE) @@ -173,7 +175,8 @@ namespace micm uint64_t decompositions{}; /// @brief The number of linear solvers uint64_t solves{}; - /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix is never singular + /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix + /// is never singular uint64_t singular{}; /// @brief The cumulative amount of time spent calculating the forcing function std::chrono::duration total_forcing_time{}; diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 7d1cc6f9b..bdb582fb3 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -1,17 +1,20 @@ // Copyright (C) 2023 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 -#define TIMED_METHOD(assigned_increment, time_it, method, ...) \ - { \ - if constexpr (time_it) { \ - auto start = std::chrono::high_resolution_clock::now(); \ - method(__VA_ARGS__); \ - auto end = std::chrono::high_resolution_clock::now(); \ - assigned_increment += std::chrono::duration_cast(end - start); \ - } else { \ - method(__VA_ARGS__); \ - } \ - } \ +#define TIMED_METHOD(assigned_increment, time_it, method, ...) \ + { \ + if constexpr (time_it) \ + { \ + auto start = std::chrono::high_resolution_clock::now(); \ + method(__VA_ARGS__); \ + auto end = std::chrono::high_resolution_clock::now(); \ + assigned_increment += std::chrono::duration_cast(end - start); \ + } \ + else \ + { \ + method(__VA_ARGS__); \ + } \ + } namespace micm { @@ -517,9 +520,9 @@ namespace micm MatrixPolicy forcing(Y.size(), Y[0].size(), 0.0); MatrixPolicy temp(Y.size(), Y[0].size(), 0.0); std::vector> K{}; - - parameters_.h_max_ = time_step; - parameters_.h_start_ = std::max(parameters_.h_min_, delta_min_); + const double h_max = parameters_.h_max_ == 0.0 ? time_step : std::min(time_step, parameters_.h_max_); + const double h_start = + parameters_.h_start_ == 0.0 ? std::max(parameters_.h_min_, delta_min_) : std::min(h_max, parameters_.h_start_); stats_.Reset(); UpdateState(state); @@ -528,12 +531,14 @@ namespace micm K.push_back(MatrixPolicy(Y.size(), Y[0].size(), 0.0)); double present_time = 0.0; - double H = - std::min(std::max(std::abs(parameters_.h_min_), std::abs(parameters_.h_start_)), std::abs(parameters_.h_max_)); + double H = std::min(std::max(std::abs(parameters_.h_min_), std::abs(h_start)), std::abs(h_max)); if (std::abs(H) <= 10 * parameters_.round_off_) H = delta_min_; + // TODO: the logic above this point should be moved to the constructor and should return an error + // if the parameters are invalid (e.g., h_min > h_max or h_start > h_max) + bool reject_last_h = false; bool reject_more_h = false; @@ -639,7 +644,7 @@ namespace micm stats_.accepted += 1; present_time = present_time + H; Y.AsVector().assign(Ynew.AsVector().begin(), Ynew.AsVector().end()); - Hnew = std::max(parameters_.h_min_, std::min(Hnew, parameters_.h_max_)); + Hnew = std::max(parameters_.h_min_, std::min(Hnew, h_max)); if (reject_last_h) { // No step size increase after a rejected step diff --git a/test/integration/terminator.hpp b/test/integration/terminator.hpp index 9a1c226c7..d417139d5 100644 --- a/test/integration/terminator.hpp +++ b/test/integration/terminator.hpp @@ -34,17 +34,21 @@ void TestTerminator(std::size_t number_of_grid_cells) .phase(gas_phase) .rate_constant(micm::UserDefinedRateConstant({ .label_ = "toy_k1" })); - constexpr double k2 = 1.0e-6; // 1.0; + constexpr double k2 = 1.0; micm::Process toy_r2 = micm::Process::create() .reactants({ cl, cl, m }) .products({ micm::Yield(cl2, 1.0) }) .phase(gas_phase) .rate_constant(micm::ArrheniusRateConstant({ .A_ = k2 })); + auto solver_params = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true); + solver_params.absolute_tolerance_ = 1.0e-20; + solver_params.relative_tolerance_ = 1.0e-8; + solver_params.max_number_of_steps_ = 100000; micm::RosenbrockSolver solver{ micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ toy_r1, toy_r2 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true) + solver_params }; micm::State state = solver.GetState(); @@ -72,14 +76,18 @@ void TestTerminator(std::size_t number_of_grid_cells) double k1 = std::max( 0.0, std::sin(lat) * std::sin(k1_lat_center) + std::cos(lat) * std::cos(k1_lat_center) * std::cos(lon - k1_lon_center)); - custom_rate_constants["toy_k1"][i_cell] = 1.0e-6; // k1; + custom_rate_constants["toy_k1"][i_cell] = k1; state.conditions_[i_cell].temperature_ = 298.0; state.conditions_[i_cell].pressure_ = 101300.0; + state.conditions_[i_cell].air_density_ = 1.0; } state.SetCustomRateParameters(custom_rate_constants); double dt = 30.0; auto result = solver.Solve(dt, state); + + EXPECT_EQ(result.state_, micm::SolverState::Converged); + for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) { double r = custom_rate_constants["toy_k1"][i_cell] / (4.0 * k2); @@ -92,9 +100,9 @@ void TestTerminator(std::size_t number_of_grid_cells) double cl_f = -l * (cl_i - det + r) * (cl_i + det + r) / (1.0 + e + dt * l * (cl_i + r)); double cl2_f = -cl_f / 2.0; EXPECT_NEAR( - result.result_[i_cell][state.variable_map_["Cl"]], cl_i + dt * cl_f, (cl_i + dt * cl_f) * 1.0e-8 + 1.0e-30); + result.result_[i_cell][state.variable_map_["Cl"]], cl_i + dt * cl_f, (cl_i + dt * cl_f) * 1.0e-8 + 1.0e-15); EXPECT_NEAR( - result.result_[i_cell][state.variable_map_["Cl2"]], cl2_i + dt * cl2_f, (cl2_i + dt * cl2_f) * 1.0e-8 + 1.0e-30); + result.result_[i_cell][state.variable_map_["Cl2"]], cl2_i + dt * cl2_f, (cl2_i + dt * cl2_f) * 1.0e-8 + 1.0e-15); } } } From 476e481b8d7dbfd85e9cf6456a08edf73c789db7 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 13 Oct 2023 14:17:35 -0700 Subject: [PATCH 112/318] add jit terminator test --- test/integration/CMakeLists.txt | 1 + test/integration/jit_terminator.cpp | 69 +++++++++++++++++++++++++++++ test/integration/terminator.cpp | 35 +++++++++++---- test/integration/terminator.hpp | 18 +++----- 4 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 test/integration/jit_terminator.cpp diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 77ccb7d86..df835627c 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -12,4 +12,5 @@ create_standard_test(NAME terminator_integration SOURCES terminator.cpp) if(ENABLE_LLVM) create_standard_test(NAME analytical_jit_rosenbrock_integration SOURCES analytical_jit_rosenbrock.cpp) + create_standard_test(NAME jit_terminator_integration SOURCES jit_terminator.cpp) endif() \ No newline at end of file diff --git a/test/integration/jit_terminator.cpp b/test/integration/jit_terminator.cpp new file mode 100644 index 000000000..1499054fc --- /dev/null +++ b/test/integration/jit_terminator.cpp @@ -0,0 +1,69 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include "terminator.hpp" + +template class MatrixPolicy, template class SparseMatrixPolicy> +void RunTerminatorTest() +{ + TestTerminator< + MatrixPolicy, + micm::JitRosenbrockSolver< + MatrixPolicy, + SparseMatrixPolicy, + micm::JitLinearSolver>>( + [&](const micm::System& s, const std::vector& p) + -> micm::JitRosenbrockSolver< + MatrixPolicy, + SparseMatrixPolicy, + micm::JitLinearSolver> + { + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + auto solver_params = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true); + solver_params.absolute_tolerance_ = 1.0e-20; + solver_params.relative_tolerance_ = 1.0e-8; + solver_params.max_number_of_steps_ = 100000; + return micm::JitRosenbrockSolver< + MatrixPolicy, + SparseMatrixPolicy, + micm::JitLinearSolver>{ jit.get(), s, p, solver_params }; + }, + number_of_grid_cells); +} + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; + +TEST(JitRosenbrockSolver, Terminator) +{ + RunTerminatorTest<1, Group1VectorMatrix, Group1SparseVectorMatrix>(); + RunTerminatorTest<2, Group2VectorMatrix, Group2SparseVectorMatrix>(); + RunTerminatorTest<3, Group3VectorMatrix, Group3SparseVectorMatrix>(); + RunTerminatorTest<4, Group4VectorMatrix, Group4SparseVectorMatrix>(); +} \ No newline at end of file diff --git a/test/integration/terminator.cpp b/test/integration/terminator.cpp index 07467b5be..813a37c6c 100644 --- a/test/integration/terminator.cpp +++ b/test/integration/terminator.cpp @@ -2,17 +2,36 @@ #include +#include +#include +#include #include #include #include #include +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +void RunTerminatorTest(std::size_t number_of_grid_cells) +{ + TestTerminator>( + [&](const micm::System& s, const std::vector& p) + -> micm::RosenbrockSolver + { + auto solver_params = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true); + solver_params.absolute_tolerance_ = 1.0e-20; + solver_params.relative_tolerance_ = 1.0e-8; + solver_params.max_number_of_steps_ = 100000; + return micm::RosenbrockSolver{ s, p, solver_params }; + }, + number_of_grid_cells); +} + TEST(RosenbrockSolver, Terminator) { - TestTerminator>(1); - TestTerminator>(2); - TestTerminator>(3); - TestTerminator>(4); + RunTerminatorTest>(2); + RunTerminatorTest>(2); + RunTerminatorTest>(3); + RunTerminatorTest>(4); } template @@ -35,8 +54,8 @@ using Group4SparseVectorMatrix = micm::SparseMatrix>(1); - TestTerminator>(4); - TestTerminator>(3); - TestTerminator>(2); + RunTerminatorTest>(1); + RunTerminatorTest>(4); + RunTerminatorTest>(3); + RunTerminatorTest>(2); } \ No newline at end of file diff --git a/test/integration/terminator.hpp b/test/integration/terminator.hpp index d417139d5..60451ed34 100644 --- a/test/integration/terminator.hpp +++ b/test/integration/terminator.hpp @@ -19,8 +19,10 @@ /// /// More details including analytical solution can be found here: /// https://github.com/ESCOMP/CAM/blob/8cd44c50fe107c0b93ccd48b61eaa3d10a5b4e2f/src/chemistry/pp_terminator/chemistry.F90#L1-L434 -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -void TestTerminator(std::size_t number_of_grid_cells) +template class MatrixPolicy, class OdeSolverPolicy> +void TestTerminator( + const std::function&)> create_solver, + std::size_t number_of_grid_cells) { auto cl2 = micm::Species("Cl2"); auto cl = micm::Species("Cl"); @@ -41,16 +43,8 @@ void TestTerminator(std::size_t number_of_grid_cells) .phase(gas_phase) .rate_constant(micm::ArrheniusRateConstant({ .A_ = k2 })); - auto solver_params = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true); - solver_params.absolute_tolerance_ = 1.0e-20; - solver_params.relative_tolerance_ = 1.0e-8; - solver_params.max_number_of_steps_ = 100000; - micm::RosenbrockSolver solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ toy_r1, toy_r2 }, - solver_params - }; - + auto solver = create_solver( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ toy_r1, toy_r2 }); micm::State state = solver.GetState(); auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); From d03b4faf7f64c93ea2ae6b555cfa7d640ffc60d6 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 13 Oct 2023 14:43:52 -0700 Subject: [PATCH 113/318] remove third-body species from terminator --- test/integration/terminator.hpp | 13 ++++++------- test/unit/system/test_species.cpp | 8 ++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/test/integration/terminator.hpp b/test/integration/terminator.hpp index 60451ed34..1a57f475f 100644 --- a/test/integration/terminator.hpp +++ b/test/integration/terminator.hpp @@ -15,7 +15,7 @@ /// @brief A test of the "Terminator" mechanism: /// /// Cl2 --hv--> 2 Cl -/// Cl + Cl + M --> Cl2 + M +/// Cl + Cl --> Cl2 /// /// More details including analytical solution can be found here: /// https://github.com/ESCOMP/CAM/blob/8cd44c50fe107c0b93ccd48b61eaa3d10a5b4e2f/src/chemistry/pp_terminator/chemistry.F90#L1-L434 @@ -26,9 +26,8 @@ void TestTerminator( { auto cl2 = micm::Species("Cl2"); auto cl = micm::Species("Cl"); - auto m = micm::Species::ThirdBody(); - micm::Phase gas_phase{ std::vector{ cl2, cl, m } }; + micm::Phase gas_phase{ std::vector{ cl2, cl } }; micm::Process toy_r1 = micm::Process::create() .reactants({ cl2 }) @@ -38,7 +37,7 @@ void TestTerminator( constexpr double k2 = 1.0; micm::Process toy_r2 = micm::Process::create() - .reactants({ cl, cl, m }) + .reactants({ cl, cl }) .products({ micm::Yield(cl2, 1.0) }) .phase(gas_phase) .rate_constant(micm::ArrheniusRateConstant({ .A_ = k2 })); @@ -71,9 +70,9 @@ void TestTerminator( 0.0, std::sin(lat) * std::sin(k1_lat_center) + std::cos(lat) * std::cos(k1_lat_center) * std::cos(lon - k1_lon_center)); custom_rate_constants["toy_k1"][i_cell] = k1; - state.conditions_[i_cell].temperature_ = 298.0; - state.conditions_[i_cell].pressure_ = 101300.0; - state.conditions_[i_cell].air_density_ = 1.0; + state.conditions_[i_cell].temperature_ = 298.0; // K + state.conditions_[i_cell].pressure_ = 101300.0; // Pa + state.conditions_[i_cell].air_density_ = 42.0; // mol m-3 } state.SetCustomRateParameters(custom_rate_constants); diff --git a/test/unit/system/test_species.cpp b/test/unit/system/test_species.cpp index 5adff1b30..dcfab1f89 100644 --- a/test/unit/system/test_species.cpp +++ b/test/unit/system/test_species.cpp @@ -22,6 +22,14 @@ TEST(Species, StringAndVectorConstructor) EXPECT_EQ(species.properties_["name2 [units2]"], 2.0); } +TEST(Species, ThirdBody) +{ + micm::Species species = micm::Species::ThirdBody(); + EXPECT_EQ(species.name_, "M"); + EXPECT_TRUE(species.IsParameterized()); + EXPECT_EQ(species.parameterize_({ .air_density_ = 42.4 }), 42.4); +} + TEST(Species, CopyConstructor) { { From 251f6db15bc8ab80dd7aabe1a0fe3d4b5ab635dc Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 09:31:55 -0600 Subject: [PATCH 114/318] Start on new JsonReaderPolicy in camp_config.hpp. --- include/micm/configure/camp_config.hpp | 38 ++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 9c3007b0f..cb82db4d6 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -15,6 +15,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include + namespace micm { enum class ConfigParseStatus @@ -71,5 +79,35 @@ namespace micm class JsonReaderPolicy { using json = nlohmann::json; + + public: + std::vector species_arr_; + + std::vector user_defined_rate_arr_; + std::vector arrhenius_rate_arr_; + std::vector troe_rate_arr_; + std::vector branched_rate_arr_; + std::vector surface_rate_arr_; + std::vector ternary_rate_arr_; + std::vector tunneling_rate_arr_; + + // Specific for solver parameters + Phase gas_phase_; + std::unordered_map phases_; + std::vector processes_; + + // Constants + // Configure files + static const inline std::string CAMP_CONFIG = "config.json"; + static const inline std::string SPECIES_CONFIG = "species.json"; + static const inline std::string MECHANISM_CONFIG = "mechanism.json"; + static const inline std::string REACTIONS_CONFIG = "reactions.json"; + static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; + + // Common JSON + static const inline std::string CAMP_DATA = "camp-data"; + static const inline std::string CAMP_FILES = "camp-files"; + static const inline std::string TYPE = "type"; + }; } // namespace micm From 84a79e1e1740808499f55ff81dc831200168a852 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 10:03:16 -0600 Subject: [PATCH 115/318] Start on new JsonReaderPolicy in camp_config.hpp. --- include/micm/configure/camp_config.hpp | 40 +++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 4 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index cb82db4d6..37d89601c 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -31,6 +31,7 @@ namespace micm None, InvalidKey, UnknownKey, + InvalidCAMPFilePath, CAMPFilesSectionNotFound, CAMPDataSectionNotFound, InvalidSpecies, @@ -47,6 +48,7 @@ namespace micm case ConfigParseStatus::None: return "None"; case ConfigParseStatus::InvalidKey: return "InvalidKey"; case ConfigParseStatus::UnknownKey: return "UnknownKey"; + case ConfigParseStatus::InvalidCAMPFilePath: return "InvalidCAMPFilePath"; case ConfigParseStatus::CAMPFilesSectionNotFound: return "CAMPFilesSectionNotFound"; case ConfigParseStatus::CAMPDataSectionNotFound: return "CAMPDataSectionNotFound"; case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; @@ -99,15 +101,45 @@ namespace micm // Constants // Configure files static const inline std::string CAMP_CONFIG = "config.json"; - static const inline std::string SPECIES_CONFIG = "species.json"; - static const inline std::string MECHANISM_CONFIG = "mechanism.json"; - static const inline std::string REACTIONS_CONFIG = "reactions.json"; - static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; // Common JSON static const inline std::string CAMP_DATA = "camp-data"; static const inline std::string CAMP_FILES = "camp-files"; static const inline std::string TYPE = "type"; + // Functions + + /// @brief Parse configures + /// @return True for successful parsing + ConfigParseStatus Parse(const std::filesystem::path& config_dir) + { + std::vector camp_files; + + // Look for CAMP config file + std::filesystem::path camp_config(config_dir / CAMP_CONFIG); + if (std::filesystem::exists(camp_config)) + { + json camp_data = json::parse(std::ifstream(camp_config)); + if (!camp_data.contains(CAMP_FILES)) + return ConfigParseStatus::CAMPFilesSectionNotFound; + + for (const auto& element : camp_data[CAMP_FILES]) + { + camp_files.push_back(element.get()); + } + } + else + { + return ConfigParseStatus::InvalidCAMPFilePath; + } + + // Merge config JSON from CAMP file list + for (const auto& camp_file : camp_files) + { + json config_data = json::parse(std::ifstream(camp_file)); + } + + return ConfigParseStatus::Success; + } }; } // namespace micm From f685fa3a154d6a0f444d392852ab3ed9dde410a1 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 10:36:40 -0600 Subject: [PATCH 116/318] Added class SolverConfig to camp_config.hpp. --- include/micm/configure/camp_config.hpp | 19 +++++++++++++++++++ test/unit/configure/test_camp_config.cpp | 6 +++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 37d89601c..5414fb2df 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -110,6 +110,7 @@ namespace micm // Functions /// @brief Parse configures + /// @param config_dir Path to a the configuration directory /// @return True for successful parsing ConfigParseStatus Parse(const std::filesystem::path& config_dir) { @@ -142,4 +143,22 @@ namespace micm return ConfigParseStatus::Success; } }; + + /// @brief Public interface to read and parse config + template + class SolverConfig : public ConfigTypePolicy + { + private: + ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; + + 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]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) + { + last_parse_status_ = this->Parse(config_dir); + return last_parse_status_; + } + }; } // namespace micm diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 60c88d4f0..8a650efe7 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -4,7 +4,7 @@ TEST(SolverConfig, DetectsInvalidConfigFile) { - // micm::SolverConfig solverConfig{}; - // auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); - // EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); + EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); } From c31d2e2d7e3ddb6bf286ffa50da0c79d5a7a6e1d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 10:41:37 -0600 Subject: [PATCH 117/318] Added ReadAndParseCAMPFiles test. --- test/unit/configure/test_camp_config.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 8a650efe7..46f06a18e 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -8,3 +8,13 @@ TEST(SolverConfig, DetectsInvalidConfigFile) auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); } + +TEST(SolverConfig, ReadAndParseCAMPFiles) +{ + micm::SolverConfig solverConfig{}; + + // Read and parse the CAMP configure file + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); +} + From 7dd63026f3e69135cd01ae3a37f3e27dfa511326 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 10:54:20 -0600 Subject: [PATCH 118/318] Prepend config dir to CAMP files. --- include/micm/configure/camp_config.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 5414fb2df..affef9873 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -137,7 +137,8 @@ namespace micm // Merge config JSON from CAMP file list for (const auto& camp_file : camp_files) { - json config_data = json::parse(std::ifstream(camp_file)); + std::cout << "JsonReaderPolicy.Parse CAMP file" << camp_file << std::endl; + json config_data = json::parse(std::ifstream(config_dir / camp_file)); } return ConfigParseStatus::Success; From aef6e2a71144ccc31a66606ef3d5d58c65c596d2 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 11:14:16 -0600 Subject: [PATCH 119/318] Added some debug output. --- include/micm/configure/camp_config.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index affef9873..bb5288aba 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -139,6 +139,7 @@ namespace micm { std::cout << "JsonReaderPolicy.Parse CAMP file" << camp_file << std::endl; json config_data = json::parse(std::ifstream(config_dir / camp_file)); + std::cout << config_data.dump(4) << std::endl; } return ConfigParseStatus::Success; From d04e2f9f0bed9d4b7c8b0b1c35225617efec748a Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 11:52:00 -0600 Subject: [PATCH 120/318] Merge config JSON from CAMP file list. --- include/micm/configure/camp_config.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index bb5288aba..a73fe01e8 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -135,12 +135,15 @@ namespace micm } // Merge config JSON from CAMP file list + json config_data; for (const auto& camp_file : camp_files) { std::cout << "JsonReaderPolicy.Parse CAMP file" << camp_file << std::endl; - json config_data = json::parse(std::ifstream(config_dir / camp_file)); - std::cout << config_data.dump(4) << std::endl; + json config_subset = json::parse(std::ifstream(config_dir / camp_file)); + std::copy(config_subset.begin(), config_subset.end(), + std::back_inserter(config_data)); } + std::cout << config_data.dump(4) << std::endl; return ConfigParseStatus::Success; } From 74765718868b44453b0f9448bbe0f3ed60384bf9 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 13:23:35 -0600 Subject: [PATCH 121/318] Iterate JSON objects. --- include/micm/configure/camp_config.hpp | 37 ++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index a73fe01e8..212bb8426 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -143,9 +143,42 @@ namespace micm std::copy(config_subset.begin(), config_subset.end(), std::back_inserter(config_data)); } - std::cout << config_data.dump(4) << std::endl; - return ConfigParseStatus::Success; + ConfigParseStatus status; + + status = Configure(config_data); + + return status; + } + + private: + + ConfigParseStatus Configure(const json& config_data) + { + // std::cout << config_data.dump(4) << std::endl; + + ConfigParseStatus status = ConfigParseStatus::None; + + std::vector sections; + std::vector objects; + + for (const auto& section : config_data) + { + sections.push_back(section); + } + + for (const auto& section : sections) + { + for (const auto& object : section) + { + objects.push_back(object); + std::cout << object.dump(4) << std::endl; + } + } + + status = ConfigParseStatus::Success; + + return status; } }; From c0dcef313dbc009fcb7c2ca814818e10ca4f54d3 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 13:37:52 -0600 Subject: [PATCH 122/318] Check JSON object for TYPE. --- include/micm/configure/camp_config.hpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 212bb8426..058c1752f 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -159,18 +159,17 @@ namespace micm ConfigParseStatus status = ConfigParseStatus::None; - std::vector sections; std::vector objects; for (const auto& section : config_data) - { - sections.push_back(section); - } - - for (const auto& section : sections) { for (const auto& object : section) { + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + break; + } objects.push_back(object); std::cout << object.dump(4) << std::endl; } @@ -180,6 +179,17 @@ namespace micm return status; } + + bool ValidateJsonWithKey(const json& object, const std::string& key) + { + if (!object.contains(key)) + { + std::string msg = "Key " + key + " was not found in the config file"; + std::cerr << msg << std::endl; + return false; + } + return true; + } }; /// @brief Public interface to read and parse config From acfb17b5794b924dace2268359bb2ca8e41af261 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 14:30:01 -0600 Subject: [PATCH 123/318] Check JSON type. --- include/micm/configure/camp_config.hpp | 45 ++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 058c1752f..609f680b9 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -175,6 +175,51 @@ namespace micm } } + for (const auto& object : objects) + { + std::string type = object[TYPE].get(); + + if (type == "CHEM_SPEC") + { + } + else if (type == "RELATIVE_TOLERANCE") + { + } + else if (type == "MECHANISM") + { + } + else if (type == "PHOTOLYSIS") + { + } + else if (type == "EMISSION") + { + } + else if (type == "FIRST_ORDER_LOSS") + { + } + else if (type == "ARRHENIUS") + { + } + else if (type == "TROE") + { + } + else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") + { + } + else if (type == "SURFACE") + { + } + else if (type == "TERNARY_CHEMICAL_ACTIVATION") + { + } + else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") + { + } + + if (status != ConfigParseStatus::Success) + break; + } + status = ConfigParseStatus::Success; return status; From 50c45667275ec31902f349130c21d98df479e1bc Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 14:35:17 -0600 Subject: [PATCH 124/318] Save. --- include/micm/configure/camp_config.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 609f680b9..6e8634f21 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -215,6 +215,10 @@ namespace micm else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") { } + else + { + status = ConfigParseStatus::UnknownKey; + } if (status != ConfigParseStatus::Success) break; From fb41655ebfbe3c420af508a529721f9d7ca64bab Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 15 Oct 2023 17:20:41 -0600 Subject: [PATCH 125/318] Save. --- include/micm/configure/camp_config.hpp | 50 ++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 6e8634f21..df6dfe904 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -181,6 +181,7 @@ namespace micm if (type == "CHEM_SPEC") { + status = ParseChemicalSpecies(object); } else if (type == "RELATIVE_TOLERANCE") { @@ -239,6 +240,55 @@ namespace micm } return true; } + + ConfigParseStatus ParseChemicalSpecies(const json& object) + { + // required keys + const std::string NAME = "name"; + std::array required_keys = { NAME }; + + // Check if it contains the required key(s) + for (const auto& key : required_keys) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + std::string name = object[NAME].get(); + + // Load remaining keys as properties + std::map properties{}; + for (auto& [key, value] : object.items()) + { + if (value.is_number_float()) + properties[key] = value; + } + species_arr_.push_back(Species(name, properties)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseRelativeTolerance(const json& object) + { + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseMechanism(const json& object) + { + std::vector required_keys = { "name", "reactions" }; + for (const auto& key : required_keys) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + std::vector objects; + for (const auto& element : object["reactions"]) + { + objects.push_back(element); + } + + return ParseObjectArray(objects); + } + }; /// @brief Public interface to read and parse config From 5bbf7b78eeceb6d3400b195ca3bfe8044072784c Mon Sep 17 00:00:00 2001 From: dwfncar Date: Sun, 15 Oct 2023 17:25:52 -0600 Subject: [PATCH 126/318] Save. --- include/micm/configure/camp_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index df6dfe904..c44cb7db0 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -286,7 +286,7 @@ namespace micm objects.push_back(element); } - return ParseObjectArray(objects); + // return ParseObjectArray(objects); } }; From a4ce2eb82f97ab8bf893c3d2e5c68b70d1d4f33c Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 07:49:58 -0600 Subject: [PATCH 127/318] Added method ParseObjectArray. --- include/micm/configure/camp_config.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index c44cb7db0..cb17519e4 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -156,7 +156,6 @@ namespace micm ConfigParseStatus Configure(const json& config_data) { // std::cout << config_data.dump(4) << std::endl; - ConfigParseStatus status = ConfigParseStatus::None; std::vector objects; @@ -175,6 +174,15 @@ namespace micm } } + status = ParseObjectArray(objects); + + return status; + } + + ConfigParseStatus ParseObjectArray(const std::vector& objects) + { + ConfigParseStatus status = ConfigParseStatus::None; + for (const auto& object : objects) { std::string type = object[TYPE].get(); From b9fb27a8ab5cb900550b2928e12af6d1ae2862de Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 08:33:58 -0600 Subject: [PATCH 128/318] Added method ParseObjectArray. --- include/micm/configure/camp_config.hpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index cb17519e4..00c7e62b3 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -164,13 +164,7 @@ namespace micm { for (const auto& object : section) { - if (!ValidateJsonWithKey(object, TYPE)) - { - status = ConfigParseStatus::ObjectTypeNotFound; - break; - } objects.push_back(object); - std::cout << object.dump(4) << std::endl; } } @@ -185,8 +179,16 @@ namespace micm for (const auto& object : objects) { + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + break; + } std::string type = object[TYPE].get(); + std::cout << type << std::endl; + std::cout << object.dump(4) << std::endl; + if (type == "CHEM_SPEC") { status = ParseChemicalSpecies(object); @@ -229,8 +231,8 @@ namespace micm status = ConfigParseStatus::UnknownKey; } - if (status != ConfigParseStatus::Success) - break; + // if (status != ConfigParseStatus::Success) + // break; } status = ConfigParseStatus::Success; @@ -294,7 +296,7 @@ namespace micm objects.push_back(element); } - // return ParseObjectArray(objects); + return ParseObjectArray(objects); } }; From 67bcea35b8ef4258678472331c6a2f6989d49474 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 16 Oct 2023 08:51:17 -0600 Subject: [PATCH 129/318] Call ParseMechanism for MECHANISM type. --- include/micm/configure/camp_config.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 00c7e62b3..bed3f40b2 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -195,9 +195,11 @@ namespace micm } else if (type == "RELATIVE_TOLERANCE") { + status = ParseRelativeTolerance(object); } else if (type == "MECHANISM") { + status = ParseMechanism(object); } else if (type == "PHOTOLYSIS") { From 8a75e3d3d489fc20fff8ce952706f90a407003c2 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 16 Oct 2023 09:08:19 -0600 Subject: [PATCH 130/318] Restored ParseReactants and ParseProducts. --- include/micm/configure/camp_config.hpp | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index bed3f40b2..6d618feb3 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -301,6 +301,40 @@ namespace micm return ParseObjectArray(objects); } + std::vector ParseReactants(const json& object) + { + const std::string QTY = "qty"; + std::vector reactants; + for (auto& [key, value] : object.items()) + { + std::size_t qty = 1; + if (value.contains(QTY)) + qty = value[QTY]; + for (std::size_t i = 0; i < qty; ++i) + reactants.push_back(Species(key)); + } + return reactants; + } + + std::vector> ParseProducts(const json& object) + { + const std::string YIELD = "yield"; + constexpr double DEFAULT_YEILD = 1.0; + std::vector> products; + for (auto& [key, value] : object.items()) + { + if (value.contains(YIELD)) + { + products.push_back(std::make_pair(Species(key), value[YIELD])); + } + else + { + products.push_back(std::make_pair(Species(key), DEFAULT_YEILD)); + } + } + return products; + } + }; /// @brief Public interface to read and parse config From dc289e1f2e98264f4b6836436f768bcecd4e9783 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 09:26:04 -0600 Subject: [PATCH 131/318] Restored more reaction type parsers. --- include/micm/configure/camp_config.hpp | 137 +++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 6d618feb3..31b29b890 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -203,15 +203,19 @@ namespace micm } else if (type == "PHOTOLYSIS") { + status = ParsePhotolysis(object); } else if (type == "EMISSION") { + status = ParseEmission(object); } else if (type == "FIRST_ORDER_LOSS") { + status = ParseFirstOrderLoss(object); } else if (type == "ARRHENIUS") { + status = ParseArrhenius(object); } else if (type == "TROE") { @@ -335,6 +339,139 @@ namespace micm return products; } + ConfigParseStatus ParsePhotolysis(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + const std::string MUSICA_NAME = "MUSICA name"; + + for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + std::string name = "PHOTO." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseEmission(const json& object) + { + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; + for (const auto& key : { SPECIES, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + products_object[species] = { { "YIELD", 1.0 } }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); + + std::string name = "EMIS." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseFirstOrderLoss(const json& object) + { + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; + for (const auto& key : { SPECIES, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + reactants_object[species] = { {} }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); + + std::string name = "LOSS." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseArrhenius(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + ArrheniusRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } + if (object.contains("D")) + { + parameters.D_ = object["D"].get(); + } + if (object.contains("E")) + { + parameters.E_ = object["E"].get(); + } + if (object.contains("Ea")) + { + // Calculate 'C' using 'Ea' + parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; + } + + arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + }; /// @brief Public interface to read and parse config From 47bdc7663cbbbccc142221a92725459e63a36cc3 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 16 Oct 2023 09:39:22 -0600 Subject: [PATCH 132/318] Restored more reaction type parsers. --- include/micm/configure/camp_config.hpp | 102 +++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 31b29b890..0b9344e2f 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -219,9 +219,11 @@ namespace micm } else if (type == "TROE") { + status = ParseTroe(object); } else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") { + status = ParseBranched(object); } else if (type == "SURFACE") { @@ -472,6 +474,106 @@ namespace micm return ConfigParseStatus::Success; } + ConfigParseStatus ParseTroe(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + TroeRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } + + troe_rate_arr_.push_back(TroeRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseBranched(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string ALKOXY_PRODUCTS = "alkoxy products"; + const std::string NITRATE_PRODUCTS = "nitrate products"; + const std::string X = "X"; + const std::string Y = "Y"; + const std::string A0 = "a0"; + const std::string N = "n"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); + auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + + BranchedRateConstantParameters parameters; + parameters.X_ = object[X].get(); + parameters.Y_ = object[Y].get(); + parameters.a0_ = object[A0].get(); + parameters.n_ = object[N].get(); + + // Alkoxy branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); + + // Nitrate branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + }; /// @brief Public interface to read and parse config From 0ebc729932d1d4b8e2df19a47be9b6c3404f5904 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 16 Oct 2023 09:58:05 -0600 Subject: [PATCH 133/318] Restored ParseSurface. --- include/micm/configure/camp_config.hpp | 44 ++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 0b9344e2f..a7b91b13d 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -227,6 +227,7 @@ namespace micm } else if (type == "SURFACE") { + status = ParseSurface(object); } else if (type == "TERNARY_CHEMICAL_ACTIVATION") { @@ -574,6 +575,49 @@ namespace micm return ConfigParseStatus::Success; } + ConfigParseStatus ParseSurface(const json& object) + { + const std::string REACTANTS = "gas-phase reactant"; + const std::string PRODUCTS = "gas-phase products"; + const std::string MUSICA_NAME = "MUSICA name"; + const std::string PROBABILITY = "reaction probability"; + for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + std::string species_name = object[REACTANTS].get(); + json reactants_object{}; + reactants_object[species_name] = { {} }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(object[PRODUCTS]); + + Species reactant_species = Species(""); + for (auto& species : species_arr_) + { + if (species.name_ == species_name) + { + reactant_species = species; + break; + } + } + SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), + .species_ = reactant_species }; + + if (object.contains(PROBABILITY)) + { + parameters.reaction_probability_ = object[PROBABILITY].get(); + } + + surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + }; /// @brief Public interface to read and parse config From bd96286f01a4f49b1b78be4b7a18f881f9c3374c Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 10:11:03 -0600 Subject: [PATCH 134/318] Save. --- include/micm/configure/camp_config.hpp | 104 ++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 4 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index a7b91b13d..4c73ea1e2 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -231,21 +231,21 @@ namespace micm } else if (type == "TERNARY_CHEMICAL_ACTIVATION") { + status = ParseTernaryChemicalActivation(object); } else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") { + status = ParseTunneling(object); } else { status = ConfigParseStatus::UnknownKey; } - // if (status != ConfigParseStatus::Success) - // break; + if (status != ConfigParseStatus::Success) + break; } - status = ConfigParseStatus::Success; - return status; } @@ -618,6 +618,102 @@ namespace micm return ConfigParseStatus::Success; } + ConfigParseStatus ParseTernaryChemicalActivation(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + TernaryChemicalActivationRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } + + ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); + + std::unique_ptr rate_ptr = + std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseTunneling(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + // Check required json objects exist + for (const auto& key : { REACTANTS, PRODUCTS }) + { + if (!ValidateJsonWithKey(object, key)) + return ConfigParseStatus::RequiredKeyNotFound; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + TunnelingRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } + + tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } }; /// @brief Public interface to read and parse config From 164fbf1e82f1538108d58aa35225bd591913f736 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 10:29:22 -0600 Subject: [PATCH 135/318] Restored complete set of config parser unit tests. --- include/micm/configure/camp_config.hpp | 3 + test/unit/configure/test_camp_config.cpp | 267 ++++++++++++++++++++++- 2 files changed, 268 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 4c73ea1e2..74ab9ab09 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -148,6 +148,9 @@ namespace micm status = Configure(config_data); + // Assign the parsed 'Species' to 'Phase' + gas_phase_ = Phase(species_arr_); + return status; } diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 46f06a18e..c9e5b7e4d 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -1,12 +1,12 @@ #include -#include +#include TEST(SolverConfig, DetectsInvalidConfigFile) { micm::SolverConfig solverConfig{}; auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); + EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); } TEST(SolverConfig, ReadAndParseCAMPFiles) @@ -18,3 +18,266 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) EXPECT_EQ(micm::ConfigParseStatus::Success, status); } +TEST(SolverConfig, DetectsInvalidCAMPConfigFile) +{ + micm::SolverConfig solverConfig{}; + + // Read and parse the CAMP configure file + micm::ConfigParseStatus status; + status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid"); + EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFileCount, status); + + status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_no_files_key"); + EXPECT_EQ(micm::ConfigParseStatus::CAMPFilesSectionNotFound, status); +} + +TEST(SolverConfig, ReadAndParseSystemObject) +{ + micm::SolverConfig solverConfig; + + // Read and parse the configure files + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + // Check 'gas_phase' in 'System' + EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 9); + + // Check 'phases' in 'System' + EXPECT_EQ(solver_params.system_.phases_.size(), 0); + + // Check 'name' and 'properties' in 'Species' + std::vector> species_name_and_num_properties = { + std::make_pair("M", 0), std::make_pair("Ar", 1), std::make_pair("CO2", 1), + std::make_pair("H2O", 1), std::make_pair("N2", 1), std::make_pair("O1D", 1), + std::make_pair("O", 1), std::make_pair("O2", 1), std::make_pair("O3", 1) + }; + + short idx = 0; + for (const auto& s : solver_params.system_.gas_phase_.species_) + { + EXPECT_EQ(s.name_, species_name_and_num_properties[idx].first); + EXPECT_EQ(s.properties_.size(), species_name_and_num_properties[idx].second); + idx++; + } +} + +TEST(SolverConfig, ReadAndParseProcessObjects) +{ + micm::SolverConfig solverConfig; + + // Read and parse the configure files + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // Check the number of 'Process' created + EXPECT_EQ(process_vector.size(), 15); + + // Check the number of 'reactants' and 'products' in each 'Process' + // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' + int num_reactants_in_each_process[] = { 1, 1, 1, 2, 2, 2, 3, 0, 0, 0, 1, 1, 1, 1, 1 }; + int num_products_in_each_process[] = { 1, 2, 2, 2, 2, 1, 2, 1, 1, 1, 0, 0, 0, 0, 0 }; + double yield_value_of_first_product_in_each_process[] = { 2.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, + 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + int num_phase_in_each_process = 9; + + short idx = 0; + for (const auto& p : process_vector) + { + EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); + EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); + if (num_products_in_each_process[idx] > 0) + EXPECT_EQ(p.products_[0].second, yield_value_of_first_product_in_each_process[idx]); + EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); + idx++; + } + + // Check the parameters for 'ArrheniusRateConstant' + micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; + double A_param[] = { 2.15e-11, 3.3e-11, 8.0e-12, 6.0e-34 }; + double B_param[] = { 0.0, 0.0, 0.0, 2.4 }; + double C_param[] = { 110.0, 55.0, -2060.00, 0.0 }; + double D_param[] = { 300.0, 300.0, 300.0, 300.0 }; + double E_param[] = { 0.0, 0.0, 0.0, 0.0 }; + + for (short i = 3; i < 7; i++) + { + arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(arrhenius_rate_const != nullptr); + EXPECT_EQ(arrhenius_rate_const->parameters_.A_, A_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.B_, B_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.C_, C_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.D_, D_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.E_, E_param[i - 3]); + } + + // Check the number of custom parameters of 'rate constant' in each 'Process' + std::vector> custom_rate_labels = { { "PHOTO.O2_1" }, + { "PHOTO.O3_1" }, + { "PHOTO.O3_2" }, + {}, + {}, + {}, + {}, + { "EMIS.O1D" }, + { "EMIS.O" }, + { "EMIS.O3" }, + { "LOSS.N2" }, + { "LOSS.O2" }, + { "LOSS.CO2" }, + { "LOSS.Ar" }, + { "LOSS.H2O" } }; + + // check photlysis, emissions, and loss reaction labels + idx = 0; + std::vector::iterator it; + for (it = solver_params.processes_.begin(); it != solver_params.processes_.end(); it++, idx++) + { + EXPECT_EQ(it->rate_constant_->SizeCustomParameters(), custom_rate_labels[idx].size()); + for (std::size_t i = 0; i < custom_rate_labels[idx].size(); ++i) + EXPECT_EQ(it->rate_constant_->CustomParameters()[i], custom_rate_labels[idx][i]); + } +} + +TEST(SolverConfig, GettingSolverParamsThrowsExceptionWithFailedParsing) +{ + micm::SolverConfig solverConfig; + micm::ConfigParseStatus status = solverConfig.ReadAndParse("not_a_config_file_directory"); + EXPECT_NE(micm::ConfigParseStatus::Success, status); + EXPECT_ANY_THROW(solverConfig.GetSolverParams()); +} + +// +// Tests for MZ326 configure files +// +TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) +{ + micm::SolverConfig solverConfig; + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + // Check 'gas_phase' in 'System' + EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 6); + + // Check 'phases' in 'System' + EXPECT_EQ(solver_params.system_.phases_.size(), 0); + + // Check 'name' and molecular_weight from 'properties' in 'Species' + std::vector> species_name_and_molecular_weight = { + std::make_pair("ALKNIT", 0.133141), std::make_pair("BZOOH", 0.124135), std::make_pair("C6H5OOH", 0.110109), + std::make_pair("COF2", 0.0), std::make_pair("O2", 0.0), std::make_pair("FUR2O2", 0.0) + }; + + short idx = 0; + for (const auto& s : solver_params.system_.gas_phase_.species_) + { + EXPECT_EQ(s.name_, species_name_and_molecular_weight[idx].first); + EXPECT_EQ(s.properties_.at("molecular weight [kg mol-1]"), species_name_and_molecular_weight[idx].second); + idx++; + } +} + +TEST(SolverConfig, ReadAndParseProcessObjectsfromMZ326) +{ + micm::SolverConfig solverConfig; + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // Check the number of 'Process' created + EXPECT_EQ(process_vector.size(), 5); + + // Check the number of 'reactants' and 'products' in each 'Process' + // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' + int num_reactants_in_each_process[] = { 2, 2, 2, 1, 1 }; + int num_products_in_each_process[] = { 5, 5, 2, 3, 2 }; + int num_phase_in_each_process = 6; + + short idx = 0; + for (const auto& p : process_vector) + { + EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); + EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); + EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); + idx++; + } + + // Check the parameters for 'TroeRateConstant' + micm::TroeRateConstant* troe_rate_const = nullptr; + double k0_A_param[] = { 5.5e-30, 1.07767 }; + double k0_B_param[] = { 0.0, -5.6 }; + double k0_C_param[] = { 0.0, -14000 }; + double kinf_A_param[] = { 8.3e-13, 1.03323e+17 }; + double kinf_B_param[] = { 0.0, 0.0 }; + double kinf_C_param[] = { 0.0, -14000 }; + double Fc_param[] = { 0.6, 0.6 }; + double N_param[] = { -2, 1.5 }; + + idx = 0; + for (short i : { 0, 4 }) + { + troe_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(troe_rate_const != nullptr); + EXPECT_EQ(troe_rate_const->parameters_.k0_A_, k0_A_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.k0_B_, k0_B_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.k0_C_, k0_C_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.kinf_A_, kinf_A_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.kinf_B_, kinf_B_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.kinf_C_, kinf_C_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.Fc_, Fc_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.N_, N_param[idx]); + + idx++; + } + + // Check the name for 'UserDefinedRateConstant' + micm::UserDefinedRateConstant* photolysis_rate_const = nullptr; + std::string photolysis_name = "PHOTO.jterpnit"; + + for (short i : { 3 }) + { + photolysis_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(photolysis_rate_const != nullptr); + EXPECT_EQ(photolysis_rate_const->CustomParameters()[0], photolysis_name); + } + + // Check the parameters for 'ArrheniusRateConstant' + micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; + double A_param[] = { 2.0e-12, 3.8e-12 }; + double B_param[] = { 0.0, 0.0 }; + double C_param[] = { -1 * -6.90325e-21 / 1.3806505e-23, -1 * -2.7613e-21 / 1.3806505e-23 }; + double D_param[] = { 300.0, 300.0 }; + double E_param[] = { 0.0, 0.0 }; + + idx = 0; + for (short i : { 1, 2 }) + { + arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(arrhenius_rate_const != nullptr); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.A_, A_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.B_, B_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.C_, C_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.D_, D_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.E_, E_param[idx]); + + idx++; + } +} From 030fa6b2fea096a25b6f466d6b17da46e50ccf9f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 16 Oct 2023 11:46:40 -0500 Subject: [PATCH 136/318] correcting path --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ed9951a70..e120f8d94 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Copyright (C) 2018-2023 National Center for Atmospheric Research

- +

> **Note** From 75d8f5b219ab5cc54f0e1b4502446087d0044f5c Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 11:08:06 -0600 Subject: [PATCH 137/318] Save. --- include/micm/configure/camp_config.hpp | 27 ++- test/unit/configure/test_camp_config.cpp | 267 +---------------------- 2 files changed, 25 insertions(+), 269 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 74ab9ab09..3b17c88d4 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -135,18 +135,23 @@ namespace micm } // Merge config JSON from CAMP file list - json config_data; + json camp_data; for (const auto& camp_file : camp_files) { std::cout << "JsonReaderPolicy.Parse CAMP file" << camp_file << std::endl; json config_subset = json::parse(std::ifstream(config_dir / camp_file)); + + if (!config_subset.contains(CAMP_DATA)) + return ConfigParseStatus::CAMPDataSectionNotFound; + // json camp_subset = config_subset[CAMP_DATA]; + std::copy(config_subset.begin(), config_subset.end(), - std::back_inserter(config_data)); + std::back_inserter(camp_data)); } ConfigParseStatus status; - status = Configure(config_data); + status = Configure(camp_data); // Assign the parsed 'Species' to 'Phase' gas_phase_ = Phase(species_arr_); @@ -158,7 +163,6 @@ namespace micm ConfigParseStatus Configure(const json& config_data) { - // std::cout << config_data.dump(4) << std::endl; ConfigParseStatus status = ConfigParseStatus::None; std::vector objects; @@ -735,5 +739,20 @@ namespace micm last_parse_status_ = this->Parse(config_dir); return last_parse_status_; } + + /// @brief Creates and returns SolverParameters + /// @return SolverParameters that contains 'System' and a collection of 'Process' + SolverParameters GetSolverParams() + { + if (last_parse_status_ != ConfigParseStatus::Success) + { + std::string msg = "Parsing configuration files failed. The parsing failed with error: " + + configParseStatusToString(last_parse_status_); + throw std::runtime_error(msg); + } + + return SolverParameters( + std::move(System(std::move(this->gas_phase_), std::move(this->phases_))), std::move(this->processes_)); + } }; } // namespace micm diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index c9e5b7e4d..46f06a18e 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -1,12 +1,12 @@ #include -#include +#include TEST(SolverConfig, DetectsInvalidConfigFile) { micm::SolverConfig solverConfig{}; auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); + EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); } TEST(SolverConfig, ReadAndParseCAMPFiles) @@ -18,266 +18,3 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) EXPECT_EQ(micm::ConfigParseStatus::Success, status); } -TEST(SolverConfig, DetectsInvalidCAMPConfigFile) -{ - micm::SolverConfig solverConfig{}; - - // Read and parse the CAMP configure file - micm::ConfigParseStatus status; - status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFileCount, status); - - status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_no_files_key"); - EXPECT_EQ(micm::ConfigParseStatus::CAMPFilesSectionNotFound, status); -} - -TEST(SolverConfig, ReadAndParseSystemObject) -{ - micm::SolverConfig solverConfig; - - // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - // Check 'gas_phase' in 'System' - EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 9); - - // Check 'phases' in 'System' - EXPECT_EQ(solver_params.system_.phases_.size(), 0); - - // Check 'name' and 'properties' in 'Species' - std::vector> species_name_and_num_properties = { - std::make_pair("M", 0), std::make_pair("Ar", 1), std::make_pair("CO2", 1), - std::make_pair("H2O", 1), std::make_pair("N2", 1), std::make_pair("O1D", 1), - std::make_pair("O", 1), std::make_pair("O2", 1), std::make_pair("O3", 1) - }; - - short idx = 0; - for (const auto& s : solver_params.system_.gas_phase_.species_) - { - EXPECT_EQ(s.name_, species_name_and_num_properties[idx].first); - EXPECT_EQ(s.properties_.size(), species_name_and_num_properties[idx].second); - idx++; - } -} - -TEST(SolverConfig, ReadAndParseProcessObjects) -{ - micm::SolverConfig solverConfig; - - // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // Check the number of 'Process' created - EXPECT_EQ(process_vector.size(), 15); - - // Check the number of 'reactants' and 'products' in each 'Process' - // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' - int num_reactants_in_each_process[] = { 1, 1, 1, 2, 2, 2, 3, 0, 0, 0, 1, 1, 1, 1, 1 }; - int num_products_in_each_process[] = { 1, 2, 2, 2, 2, 1, 2, 1, 1, 1, 0, 0, 0, 0, 0 }; - double yield_value_of_first_product_in_each_process[] = { 2.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, - 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; - int num_phase_in_each_process = 9; - - short idx = 0; - for (const auto& p : process_vector) - { - EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); - EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); - if (num_products_in_each_process[idx] > 0) - EXPECT_EQ(p.products_[0].second, yield_value_of_first_product_in_each_process[idx]); - EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); - idx++; - } - - // Check the parameters for 'ArrheniusRateConstant' - micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; - double A_param[] = { 2.15e-11, 3.3e-11, 8.0e-12, 6.0e-34 }; - double B_param[] = { 0.0, 0.0, 0.0, 2.4 }; - double C_param[] = { 110.0, 55.0, -2060.00, 0.0 }; - double D_param[] = { 300.0, 300.0, 300.0, 300.0 }; - double E_param[] = { 0.0, 0.0, 0.0, 0.0 }; - - for (short i = 3; i < 7; i++) - { - arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(arrhenius_rate_const != nullptr); - EXPECT_EQ(arrhenius_rate_const->parameters_.A_, A_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.B_, B_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.C_, C_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.D_, D_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.E_, E_param[i - 3]); - } - - // Check the number of custom parameters of 'rate constant' in each 'Process' - std::vector> custom_rate_labels = { { "PHOTO.O2_1" }, - { "PHOTO.O3_1" }, - { "PHOTO.O3_2" }, - {}, - {}, - {}, - {}, - { "EMIS.O1D" }, - { "EMIS.O" }, - { "EMIS.O3" }, - { "LOSS.N2" }, - { "LOSS.O2" }, - { "LOSS.CO2" }, - { "LOSS.Ar" }, - { "LOSS.H2O" } }; - - // check photlysis, emissions, and loss reaction labels - idx = 0; - std::vector::iterator it; - for (it = solver_params.processes_.begin(); it != solver_params.processes_.end(); it++, idx++) - { - EXPECT_EQ(it->rate_constant_->SizeCustomParameters(), custom_rate_labels[idx].size()); - for (std::size_t i = 0; i < custom_rate_labels[idx].size(); ++i) - EXPECT_EQ(it->rate_constant_->CustomParameters()[i], custom_rate_labels[idx][i]); - } -} - -TEST(SolverConfig, GettingSolverParamsThrowsExceptionWithFailedParsing) -{ - micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_NE(micm::ConfigParseStatus::Success, status); - EXPECT_ANY_THROW(solverConfig.GetSolverParams()); -} - -// -// Tests for MZ326 configure files -// -TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) -{ - micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - // Check 'gas_phase' in 'System' - EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 6); - - // Check 'phases' in 'System' - EXPECT_EQ(solver_params.system_.phases_.size(), 0); - - // Check 'name' and molecular_weight from 'properties' in 'Species' - std::vector> species_name_and_molecular_weight = { - std::make_pair("ALKNIT", 0.133141), std::make_pair("BZOOH", 0.124135), std::make_pair("C6H5OOH", 0.110109), - std::make_pair("COF2", 0.0), std::make_pair("O2", 0.0), std::make_pair("FUR2O2", 0.0) - }; - - short idx = 0; - for (const auto& s : solver_params.system_.gas_phase_.species_) - { - EXPECT_EQ(s.name_, species_name_and_molecular_weight[idx].first); - EXPECT_EQ(s.properties_.at("molecular weight [kg mol-1]"), species_name_and_molecular_weight[idx].second); - idx++; - } -} - -TEST(SolverConfig, ReadAndParseProcessObjectsfromMZ326) -{ - micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // Check the number of 'Process' created - EXPECT_EQ(process_vector.size(), 5); - - // Check the number of 'reactants' and 'products' in each 'Process' - // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' - int num_reactants_in_each_process[] = { 2, 2, 2, 1, 1 }; - int num_products_in_each_process[] = { 5, 5, 2, 3, 2 }; - int num_phase_in_each_process = 6; - - short idx = 0; - for (const auto& p : process_vector) - { - EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); - EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); - EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); - idx++; - } - - // Check the parameters for 'TroeRateConstant' - micm::TroeRateConstant* troe_rate_const = nullptr; - double k0_A_param[] = { 5.5e-30, 1.07767 }; - double k0_B_param[] = { 0.0, -5.6 }; - double k0_C_param[] = { 0.0, -14000 }; - double kinf_A_param[] = { 8.3e-13, 1.03323e+17 }; - double kinf_B_param[] = { 0.0, 0.0 }; - double kinf_C_param[] = { 0.0, -14000 }; - double Fc_param[] = { 0.6, 0.6 }; - double N_param[] = { -2, 1.5 }; - - idx = 0; - for (short i : { 0, 4 }) - { - troe_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(troe_rate_const != nullptr); - EXPECT_EQ(troe_rate_const->parameters_.k0_A_, k0_A_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.k0_B_, k0_B_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.k0_C_, k0_C_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.kinf_A_, kinf_A_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.kinf_B_, kinf_B_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.kinf_C_, kinf_C_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.Fc_, Fc_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.N_, N_param[idx]); - - idx++; - } - - // Check the name for 'UserDefinedRateConstant' - micm::UserDefinedRateConstant* photolysis_rate_const = nullptr; - std::string photolysis_name = "PHOTO.jterpnit"; - - for (short i : { 3 }) - { - photolysis_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(photolysis_rate_const != nullptr); - EXPECT_EQ(photolysis_rate_const->CustomParameters()[0], photolysis_name); - } - - // Check the parameters for 'ArrheniusRateConstant' - micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; - double A_param[] = { 2.0e-12, 3.8e-12 }; - double B_param[] = { 0.0, 0.0 }; - double C_param[] = { -1 * -6.90325e-21 / 1.3806505e-23, -1 * -2.7613e-21 / 1.3806505e-23 }; - double D_param[] = { 300.0, 300.0 }; - double E_param[] = { 0.0, 0.0 }; - - idx = 0; - for (short i : { 1, 2 }) - { - arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(arrhenius_rate_const != nullptr); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.A_, A_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.B_, B_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.C_, C_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.D_, D_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.E_, E_param[idx]); - - idx++; - } -} From da0f317bd26734510130b0663ef96e9b0b764e16 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 11:55:45 -0600 Subject: [PATCH 138/318] If no CAMP config, use species.json and mechanism.json files for unit test purposes. --- include/micm/configure/camp_config.hpp | 9 ++++++++- test/unit/configure/test_camp_config.cpp | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 3b17c88d4..32ecc6aa3 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -101,6 +101,10 @@ namespace micm // Constants // Configure files static const inline std::string CAMP_CONFIG = "config.json"; + static const inline std::string SPECIES_CONFIG = "species.json"; + static const inline std::string MECHANISM_CONFIG = "mechanism.json"; + static const inline std::string REACTIONS_CONFIG = "reactions.json"; + static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; // Common JSON static const inline std::string CAMP_DATA = "camp-data"; @@ -131,7 +135,10 @@ namespace micm } else { - return ConfigParseStatus::InvalidCAMPFilePath; + // return ConfigParseStatus::InvalidCAMPFilePath; + // TODO: add complete backward compatibility with original parser + camp_files.push_back(config_dir / SPECIES_CONFIG); + camp_files.push_back(config_dir / MECHANISM_CONFIG); } // Merge config JSON from CAMP file list diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 46f06a18e..37f7d7bb9 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -2,12 +2,14 @@ #include +/* TEST(SolverConfig, DetectsInvalidConfigFile) { micm::SolverConfig solverConfig{}; auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); } +*/ TEST(SolverConfig, ReadAndParseCAMPFiles) { From d5e8d0aa609a8b1f2fb954883dbd5b56140f02e4 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 12:19:42 -0600 Subject: [PATCH 139/318] Prepend config_dir to CAMP files before push backs. --- include/micm/configure/camp_config.hpp | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 32ecc6aa3..33e4e85a2 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -130,23 +130,39 @@ namespace micm for (const auto& element : camp_data[CAMP_FILES]) { - camp_files.push_back(element.get()); + camp_files.push_back(config_dir / element.get()); } } else { // return ConfigParseStatus::InvalidCAMPFilePath; // TODO: add complete backward compatibility with original parser - camp_files.push_back(config_dir / SPECIES_CONFIG); - camp_files.push_back(config_dir / MECHANISM_CONFIG); + std::filesystem::path species_config(config_dir / SPECIES_CONFIG); + std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); + if (std::filesystem::exists(species_config)) + { + camp_files.push_back(config_dir / SPECIES_CONFIG); + } + else + { + return ConfigParseStatus::InvalidCAMPFilePath; + } + if (std::filesystem::exists(mechanism_config)) + { + camp_files.push_back(config_dir / MECHANISM_CONFIG); + } + else + { + return ConfigParseStatus::InvalidCAMPFilePath; + } } // Merge config JSON from CAMP file list json camp_data; for (const auto& camp_file : camp_files) { - std::cout << "JsonReaderPolicy.Parse CAMP file" << camp_file << std::endl; - json config_subset = json::parse(std::ifstream(config_dir / camp_file)); + std::cout << "JsonReaderPolicy.Parse CAMP file " << camp_file << std::endl; + json config_subset = json::parse(std::ifstream(camp_file)); if (!config_subset.contains(CAMP_DATA)) return ConfigParseStatus::CAMPDataSectionNotFound; From f816b170e69177e35875ef29802b9865ce21154e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 12:22:14 -0600 Subject: [PATCH 140/318] Added ReadAndParseSystemObject. --- test/unit/configure/test_camp_config.cpp | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 37f7d7bb9..a46457314 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -20,3 +20,35 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) EXPECT_EQ(micm::ConfigParseStatus::Success, status); } +TEST(SolverConfig, ReadAndParseSystemObject) +{ + micm::SolverConfig solverConfig; + + // Read and parse the configure files + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + // Check 'gas_phase' in 'System' + EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 9); + + // Check 'phases' in 'System' + EXPECT_EQ(solver_params.system_.phases_.size(), 0); + + // Check 'name' and 'properties' in 'Species' + std::vector> species_name_and_num_properties = { + std::make_pair("M", 0), std::make_pair("Ar", 1), std::make_pair("CO2", 1), + std::make_pair("H2O", 1), std::make_pair("N2", 1), std::make_pair("O1D", 1), + std::make_pair("O", 1), std::make_pair("O2", 1), std::make_pair("O3", 1) + }; + + short idx = 0; + for (const auto& s : solver_params.system_.gas_phase_.species_) + { + EXPECT_EQ(s.name_, species_name_and_num_properties[idx].first); + EXPECT_EQ(s.properties_.size(), species_name_and_num_properties[idx].second); + idx++; + } +} From ebee9407550459a65d48b77289b3ce60a6d9c58a Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 12:32:02 -0600 Subject: [PATCH 141/318] Added NoConfigFilesFound error. --- include/micm/configure/camp_config.hpp | 28 +++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 33e4e85a2..350b023ed 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -32,6 +32,7 @@ namespace micm InvalidKey, UnknownKey, InvalidCAMPFilePath, + NoConfigFilesFound, CAMPFilesSectionNotFound, CAMPDataSectionNotFound, InvalidSpecies, @@ -49,6 +50,7 @@ namespace micm 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"; @@ -135,28 +137,36 @@ namespace micm } else { - // return ConfigParseStatus::InvalidCAMPFilePath; - // TODO: add complete backward compatibility with original parser std::filesystem::path species_config(config_dir / SPECIES_CONFIG); std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); + std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); + std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); if (std::filesystem::exists(species_config)) { - camp_files.push_back(config_dir / SPECIES_CONFIG); + camp_files.push_back(species_config); } - else + if (std::filesystem::exists(mechanism_config)) { - return ConfigParseStatus::InvalidCAMPFilePath; + camp_files.push_back(mechanism_config); } - if (std::filesystem::exists(mechanism_config)) + if (std::filesystem::exists(reactions_config)) { - camp_files.push_back(config_dir / MECHANISM_CONFIG); + camp_files.push_back(reactions_config); } - else + if (std::filesystem::exists(tolerance_config)) { - return ConfigParseStatus::InvalidCAMPFilePath; + camp_files.push_back(tolerance_config); } } + // No config files found + if (camp_files.size() < 1) + { + std::string msg = "No config files found"; + std::cerr << msg << std::endl; + return ConfigParseStatus::NoConfigFilesFound; + } + // Merge config JSON from CAMP file list json camp_data; for (const auto& camp_file : camp_files) From dbe47f3723d98bec005dff9b1acf21e04fbe7719 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 16 Oct 2023 12:58:23 -0600 Subject: [PATCH 142/318] Added ReadAndParseProcessObjects test. --- test/unit/configure/test_camp_config.cpp | 84 ++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index a46457314..82ded148d 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -52,3 +52,87 @@ TEST(SolverConfig, ReadAndParseSystemObject) idx++; } } + +TEST(SolverConfig, ReadAndParseProcessObjects) +{ + micm::SolverConfig solverConfig; + + // Read and parse the configure files + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // Check the number of 'Process' created + EXPECT_EQ(process_vector.size(), 15); + + // Check the number of 'reactants' and 'products' in each 'Process' + // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' + int num_reactants_in_each_process[] = { 1, 1, 1, 2, 2, 2, 3, 0, 0, 0, 1, 1, 1, 1, 1 }; + int num_products_in_each_process[] = { 1, 2, 2, 2, 2, 1, 2, 1, 1, 1, 0, 0, 0, 0, 0 }; + double yield_value_of_first_product_in_each_process[] = { 2.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, + 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; + int num_phase_in_each_process = 9; + + short idx = 0; + for (const auto& p : process_vector) + { + EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); + EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); + if (num_products_in_each_process[idx] > 0) + EXPECT_EQ(p.products_[0].second, yield_value_of_first_product_in_each_process[idx]); + // EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); + idx++; + } + + // Check the parameters for 'ArrheniusRateConstant' + micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; + double A_param[] = { 2.15e-11, 3.3e-11, 8.0e-12, 6.0e-34 }; + double B_param[] = { 0.0, 0.0, 0.0, 2.4 }; + double C_param[] = { 110.0, 55.0, -2060.00, 0.0 }; + double D_param[] = { 300.0, 300.0, 300.0, 300.0 }; + double E_param[] = { 0.0, 0.0, 0.0, 0.0 }; + + for (short i = 3; i < 7; i++) + { + arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(arrhenius_rate_const != nullptr); + EXPECT_EQ(arrhenius_rate_const->parameters_.A_, A_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.B_, B_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.C_, C_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.D_, D_param[i - 3]); + EXPECT_EQ(arrhenius_rate_const->parameters_.E_, E_param[i - 3]); + } + + // Check the number of custom parameters of 'rate constant' in each 'Process' + std::vector> custom_rate_labels = { { "PHOTO.O2_1" }, + { "PHOTO.O3_1" }, + { "PHOTO.O3_2" }, + {}, + {}, + {}, + {}, + { "EMIS.O1D" }, + { "EMIS.O" }, + { "EMIS.O3" }, + { "LOSS.N2" }, + { "LOSS.O2" }, + { "LOSS.CO2" }, + { "LOSS.Ar" }, + { "LOSS.H2O" } }; + + // check photlysis, emissions, and loss reaction labels + idx = 0; + std::vector::iterator it; + for (it = solver_params.processes_.begin(); it != solver_params.processes_.end(); it++, idx++) + { + EXPECT_EQ(it->rate_constant_->SizeCustomParameters(), custom_rate_labels[idx].size()); + for (std::size_t i = 0; i < custom_rate_labels[idx].size(); ++i) + EXPECT_EQ(it->rate_constant_->CustomParameters()[i], custom_rate_labels[idx][i]); + } +} + From 3c4d021e3b421d7791e9021737954bb572d1e81c Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 17 Oct 2023 10:36:33 -0500 Subject: [PATCH 143/318] Update README.md Add release badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e120f8d94..7cd4a6db1 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ MICM Chemistry Model Independent Chemical Module. MICM can be used to configure and solve atmospheric chemistry systems. +[![GitHub Releases](https://img.shields.io/github/release/NCAR/micm.svg)](https://github.com/NCAR/micm/releases) [![License](https://img.shields.io/github/license/NCAR/micm.svg)](https://github.com/NCAR/micm/blob/master/LICENSE) [![Docker builds](https://github.com/NCAR/micm/actions/workflows/docker_and_coverage.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/docker_and_coverage.yml) [![Windows](https://github.com/NCAR/micm/actions/workflows/windows.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/windows.yml) From 3bffc5db9f286fca99ea0e4374961d4cae131b38 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 18 Oct 2023 10:53:34 -0500 Subject: [PATCH 144/318] surface rate constants can only have one reactant --- include/micm/process/process.hpp | 28 ++++++++++++++++++++++++---- include/micm/system/species.hpp | 1 + test/unit/process/test_process.cpp | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index cd7aee650..7eb09a62a 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -6,6 +6,14 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include #include @@ -59,6 +67,21 @@ namespace micm rate_constant_(std::move(rate_constant)), phase_(phase) { + if (dynamic_cast(rate_constant_.get())) { + } + else if (dynamic_cast(rate_constant_.get())) { + } + else if (dynamic_cast(rate_constant_.get())) { + if (reactants_.size() > 1) { + throw std::runtime_error("A surface rate constant can only have one reactant"); + } + } + else if (dynamic_cast(rate_constant_.get())) { + } + else if (dynamic_cast(rate_constant_.get())) { + } + else if (dynamic_cast(rate_constant_.get())) { + } } Process& operator=(const Process& other) @@ -148,10 +171,7 @@ namespace micm }; inline Process::Process(ProcessBuilder& builder) - : reactants_(builder.reactants_), - products_(builder.products_), - rate_constant_(std::move(builder.rate_constant_)), - phase_(builder.phase_) + : Process(builder.reactants_, builder.products_, std::move(builder.rate_constant_), builder.phase_) { } diff --git a/include/micm/system/species.hpp b/include/micm/system/species.hpp index e64adcef4..81f1e14b6 100644 --- a/include/micm/system/species.hpp +++ b/include/micm/system/species.hpp @@ -5,6 +5,7 @@ #include #include +#include namespace micm { diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index 8a1b88a45..2b27162b5 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -85,3 +85,18 @@ TEST(Process, VectorMatrix) testProcessUpdateState(5); testProcessUpdateState(5); } + +TEST(Process, SurfaceRateConstantOnlyHasOneReactant) +{ + micm::Species c("c", { { "molecular weight [kg mol-1]", 0.025 }, { "diffusion coefficient [m2 s-1]", 2.3e2 } }); + micm::Species e("e"); + + micm::Phase gas_phase({c, e}); + EXPECT_ANY_THROW( + micm::Process r = micm::Process::create() + .reactants({ c, c }) + .products({ yields(e, 1) }) + .rate_constant(micm::SurfaceRateConstant({ .label_ = "c", .species_ = c, .reaction_probability_ = 0.90 })) + .phase(gas_phase); + ); +} From 187b3ed1b14a29e7bec9ae17392d1192801a444e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 18 Oct 2023 17:09:08 -0500 Subject: [PATCH 145/318] 299 install micm and use in a new project (#306) * maybe setting up codespaces * trying to fix codespace * trying again * building under workspace directory * adding archive directions * writing instructions for direct inclusion after install * debugging instructions * adding test for fetch content and find package --- .devcontainer/Dockerfile.codespace | 12 + .devcontainer/devcontainer.json | 10 + CMakeLists.txt | 4 - docker/Dockerfile | 15 ++ docs/source/contributing/index.rst | 11 +- docs/source/getting_started.rst | 6 + docs/source/user_guide/but_how_fast_is_it.rst | 8 +- docs/source/user_guide/images/codespaces.png | Bin 0 -> 283905 bytes docs/source/user_guide/index.rst | 1 + .../user_guide/installation_and_usage.rst | 215 ++++++++++++++++++ .../source/user_guide/multiple_grid_cells.rst | 21 +- .../user_guide/solver_configurations.rst | 4 +- .../user_defined_rate_constant_tutorial.rst | 16 +- packaging/CMakeLists.txt | 2 + test/integration/cmake/README.md | 3 + .../cmake/configs/robertson/reactions.json | 43 ++++ .../cmake/configs/robertson/species.json | 16 ++ .../cmake/fetch_content/CMakeLists.txt | 37 +++ .../cmake/find_package/CMakeLists.txt | 30 +++ test/integration/cmake/test_micm.cpp | 87 +++++++ test/tutorial/test_but_how_fast_is_it.cpp | 8 +- test/tutorial/test_multiple_grid_cells.cpp | 8 +- ...st_rate_constants_user_defined_by_hand.cpp | 8 +- ...ate_constants_user_defined_with_config.cpp | 8 +- test/tutorial/test_solver_configuration.cpp | 16 +- 25 files changed, 513 insertions(+), 76 deletions(-) create mode 100644 .devcontainer/Dockerfile.codespace create mode 100644 .devcontainer/devcontainer.json create mode 100644 docs/source/user_guide/images/codespaces.png create mode 100644 docs/source/user_guide/installation_and_usage.rst create mode 100644 test/integration/cmake/README.md create mode 100644 test/integration/cmake/configs/robertson/reactions.json create mode 100644 test/integration/cmake/configs/robertson/species.json create mode 100644 test/integration/cmake/fetch_content/CMakeLists.txt create mode 100644 test/integration/cmake/find_package/CMakeLists.txt create mode 100644 test/integration/cmake/test_micm.cpp diff --git a/.devcontainer/Dockerfile.codespace b/.devcontainer/Dockerfile.codespace new file mode 100644 index 000000000..215600811 --- /dev/null +++ b/.devcontainer/Dockerfile.codespace @@ -0,0 +1,12 @@ +FROM fedora:37 + +RUN dnf -y update \ + && dnf -y install \ + cmake \ + gcc-c++ \ + gdb \ + git \ + make \ + zlib-devel \ + llvm-devel \ + && dnf clean all \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..eccf8125f --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,10 @@ +{ + "name": "MICM Development Environment", + "dockerFile": "Dockerfile.codespace", + "extensions": [ + "ms-vscode.cpptools" + ], + "settings": { + }, + "postCreateCommand": "cd /workspaces/micm && mkdir build && cd build && cmake -D CMAKE_BUILD_TYPE=debug -D ENABLE_CLANG_TIDY:BOOL=FALSE -D ENABLE_LLVM:BOOL=TRUE -D ENABLE_MEMCHECK:BOOL=TRUE .. && make install -j 2" +} diff --git a/CMakeLists.txt b/CMakeLists.txt index f105b8a20..5ea8bc748 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,10 +27,6 @@ set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${PROJECT_SOURCE_DIR}/cmake") # Set up include and lib directories set(MICM_LIB_DIR "${PROJECT_BINARY_DIR}/lib") -include(GNUInstallDirs) -set(INSTALL_PREFIX "micm-${PROJECT_VERSION}" ) -set(INSTALL_MOD_DIR "${INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") - option(ENABLE_CLANG_TIDY "Enable clang-tiday to format source code" OFF) option(ENABLE_MPI "Enable MPI parallel support" OFF) option(ENABLE_OPENMP "Enable OpenMP support" OFF) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1241bd0bf..1954e7222 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -7,6 +7,7 @@ RUN dnf -y update \ gdb \ git \ make \ + json-devel \ && dnf clean all # copy the MICM code @@ -21,4 +22,18 @@ RUN mkdir /build \ ../micm \ && make install -j 8 +# now test if we can use the installed files +RUN cd /micm/test/integration/cmake/find_package \ + && mkdir build && cd build \ + && cmake .. \ + && make \ + && make test + +# now test that fetch content can be used +RUN cd /micm/test/integration/cmake/fetch_content \ + && mkdir build && cd build \ + && cmake .. \ + && make \ + && make test + WORKDIR /build \ No newline at end of file diff --git a/docs/source/contributing/index.rst b/docs/source/contributing/index.rst index 3e5950e5b..a684149bb 100644 --- a/docs/source/contributing/index.rst +++ b/docs/source/contributing/index.rst @@ -1,3 +1,4 @@ +.. _Contributing: Contributing ============ @@ -40,14 +41,8 @@ of ``clang-format`` enforces some consistency which means moving from file to fi Setting up developer environments --------------------------------- -macOS -^^^^^ - -Linux -^^^^^ - -Windows -^^^^^^^ +Most developers work on micm in VS Code. For instructions on how to setup micm with Xcode or Visual Studio, +please see the :ref:`Debugging` section of the :ref:`Installation and usage` guide. Building the documentation -------------------------- diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index 1483c0697..cf1ce1d07 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -5,6 +5,9 @@ Getting Started Build and Test ============== +Configuring for different platforms and environments is shown here. There is an additional tutorial which covers +some other specifics: :ref:`Installation and usage`. + CPU --- To build and install MICM locally, you must have the following libraries installed: @@ -48,6 +51,9 @@ others require that you have libraries installed on your system. - OpenMP - On macOS, you either need to configure cmake to use gcc which ships with OpenMP (either ``CXX=g++ cmake -DENABLE_OPENMP=ON ..`` or ``cmake -DCMAKE_CXX_COMPILER=g++ -DENABLE_OPENMP=ON ..``) +For more ways to build and install micm, see :ref:`Installation and usage`. If you would like instructions for building +the docs, see :ref:`Contributing`. + Docker Container ---------------- diff --git a/docs/source/user_guide/but_how_fast_is_it.rst b/docs/source/user_guide/but_how_fast_is_it.rst index de67d03c0..2d4e2ad4e 100644 --- a/docs/source/user_guide/but_how_fast_is_it.rst +++ b/docs/source/user_guide/but_how_fast_is_it.rst @@ -56,9 +56,9 @@ in the stats object. .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp :language: cpp - :lines: 75-86 + :lines: 69-80 -.. code-block:: console +.. code-block:: bash Solver state: Converged accepted: 20 @@ -78,9 +78,9 @@ wasted, for the ``false`` version. .. literalinclude:: ../../../test/tutorial/test_but_how_fast_is_it.cpp :language: cpp - :lines: 88-96 + :lines: 82-90 -.. code-block:: console +.. code-block:: bash Total solve time: 24416 nanoseconds total_forcing_time: 3167 nanoseconds diff --git a/docs/source/user_guide/images/codespaces.png b/docs/source/user_guide/images/codespaces.png new file mode 100644 index 0000000000000000000000000000000000000000..149765743d3d7e9253ee4d0edcdde69a269ee890 GIT binary patch literal 283905 zcmeFYbyU?&*EfzJprj}W5)w*x!=Xe*BzfneU#NJ$v@-`Rvd4t-P!RCK@3c0s;c2df_oy|=U5S+)Y6ckEY$Znl4 z<8Ix$6#$RtkTjjfoZcaw8|O#}$I<6R4}@J_2(FsOz*4kcmO|U0)yqtAsKJ>6&D6cK z)MceN>Sq_O%thz={Y~1mpEyF8m=!l^!iO)!%Dl>4lncBWkv!eLpG~H_-%UZ1$#Q#w z=&wwb_-z~Wv>>*K8(24Z~V3~ zFBo7&Uz_sGUVa|@jH5D0tNW!Y&u)4yFUlg%LGH3yURGCq3DSe}xX7RK*`1VR-@Bbz z6jHge0Y11J?E>d5A-WXNPYdo6@ZOhyjWV|5ebuxD%|HM4F%CBXNt?Oif)xd}h1gSL zxOa0~2uTe-p=*v%n)GpGLSy09$_xrq!Ni9WRIN<}5jkJW&X9#n+J(JOXAj(! z?h{lYds#rO^WEbG5zK$kxgZe}BhDjt>Kr*c$(dSTC7K@`A*r+3#mB)H))5e}i_C#V zzL1fI7+PB~=^I%a7&AFr*#J{TK;Rd0w$V4VFouyC7@L|y1t_*^8!5=ljRYvvIAvI5 zY($LB%%8#SjFsTB%7$hS3{hiyLpyUDn7K8S>{_P2 zfwconfPwl_m5q$dAMsH8zqA1K!R)MW!_3OW!fa*5{Ld%sVPcLz$X^5ck5AYu zgN@CsXl!roU}tD7=4cFsJ^H5z2}v3GKN7B|WNL0@^K()_dH>Rxk>Q_qHV$@{Kie1? zG8*+y^tPRbLAV2>!VKp!?;9@soHeNPHHZBex z16~tD18z2+e~6H?GY3YjZ~4zrU5hfh7R6)2%gt%P!)VOHWyr|E&c?y0udmO`sL#V@ z!ehv3$jM>A@>A6HvO$F9B?TzhnEp{BZ>bM6v9_}kppelwB$HG6yREXhm9Y{`|5|IT zT)f;YT&%n-tl)=}jqUG7s>XKqz(B8sva&F-bNs9^GK7c&k@~>U%&qiIjhStrravpL zmk}ajZE0<%1h%I!5d3$syt%Wng;;0AwWZ5 zwe+v|%5R}R%df52(&4&T1!@k4^K0Zkt6uzF2Oi=0hcNa3R0H%45wSDYhgsVxTU%QS zP+aSi?0VKer*K^@13ZM0K1^R!9|oqz#=^!9VP%1^vMRH3Ls&Q=EG&#aKmTrTZDelZ z^8eTR+8>bd1Np!#pPAc({#||+{c;jY#(f;dToXm``O$ca=k(Gt*g4qQP4o>}e{ISCr*i~W z!TPUrw^Mf36>f-A4Vf% z6MY9u7=@scp`9_iQpP;D6Nkzt;8tOkHSy zU&D=|Ak=gMm+Woy+J10BM=^LVA&PKy{Xe-rGXj*{wt1#*kAQHW^7`k7q~hakP>2eX zlo3N+Mnk`kj39K(Sr3Zt!o*&{{)&7M{tjf$aB=~BMTQ_LDy-~0zCPvRraW|UwKJ07 zPSx=^0yDhGr^p)3D-Y+UutMOi098?iGBJhRa*amE(&6!%ebY553v*;qWIS@jVg{U> zub*Ia5)Ky`ku!{$u$^^ZT;LGgzWt5_F==^zKw`01yRY7Ud8uxT7xm4Zr?<%e@nP14 zn>mpy#riLWcb@vcf2M%Tp@{LXjqjwUE2^j!wY$R@D@=PKVT^F-ec*)+_Ne;mkpO zmKD}m>6jjqG7YN;($Z8K8re#<{Jv&kmGrzxVHY3t!V$lmoZtp~6z_)?F|p4qcnHPJ zhtX3fq14pWek!>QCThJ-rIGYo zIuVl`@@}ALeh^9{8nCC}3j#X{%TnBbuf%E;RI>Snx-~c1M`m$iv*BfT5SIMr@oL z4%$hdx|XrudG7O|cqgu8-I5j;Nt==O$^p-&SA7c^S$mZ2Kn&L&%zU-z$Y;Oo@g<&j zkEU|MqCAL`!YeGTV7XX=l$@4U!NdH|@+!2h*5D?{`f*Nr2^Wfl8Q?tiM^U&@*e6%r z@^)s+x&pFLy0syrE;p@AsCea(y^N&x<1#xZAg9^=O0cbjq?d5Hx;oQ`7rS-DWeTi7 z1a)9X7qi~fDJJo#(;XF+ftQ?Fgm{&Ma?WqUzR)lZ3=BBO3u2M*%0@B5-n7d$L-19?bma1_J12pMu8?J%QLVhQ})uh<>QfXzvu+k#_GVv%enda z+t}FF3wtTa*=tXY7ehK1SicfVVZ?2ksJOHQgp?VutkBXsadbHD?x9zC{@|GQFg5XR zW_sl`ozSsyk2)+#m#mTN&%R2|n|v*s`3S;BT(3i|AARR32ZfkI{~G;PF(jd&n@oCROMDq<1yd}r?Wfaln??{%LqWzY z-qEOmg`_txreuAQxw8$wAf~5DY2HAs_dBl>V30bCtam+9(9jrPIn!MJlv~&A!^oDZ zenz39f~RS8a}lpVT-V9NL438-n_Vuc2XmqBCpsF9YQs4B}ktJpp z&qU(PYBJ!^lgE{Y_tR+5#Y02zs}&Rmh;V}-K2OMAjpe1O#7FVO;W47+r{+7yFS?b( zSy8cEjM%7saVm-Z<)1`mw(q@Q?5IkXETqT z<22KpAf`5g|aeReLslQbCq1D>ZdiZ{T)w zgmNdHl;jm$P+}K*)`mkiS6U@s0==+vVaI@@UX4)Tz-|34b?!|?;B|rs9R$EH2c2lJ ziFfAP2l6#X`peGG5g)qFb)Yy!(EL`p|NG}EDrJPhxS)W8HD6q*5V9ap?Q&34sT+T7 z@3p_}9m^Kd0B3t0=c-Xe$ngS`5V~nTn@fFf?wvmEP!hcL$X+RAiueWG z-8U>UdmF%E=;XY8;&o@=FrYt84Ho&nc4e}dgh>tghSv)WD5Q*ipdhWe>5(RDY_r)A zU4w9Yo}CYh&dCXK-QH&R%fUX&F^99`2?EZSkHN@4e*U28w$a%EJnd}@QBgCE%FU^q z9&XcK{tW5r%1*aG8~cu0P6Dqh4L>~S!?cgut2e_t`zym@Xj1<7U&=urYKKoe6TTJj zTuQy(QDDMxy?HT*c%E+DtaLkyHf`5| z1sgM=uP@GY?zbP!Qll615yO7O1f^zA=PR*fm6%M4mo(1w);r0)LkGIj$nr7odI5BB zzw|A5G#u`Tnp{=&#){7|JT|Jw{~Nh9fzzJYSfz~mSd6sE!$i0o&G*jk@#eadq`f5z zX6^mmIBrk!SoN{x^GO4)flO2E&FWmo#wbeTLv$3K#l=O-sj#N3fW_xPpliAZx~BS4EJ?j(waMGfIPk`J?aCYj5=iy5L>I^vGyFe<+@~ zSW#pakHACES9L8CtTApY2axc)6tuKsk}fV^(}9wsg@hX0R)fbnyS9$f2F&{7hpsAfa&98tfI+6zu>*udhE%+U2$y=)bjo{2kj3DB?y(?p~tqePO2gXfMUqqU48AWbMhcx8x~%x{HzMY^0Ge zs+gGk(RZ2478!s?Qf|Vf`XEzP^Au1qk>sGfPi!EI)v0g2QwwR2U%cN?@ ziJsi`r|lijq~$&=}kq92Rn{cU3sieC3Q8I8S@LeYtXEFbwC~!GIoVzuWsaNf!iXM1)S*!&unwsj2f5iyCn3?L)AIG1 zQoK+RiBHRv%^C6v1ue6-_a3B?H>lvApiYWvHbI#mQBh?~_fZC
#t=bnN~DN7m`l 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

JiT8t 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

ezUSe@4dN+d!$~0H6u8`C`gk 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≪&3~tm|1&B!D*wTv|IZ=19g*DsLCpK-|EmxG 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

Up3A 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≤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 ME+>}#RQKcm1$=B4t^fc4 literal 0 HcmV?d00001 diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 4a2d3b497..7896b135a 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -40,6 +40,7 @@ If you would like to include the json examples, you must configure micm to build :maxdepth: 1 :caption: Contents: + installation_and_usage rate_constant_tutorial user_defined_rate_constant_tutorial multiple_grid_cells diff --git a/docs/source/user_guide/installation_and_usage.rst b/docs/source/user_guide/installation_and_usage.rst new file mode 100644 index 000000000..e0287a4db --- /dev/null +++ b/docs/source/user_guide/installation_and_usage.rst @@ -0,0 +1,215 @@ +.. _Installation and usage: + +Installation and usage +====================== + +This tutorial is going to focus **only** and how to install micm and/or include it +into your project in multiple different ways. Any API specific details will be elided and will be covered +in another tutorial. + + +Github codespace +---------------- + +If you want to play around with micm without figuring out how to include it in your local setup, this is the **easiest** +option. Github codespaces offers a cloud-hosted version of Visual Studio Code configured to work with specific projects. +We've set one up for micm. It'll allow you to instantly run the tests and make changes. Please note that there is a cap on +the number of hours your personal github account has each month. Follow these instructions to see your +`github codespace usage `_. +At the time of this writing, there was a maximum of 120 core-houres allowed for github codespaces for free accounts. + +To set this up, on the github page for `micm `_, poke on the green code button and choose the +codespaces tab and select "create codespace on main". This will open up a new tab and start building a cloud environment +running an instance of VSCode with the micm repository displayed and all of the tests prebuilt. + +.. image:: images/codespaces.png + +The first time that you open up a codespace, it will spend some time building the image and then compiling the test files. +Onces that's done, you can move into the build direcotry and run the tests. + + +.. code-block:: bash + + cd build + make test + +Installing +---------- + +From an archive +^^^^^^^^^^^^^^^ + +All versions of micm are associated with a github `release `_. +Each release includes a tarall and zip that you can use to grab the code. + +Find a release of micm that you want to build and download that archive. You can either do this with the browser by +poking on the desired file or with the commands below. + +If you intend to use cmake to install micm, you can choose the install location when you configure +cmake: ``cmake -D CMAKE_INSTALL_PREFIX=/Users/me/Documents ..``. + +Zip +~~~ +.. code-block:: bash + + wget https://github.com/NCAR/micm/archive/refs/tags/v3.2.0.zip + unzip v3.2.0.zip + cd micm-3.2.0 + mkdir build && cd build + cmake .. + make -j 8 + make test + sudo make install + +Tarball +~~~~~~~ +.. code-block:: bash + + wget https://github.com/NCAR/micm/archive/refs/tags/v3.2.0.tar.gz + tar -xf v3.2.0.tar.gz + cd micm-3.2.0 + mkdir build && cd build + cmake .. + make -j 8 + make test + sudo make install + +Cloning from github +^^^^^^^^^^^^^^^^^^^ + +.. code-block:: bash + + git clone https://github.com/NCAR/micm.git + cd micm + mkdir build && cd build + cmake .. + make -j 8 + make test + sudo make install + +Usage after installation +^^^^^^^^^^^^^^^^^^^^^^^^ + +micm installs itself in a location typical on your system, like ``/usr/local``. It does so under it's own +directory with the version appended, e.g. ``/usr/local/micm-3.2.0``. It installs header files and files suitable +for use with cmake's `find_package `_. + +:: + + $ tree /usr/local/micm-3.2.0 -L 2 + /usr/local/micm-3.2.0 + ├── cmake + │ ├── micmConfig.cmake + │ ├── micmConfigVersion.cmake + │ └── micm_Exports.cmake + └── include + └── micm + +micm only installs its header and cmake files. If you intend to use any of the additional features of micm +like JIT compiling or json parsing, you'll need to have it's dependencies installed. + +Specify include path +~~~~~~~~~~~~~~~~~~~~ + +To compile micm code, it's as simple as adding the include path to your compile command ``-I/usr/local/micm-3.2.0/include`` +or ``export CPPFLAGS="-I/usr/local/micm-3.2.0/include"``. If you changed the install location when configuring cmake, you'll +need to set that path instead. + +Cmake +----- + +micm is developed with cmake support. This makes the inclusion of micm into projects that use cmake especially easy. + +find_package +^^^^^^^^^^^^ + +This assumes you have micm installed using one of the methods above. + +The cmake files installed with micm allow you to ask cmake to take care of finding and setting the include paths for you +in a cmake project. Using micm with `find_package `_ +is as simple as adding this line to your cmake project and linking +your target to micm. You will need to change the version accordingly. + +.. code-block:: cmake + + find_package(micm 3.2.0 REQUIRED) + + add_executable(my_target my_target.cpp) + + target_link_libraries(my_target + PUBLIC + musica::micm + ) + +Fetch content +^^^^^^^^^^^^^ + +If you can use cmake 3.11+, the easiest way to include micm is with the +`FetchContent module `_. +If you must use a lower version, you'll either need to install the files on your system, or properly set +the include flags of your cmake project to point to the micm header files if you don't need GPU support. +You may also want to look into `ExternalProject `_. + +To use micm with fetch content, you'll need to include the module and point it to the micm repository and a commit +or tag that you want to use. Then you make the content available and link your cmake target to micm. + +.. code-block:: cmake + + include(FetchContent) + + FetchContent_Declare(micm + GIT_REPOSITORY https://github.com/NCAR/micm.git + GIT_TAG 0996e5848b097e77ccbb2819f22c49844154f3e3 + ) + + FetchContent_MakeAvailable(micm) + + add_executable(my_target my_target.cpp) + + target_link_libraries(my_target + PUBLIC + musica::micm + ) + + +.. _Debugging: + +Debugging +--------- + +If you find yourself needing to debug the internals of micm, you don't have to stick to print statements. + +Luckily, cmake will generate projects for the major IDEs. + +VS Code +^^^^^^^ + +At this time, the easiest way to work in VS Code is to build micm yourself with cmake on the command line and use print +statements. Feel free to submit a PR with instructions for setting up visual studio code for debugging micm. + +Xcode +^^^^^ + +On macOS, you'll want to use xcode. You can ask cmake to generate an xcode project file for you. + +.. code-block:: + + mkdir xcode && cd xcode + cmake -G Xcode .. + +After this completes, there will be a file called micm.xcodeproj that you can open with Xcode. Select a target you want to build +and start debugging. Further use of xcode is beyond the scope of this documentation. + +Visual Studio +^^^^^^^^^^^^^ + +On windows, you'll want to use Visual Studio. You can ask cmake to generate an xcode project file for you. + +.. code-block:: + + mkdir xcode && cd xcode + cmake -G "Visual Studio 17 2022" .. + +After this completes, there will be a file called micm.sln that you can open with Visual Studio. +Select a target you want to build and start debugging. +Further use of Visual Studio is beyond the scope of this documentation. \ No newline at end of file diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index 245911f4c..f91b9ed02 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -34,14 +34,13 @@ This mechanism only needs the user defined rate constant and the rosenbrock solv .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 1-5 + :lines: 1-4 -After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the -rosenbrock solver. +After that, we'll use the ``micm`` namespace. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 7-14 + :lines: 6-7 To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these as @@ -49,14 +48,14 @@ well as a :cpp:class:`micm::Phase`. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 56-60 + :lines: 49-53 With the species and gas phase, we can define all of our reactions .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 62-78 + :lines: 55-71 Now we can define our RosenbrockSolver. This time we'll form the reactions and chemical system in place. @@ -67,7 +66,7 @@ the order the species are added to the gas phase. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 80-84 + :lines: 73-77 Now we need to get a state and set the concentations of each of the species. In the @@ -77,25 +76,25 @@ to set the concentrations. Here we will set the concentations by providing the : .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 86-91 + :lines: 79-84 Then we set the reaction rates by creating a vector that is 3 elements long, one for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 93-98 + :lines: 86-91 And lastly set the temperature, pressure, and air density for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 100-108 + :lines: 93-102 Now we are ready to run the simulation. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 110-133 + :lines: 104-127 And these are the results. diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index 37b68039b..ecde3e0b3 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -45,14 +45,14 @@ Configuring the rosenbrock solver is as easy as providing the solver with a set .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp - :lines: 154-172 + :lines: 147-165 After that, the usage is the same as a regular solver. A templated method was used here to run the same solving code for each of the different solver configurations. .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp - :lines: 40-123 + :lines: 34-116 Running this program should give an output similar to this: diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index 089f6aa12..a36d7246d 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -56,14 +56,14 @@ Adding the custom rate constant is quite simple. Include the header file: .. code-block:: diff - #include - #include - #include - #include - #include - #include - +#include - #include + #include + #include + #include + #include + #include + #include + + #include + #include Then setup the reaction which will use this rate constant: diff --git a/packaging/CMakeLists.txt b/packaging/CMakeLists.txt index d9b7208ae..122ffdc1f 100644 --- a/packaging/CMakeLists.txt +++ b/packaging/CMakeLists.txt @@ -1,4 +1,6 @@ include(CMakePackageConfigHelpers) +include(GNUInstallDirs) +set(INSTALL_PREFIX "micm-${PROJECT_VERSION}" ) install( TARGETS diff --git a/test/integration/cmake/README.md b/test/integration/cmake/README.md new file mode 100644 index 000000000..c6a47a3a2 --- /dev/null +++ b/test/integration/cmake/README.md @@ -0,0 +1,3 @@ +This folder tests usage with cmake. These are not tests that can be run with `make test`. +Both of these are tested in the Dockerfile so they are tested as part of the automated build. Please +look at that dockerfile to see how to test these. \ No newline at end of file diff --git a/test/integration/cmake/configs/robertson/reactions.json b/test/integration/cmake/configs/robertson/reactions.json new file mode 100644 index 000000000..cbffae848 --- /dev/null +++ b/test/integration/cmake/configs/robertson/reactions.json @@ -0,0 +1,43 @@ +{ + "camp-data": [ + { + "name": "reaction rates no user defined", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "A": {} + }, + "products": { + "B": {} + }, + "MUSICA name": "r1" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": { "qty": 2} + }, + "products": { + "B": {}, + "C": {} + }, + "MUSICA name": "r2" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": {}, + "C": {} + }, + "products": { + "A": {}, + "C": {} + }, + "MUSICA name": "r3" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/integration/cmake/configs/robertson/species.json b/test/integration/cmake/configs/robertson/species.json new file mode 100644 index 000000000..5a1a8db68 --- /dev/null +++ b/test/integration/cmake/configs/robertson/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "A", + "type": "CHEM_SPEC" + }, + { + "name": "B", + "type": "CHEM_SPEC" + }, + { + "name": "C", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/integration/cmake/fetch_content/CMakeLists.txt b/test/integration/cmake/fetch_content/CMakeLists.txt new file mode 100644 index 000000000..0003ff08b --- /dev/null +++ b/test/integration/cmake/fetch_content/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.11) + +project( + test_micm_fetch_content + VERSION 0.0.0 + LANGUAGES CXX +) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +include(FetchContent) + +FetchContent_Declare(micm + GIT_REPOSITORY https://github.com/NCAR/micm.git + GIT_TAG 0996e5848b097e77ccbb2819f22c49844154f3e3 +) + +FetchContent_MakeAvailable(micm) + +################################################################################ +# Tests +add_executable(test_micm ../test_micm.cpp) + +target_link_libraries(test_micm + PUBLIC + musica::micm +) + +enable_testing() + +add_test( + NAME test_micm + COMMAND test_micm +) + +add_custom_target(copy_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/../configs ${CMAKE_BINARY_DIR}/configs) \ No newline at end of file diff --git a/test/integration/cmake/find_package/CMakeLists.txt b/test/integration/cmake/find_package/CMakeLists.txt new file mode 100644 index 000000000..c27ec0d37 --- /dev/null +++ b/test/integration/cmake/find_package/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required(VERSION 3.11) + +project( + test_micm_fetch_content + VERSION 0.0.0 + LANGUAGES CXX +) + +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +find_package(micm 3.2.0 REQUIRED) + +################################################################################ +# Tests +add_executable(test_micm ../test_micm.cpp) + +target_link_libraries(test_micm + PUBLIC + musica::micm +) + +enable_testing() + +add_test( + NAME test_micm + COMMAND test_micm +) + +add_custom_target(copy_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/../configs ${CMAKE_BINARY_DIR}/configs) \ No newline at end of file diff --git a/test/integration/cmake/test_micm.cpp b/test/integration/cmake/test_micm.cpp new file mode 100644 index 000000000..03974f612 --- /dev/null +++ b/test/integration/cmake/test_micm.cpp @@ -0,0 +1,87 @@ +#include +#include + +using namespace micm; + +void print_header() +{ + std::cout << std::setw(5) << "time" + << "," << std::setw(10) << "A" + << "," << std::setw(10) << "B" + << "," << std::setw(10) << "C" << std::endl; +} + +template class T> +void print_state(double time, State& state) +{ + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + std::cout << std::setw(5) << time << ","; + std::cout << std::scientific << std::setprecision(2) + << std::setw(10) << state.variables_[0][state.variable_map_["A"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," + << std::setw(10) << state.variables_[0][state.variable_map_["C"]] + << std::endl; + + std::cout.copyfmt(oldState); +} + +int main() +{ + constexpr size_t n_threads = 3; + + SolverConfig solverConfig; + + std::string config_path = "./configs/robertson"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + RosenbrockSolver<> solver{ chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; + State state = solver.GetState(); + + // mol m-3 + state.variables_[0] = { 1, 0, 0 }; + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("PHOTO.r1", k1); + state.SetCustomRateParameter("PHOTO.r2", k2); + state.SetCustomRateParameter("PHOTO.r3", k3); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + double time_step = 200; // s + + print_header(); + print_state(0, state); + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + print_state(time_step * (i + 1), state); + } +} \ No newline at end of file diff --git a/test/tutorial/test_but_how_fast_is_it.cpp b/test/tutorial/test_but_how_fast_is_it.cpp index 0cb133a83..cdc114fd3 100644 --- a/test/tutorial/test_but_how_fast_is_it.cpp +++ b/test/tutorial/test_but_how_fast_is_it.cpp @@ -7,12 +7,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - int main() { auto a = Species("A"); @@ -39,7 +33,7 @@ int main() .rate_constant(UserDefinedRateConstant({ .label_ = "r3" })) .phase(gas_phase); - RosenbrockSolver solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), + RosenbrockSolver<> solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, RosenbrockSolverParameters::three_stage_rosenbrock_parameters( 3, false) }; diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index 57b17c691..bf0216108 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -6,12 +6,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -76,7 +70,7 @@ int main() .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) .phase(gas_phase); - micm::RosenbrockSolver solver{ + micm::RosenbrockSolver<> solver{ micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index 6938c20ee..df997e096 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -15,12 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -152,7 +146,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; - RosenbrockSolver solver{ chemical_system, + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index 2d0218ecf..60ba430e4 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -15,12 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -68,7 +62,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver solver{ chemical_system, + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp index 7f208005b..611fadecc 100644 --- a/test/tutorial/test_solver_configuration.cpp +++ b/test/tutorial/test_solver_configuration.cpp @@ -7,12 +7,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -// The Rosenbrock solver can use many matrix ordering types -// Here, we use the default ordering, but we still need to provide a templated -// Arguent to the solver so it can use the proper ordering with any data type -template -using SparseMatrixPolicy = SparseMatrix; - void print_header() { std::cout << std::setw(5) << "time" @@ -150,23 +144,23 @@ int main() auto system = System(SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3 }; - RosenbrockSolver two_stage{ + RosenbrockSolver<> two_stage{ system, reactions, RosenbrockSolverParameters::two_stage_rosenbrock_parameters() }; - RosenbrockSolver three_stage{ + RosenbrockSolver<> three_stage{ system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - RosenbrockSolver four_stage{ + RosenbrockSolver<> four_stage{ system, reactions, RosenbrockSolverParameters::four_stage_rosenbrock_parameters() }; - RosenbrockSolver four_stage_da{ + RosenbrockSolver<> four_stage_da{ system, reactions, RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters() }; - RosenbrockSolver six_stage_da{ + RosenbrockSolver<> six_stage_da{ system, reactions, RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters() }; From c7e7118e2a2876667cf4e871e169a6f9d0acfcec Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 18 Oct 2023 17:16:00 -0500 Subject: [PATCH 146/318] only using one if block --- include/micm/process/process.hpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index 7eb09a62a..92c0c5250 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -67,21 +67,11 @@ namespace micm rate_constant_(std::move(rate_constant)), phase_(phase) { - if (dynamic_cast(rate_constant_.get())) { - } - else if (dynamic_cast(rate_constant_.get())) { - } - else if (dynamic_cast(rate_constant_.get())) { + if (dynamic_cast(rate_constant_.get())) { if (reactants_.size() > 1) { throw std::runtime_error("A surface rate constant can only have one reactant"); } } - else if (dynamic_cast(rate_constant_.get())) { - } - else if (dynamic_cast(rate_constant_.get())) { - } - else if (dynamic_cast(rate_constant_.get())) { - } } Process& operator=(const Process& other) From 8e35578b34eed468603bf06ebb1276bab26b5af6 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 18 Oct 2023 16:27:52 -0700 Subject: [PATCH 147/318] add missing include --- include/micm/system/phase.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/micm/system/phase.hpp b/include/micm/system/phase.hpp index 3928a5c2e..c922acaf2 100644 --- a/include/micm/system/phase.hpp +++ b/include/micm/system/phase.hpp @@ -5,6 +5,7 @@ #pragma once #include +#include #include namespace micm From 08e486f4dbbd2d050a6e229dfff14f0ff22cd7ea Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 18 Oct 2023 16:51:26 -0700 Subject: [PATCH 148/318] fix template args in terminator test --- test/integration/terminator.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/integration/terminator.cpp b/test/integration/terminator.cpp index 813a37c6c..b08446b00 100644 --- a/test/integration/terminator.cpp +++ b/test/integration/terminator.cpp @@ -10,6 +10,9 @@ #include #include +template +using SparseMatrixTest = micm::SparseMatrix; + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> void RunTerminatorTest(std::size_t number_of_grid_cells) { @@ -28,10 +31,10 @@ void RunTerminatorTest(std::size_t number_of_grid_cells) TEST(RosenbrockSolver, Terminator) { - RunTerminatorTest>(2); - RunTerminatorTest>(2); - RunTerminatorTest>(3); - RunTerminatorTest>(4); + RunTerminatorTest>(2); + RunTerminatorTest>(2); + RunTerminatorTest>(3); + RunTerminatorTest>(4); } template From ec206447e8e41bc1c0bcd9da57b9ba27a9815f09 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 18 Oct 2023 17:00:30 -0700 Subject: [PATCH 149/318] relax terminator test tolerance --- test/integration/terminator.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/terminator.hpp b/test/integration/terminator.hpp index 1a57f475f..3d300c58e 100644 --- a/test/integration/terminator.hpp +++ b/test/integration/terminator.hpp @@ -93,9 +93,9 @@ void TestTerminator( double cl_f = -l * (cl_i - det + r) * (cl_i + det + r) / (1.0 + e + dt * l * (cl_i + r)); double cl2_f = -cl_f / 2.0; EXPECT_NEAR( - result.result_[i_cell][state.variable_map_["Cl"]], cl_i + dt * cl_f, (cl_i + dt * cl_f) * 1.0e-8 + 1.0e-15); + result.result_[i_cell][state.variable_map_["Cl"]], cl_i + dt * cl_f, (cl_i + dt * cl_f) * 1.0e-7 + 1.0e-14); EXPECT_NEAR( - result.result_[i_cell][state.variable_map_["Cl2"]], cl2_i + dt * cl2_f, (cl2_i + dt * cl2_f) * 1.0e-8 + 1.0e-15); + result.result_[i_cell][state.variable_map_["Cl2"]], cl2_i + dt * cl2_f, (cl2_i + dt * cl2_f) * 1.0e-7 + 1.0e-14); } } } From 28daf41dd54e8385bfe8bc765e9165bd604445d0 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 20 Oct 2023 09:33:09 -0600 Subject: [PATCH 150/318] Added the ReadAndParseSystemObjectfromMZ326 unit test. --- include/micm/configure/camp_config.hpp | 15 ++++++---- test/unit/configure/test_camp_config.cpp | 33 +++++++++++++++++++++ test/unit/unit_configs/MZ326/reactions.json | 3 -- 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 350b023ed..fa3a7e29d 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -153,10 +153,10 @@ namespace micm { camp_files.push_back(reactions_config); } - if (std::filesystem::exists(tolerance_config)) - { - camp_files.push_back(tolerance_config); - } + // if (std::filesystem::exists(tolerance_config)) + // { + // camp_files.push_back(tolerance_config); + // } } // No config files found @@ -176,8 +176,10 @@ namespace micm if (!config_subset.contains(CAMP_DATA)) return ConfigParseStatus::CAMPDataSectionNotFound; - // json camp_subset = config_subset[CAMP_DATA]; - + // std::cout << "config_subset" << std::endl; + // std::cout << config_subset.dump(4) << std::endl; + json camp_subset = config_subset[CAMP_DATA]; + // need to this merge differently, to work with camp_subset objects std::copy(config_subset.begin(), config_subset.end(), std::back_inserter(camp_data)); } @@ -187,6 +189,7 @@ namespace micm status = Configure(camp_data); // Assign the parsed 'Species' to 'Phase' + std::cout << "Parse species_arr_ size " << species_arr_.size() << std::endl; gas_phase_ = Phase(species_arr_); return status; diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 82ded148d..aef58fec7 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -136,3 +136,36 @@ TEST(SolverConfig, ReadAndParseProcessObjects) } } +// +// Tests for MZ326 configure files +// +TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) +{ + micm::SolverConfig solverConfig; + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + // Check 'gas_phase' in 'System' + EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 6); + + // Check 'phases' in 'System' + EXPECT_EQ(solver_params.system_.phases_.size(), 0); + + // Check 'name' and molecular_weight from 'properties' in 'Species' + std::vector> species_name_and_molecular_weight = { + std::make_pair("ALKNIT", 0.133141), std::make_pair("BZOOH", 0.124135), std::make_pair("C6H5OOH", 0.110109), + std::make_pair("COF2", 0.0), std::make_pair("O2", 0.0), std::make_pair("FUR2O2", 0.0) + }; + + short idx = 0; + for (const auto& s : solver_params.system_.gas_phase_.species_) + { + EXPECT_EQ(s.name_, species_name_and_molecular_weight[idx].first); + EXPECT_EQ(s.properties_.at("molecular weight [kg mol-1]"), species_name_and_molecular_weight[idx].second); + idx++; + } +} + diff --git a/test/unit/unit_configs/MZ326/reactions.json b/test/unit/unit_configs/MZ326/reactions.json index 1903b5e94..d0c8c042d 100644 --- a/test/unit/unit_configs/MZ326/reactions.json +++ b/test/unit/unit_configs/MZ326/reactions.json @@ -1,7 +1,4 @@ { - "comments": [ "This mechanism may contain refactored reactions based on custom", - "...." - ], "camp-data": [ { "name": "MZ326_TS1.3_20230106", From 8cc1e27f8832f9d9534240e6f6478ef14261a077 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 20 Oct 2023 09:42:41 -0600 Subject: [PATCH 151/318] Restored all unit tests from test_solver_config. --- test/unit/configure/test_camp_config.cpp | 93 ++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index aef58fec7..f245e3c3e 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -169,3 +169,96 @@ TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) } } +TEST(SolverConfig, ReadAndParseProcessObjectsfromMZ326) +{ + micm::SolverConfig solverConfig; + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + // Get solver parameters ('System', the collection of 'Process') + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // Check the number of 'Process' created + EXPECT_EQ(process_vector.size(), 5); + + // Check the number of 'reactants' and 'products' in each 'Process' + // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' + int num_reactants_in_each_process[] = { 2, 2, 2, 1, 1 }; + int num_products_in_each_process[] = { 5, 5, 2, 3, 2 }; + int num_phase_in_each_process = 6; + + short idx = 0; + for (const auto& p : process_vector) + { + EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); + EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); + // EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); + idx++; + } + + // Check the parameters for 'TroeRateConstant' + micm::TroeRateConstant* troe_rate_const = nullptr; + double k0_A_param[] = { 5.5e-30, 1.07767 }; + double k0_B_param[] = { 0.0, -5.6 }; + double k0_C_param[] = { 0.0, -14000 }; + double kinf_A_param[] = { 8.3e-13, 1.03323e+17 }; + double kinf_B_param[] = { 0.0, 0.0 }; + double kinf_C_param[] = { 0.0, -14000 }; + double Fc_param[] = { 0.6, 0.6 }; + double N_param[] = { -2, 1.5 }; + + idx = 0; + for (short i : { 0, 4 }) + { + troe_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(troe_rate_const != nullptr); + EXPECT_EQ(troe_rate_const->parameters_.k0_A_, k0_A_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.k0_B_, k0_B_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.k0_C_, k0_C_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.kinf_A_, kinf_A_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.kinf_B_, kinf_B_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.kinf_C_, kinf_C_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.Fc_, Fc_param[idx]); + EXPECT_EQ(troe_rate_const->parameters_.N_, N_param[idx]); + + idx++; + } + + // Check the name for 'UserDefinedRateConstant' + micm::UserDefinedRateConstant* photolysis_rate_const = nullptr; + std::string photolysis_name = "PHOTO.jterpnit"; + + for (short i : { 3 }) + { + photolysis_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(photolysis_rate_const != nullptr); + EXPECT_EQ(photolysis_rate_const->CustomParameters()[0], photolysis_name); + } + + // Check the parameters for 'ArrheniusRateConstant' + micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; + double A_param[] = { 2.0e-12, 3.8e-12 }; + double B_param[] = { 0.0, 0.0 }; + double C_param[] = { -1 * -6.90325e-21 / 1.3806505e-23, -1 * -2.7613e-21 / 1.3806505e-23 }; + double D_param[] = { 300.0, 300.0 }; + double E_param[] = { 0.0, 0.0 }; + + idx = 0; + for (short i : { 1, 2 }) + { + arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); + + EXPECT_TRUE(arrhenius_rate_const != nullptr); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.A_, A_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.B_, B_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.C_, C_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.D_, D_param[idx]); + EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.E_, E_param[idx]); + + idx++; + } +} From 11822d9e61925274a9b864637c1e187f03a260ee Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 20 Oct 2023 09:44:32 -0700 Subject: [PATCH 152/318] remove unneeded index from linear solver --- include/micm/solver/linear_solver.inl | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index ec6ebbad3..310f661bb 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -138,17 +138,11 @@ namespace micm } } { - auto y_elem = std::next(y_cell.end(), -1); auto x_elem = std::next(x_cell.end(), -1); auto Uij_xj = Uij_xj_.begin(); for (auto& nUij_Uii : nUij_Uii_) { - // don't iterate before the beginning of the vector - if (y_elem != y_cell.begin()) - { - --y_elem; - } - + // x_elem starts out as y_elem from the previous loop for (std::size_t i = 0; i < nUij_Uii.first; ++i) { *x_elem -= upper_matrix_.AsVector()[upper_grid_offset + (*Uij_xj).first] * x_cell[(*Uij_xj).second]; @@ -203,15 +197,11 @@ namespace micm } } { - auto y_elem = std::next(y_group, x.GroupSize() - n_cells); auto x_elem = std::next(x_group, x.GroupSize() - n_cells); auto Uij_xj = Uij_xj_.begin(); for (auto& nUij_Uii : nUij_Uii_) { - // don't iterate before the beginning of the vector - std::size_t y_elem_distance = std::distance(x.AsVector().begin(), y_elem); - y_elem -= std::min(n_cells, y_elem_distance); - + // x_elem starts out as y_elem from the previous loop for (std::size_t i = 0; i < nUij_Uii.first; ++i) { for (std::size_t i_cell = 0; i_cell < n_cells; ++i_cell) From 1d24332dbad4e1e8c8c2fd45c83326ab09918767 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Fri, 20 Oct 2023 14:40:05 -0600 Subject: [PATCH 153/318] Simplified top level multi-file parsing. Assemble CAMP data object list within iteration over CAMP files. --- include/micm/configure/camp_config.hpp | 51 ++++++++------------- test/unit/unit_configs/MZ326/reactions.json | 3 ++ 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index fa3a7e29d..62d2f709c 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -153,10 +153,10 @@ namespace micm { camp_files.push_back(reactions_config); } - // if (std::filesystem::exists(tolerance_config)) - // { - // camp_files.push_back(tolerance_config); - // } + if (std::filesystem::exists(tolerance_config)) + { + camp_files.push_back(tolerance_config); + } } // No config files found @@ -167,26 +167,30 @@ namespace micm return ConfigParseStatus::NoConfigFilesFound; } - // Merge config JSON from CAMP file list - json camp_data; + // Iterate CAMP file list and form CAMP data object list + std::vector objects; + for (const auto& camp_file : camp_files) { std::cout << "JsonReaderPolicy.Parse CAMP file " << camp_file << std::endl; json config_subset = json::parse(std::ifstream(camp_file)); - if (!config_subset.contains(CAMP_DATA)) + if (config_subset.contains(CAMP_DATA)) + { + for (const auto& object : config_subset[CAMP_DATA]) + { + objects.push_back(object); + } + } + else + { return ConfigParseStatus::CAMPDataSectionNotFound; - // std::cout << "config_subset" << std::endl; - // std::cout << config_subset.dump(4) << std::endl; - json camp_subset = config_subset[CAMP_DATA]; - // need to this merge differently, to work with camp_subset objects - std::copy(config_subset.begin(), config_subset.end(), - std::back_inserter(camp_data)); + } } ConfigParseStatus status; - status = Configure(camp_data); + status = ParseObjectArray(objects); // Assign the parsed 'Species' to 'Phase' std::cout << "Parse species_arr_ size " << species_arr_.size() << std::endl; @@ -197,25 +201,6 @@ namespace micm private: - ConfigParseStatus Configure(const json& config_data) - { - ConfigParseStatus status = ConfigParseStatus::None; - - std::vector objects; - - for (const auto& section : config_data) - { - for (const auto& object : section) - { - objects.push_back(object); - } - } - - status = ParseObjectArray(objects); - - return status; - } - ConfigParseStatus ParseObjectArray(const std::vector& objects) { ConfigParseStatus status = ConfigParseStatus::None; diff --git a/test/unit/unit_configs/MZ326/reactions.json b/test/unit/unit_configs/MZ326/reactions.json index d0c8c042d..1903b5e94 100644 --- a/test/unit/unit_configs/MZ326/reactions.json +++ b/test/unit/unit_configs/MZ326/reactions.json @@ -1,4 +1,7 @@ { + "comments": [ "This mechanism may contain refactored reactions based on custom", + "...." + ], "camp-data": [ { "name": "MZ326_TS1.3_20230106", From e8b3f7f99f42fd5151b74a9287f303b7e7d5c8b6 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 20 Oct 2023 14:22:38 -0700 Subject: [PATCH 154/318] add singularity check to LU decomposition --- include/micm/solver/linear_solver.hpp | 7 +++++ include/micm/solver/linear_solver.inl | 6 +++++ include/micm/solver/lu_decomposition.hpp | 17 ++++++++++-- include/micm/solver/lu_decomposition.inl | 27 ++++++++++++++++--- include/micm/solver/rosenbrock.hpp | 1 + include/micm/solver/rosenbrock.inl | 10 +++++-- .../unit/solver/test_jit_lu_decomposition.cpp | 26 ++++++++++++++++++ test/unit/solver/test_lu_decomposition.cpp | 22 +++++++++++++++ .../solver/test_lu_decomposition_policy.hpp | 25 +++++++++++++++++ 9 files changed, 134 insertions(+), 7 deletions(-) diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index 2a53386cf..cd86f107b 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -71,7 +71,14 @@ namespace micm const std::function&)> create_lu_decomp); /// @brief Decompose the matrix into upper and lower triangular matrices + /// @param matrix Matrix to decompose into lower and upper triangular matrices + /// @param is_singular Flag that is set to true if matrix is singular; false otherwise void Factor(const SparseMatrixPolicy& matrix); + + /// @brief Decompose the matrix into upper and lower triangular matrices + /// @param matrix Matrix to decompose into lower and upper triangular matrices + /// @param is_singular Flag that is set to true if matrix is singular; false otherwise + void Factor(const SparseMatrixPolicy& matrix, bool& is_singular); /// @brief Solve for x in Ax = b template class MatrixPolicy> diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index 310f661bb..0e1957aab 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -110,6 +110,12 @@ namespace micm lu_decomp_.template Decompose(matrix, lower_matrix_, upper_matrix_); } + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix, bool& is_singular) + { + lu_decomp_.template Decompose(matrix, lower_matrix_, upper_matrix_, is_singular); + } + template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> requires(!VectorizableDense> || !VectorizableSparse>) diff --git a/include/micm/solver/lu_decomposition.hpp b/include/micm/solver/lu_decomposition.hpp index b075abe54..fb2b8f693 100644 --- a/include/micm/solver/lu_decomposition.hpp +++ b/include/micm/solver/lu_decomposition.hpp @@ -91,15 +91,28 @@ namespace micm /// @param L The lower triangular matrix created by decomposition /// @param U The upper triangular matrix created by decomposition template class SparseMatrixPolicy> - requires(!VectorizableSparse>) void Decompose( + void Decompose( const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) const; + + /// @brief Perform an LU decomposition on a given A matrix + /// @param A Sparse matrix to decompose + /// @param L The lower triangular matrix created by decomposition + /// @param U The upper triangular matrix created by decomposition + /// @param is_singular Flag that is set to true if A is singular; false otherwise + template class SparseMatrixPolicy> + requires(!VectorizableSparse>) void Decompose( + const SparseMatrixPolicy& A, + SparseMatrixPolicy& L, + SparseMatrixPolicy& U, + bool& is_singular) const; template class SparseMatrixPolicy> requires(VectorizableSparse>) void Decompose( const SparseMatrixPolicy& A, SparseMatrixPolicy& L, - SparseMatrixPolicy& U) const; + SparseMatrixPolicy& U, + bool& is_singular) const; }; } // namespace micm diff --git a/include/micm/solver/lu_decomposition.inl b/include/micm/solver/lu_decomposition.inl index 19064bca2..0483970ef 100644 --- a/include/micm/solver/lu_decomposition.inl +++ b/include/micm/solver/lu_decomposition.inl @@ -145,9 +145,17 @@ namespace micm } template class SparseMatrixPolicy> - requires(!VectorizableSparse>) inline void LuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) const + { + bool is_singular; + Decompose(A, L, U, is_singular); + } + + template class SparseMatrixPolicy> + requires(!VectorizableSparse>) + inline void LuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U, bool& is_singular) + const { // Loop over blocks for (std::size_t i_block = 0; i_block < A.size(); ++i_block) @@ -164,6 +172,7 @@ namespace micm auto lki_nkj = lki_nkj_.begin(); auto lkj_uji = lkj_uji_.begin(); auto uii = uii_.begin(); + is_singular = false; for (auto& inLU : niLU_) { // Upper trianglur matrix @@ -189,6 +198,11 @@ namespace micm L_vector[lki_nkj->first] -= L_vector[lkj_uji->first] * U_vector[lkj_uji->second]; ++lkj_uji; } + if( U_vector[*uii] == 0.0 ) + { + is_singular = true; + return; + } L_vector[lki_nkj->first] /= U_vector[*uii]; ++lki_nkj; ++uii; @@ -199,10 +213,9 @@ namespace micm template class SparseMatrixPolicy> requires(VectorizableSparse>) - inline void LuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) + inline void LuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U, bool& is_singular) const { - const std::size_t n_cells = A.GroupVectorSize(); // Loop over groups of blocks for (std::size_t i_group = 0; i_group < A.NumberOfGroups(A.size()); ++i_group) { @@ -218,6 +231,8 @@ namespace micm auto lki_nkj = lki_nkj_.begin(); auto lkj_uji = lkj_uji_.begin(); auto uii = uii_.begin(); + is_singular = false; + const std::size_t n_cells = std::min(A.GroupVectorSize(), A.size() - i_group * A.GroupVectorSize()); for (auto& inLU : niLU_) { // Upper trianglur matrix @@ -256,7 +271,13 @@ namespace micm ++lkj_uji; } for (std::size_t i_cell = 0; i_cell < n_cells; ++i_cell) + { + if (U_vector[*uii + i_cell] == 0.0) { + is_singular = true; + return; + } L_vector[lki_nkj->first + i_cell] /= U_vector[*uii + i_cell]; + } ++lki_nkj; ++uii; } diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 99f95af55..4ad24239b 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -84,6 +84,7 @@ namespace micm size_t number_of_grid_cells_{ 1 }; // Number of grid cells to solve simultaneously bool reorder_state_{ true }; // Reorder state during solver construction to minimize LU fill-in + bool check_singularity_{ false }; // Check for singular A matrix in linear solve of A x = b // Print RosenbrockSolverParameters to console void print() const; diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index bdb582fb3..4f3f1b7eb 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -757,12 +757,18 @@ namespace micm // From my understanding the fortran do loop would only ever do one iteration and is equivalent to what's below SparseMatrixPolicy jacobian = jacobian_; uint64_t n_consecutive = 0; - singular = true; + singular = false; while (true) { double alpha = 1 / (H * gamma); AlphaMinusJacobian(jacobian, alpha); - linear_solver_.Factor(jacobian); + if (parameters_.check_singularity_) + { + linear_solver_.Factor(jacobian, singular); + } else { + singular = false; + linear_solver_.Factor(jacobian); + } singular = false; // TODO This should be evaluated in some way stats_.decompositions += 1; if (!singular) diff --git a/test/unit/solver/test_jit_lu_decomposition.cpp b/test/unit/solver/test_jit_lu_decomposition.cpp index 188373534..2a51853dc 100644 --- a/test/unit/solver/test_jit_lu_decomposition.cpp +++ b/test/unit/solver/test_jit_lu_decomposition.cpp @@ -41,6 +41,32 @@ TEST(JitLuDecomposition, DenseMatrixVectorOrdering) }); } +TEST(JitLuDecomposition, SingularMatrixVectorOrdering) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + testSingularMatrix>( + [&](const Group1SparseVectorMatrix& matrix) -> micm::JitLuDecomposition<1> { + return micm::JitLuDecomposition<1>{ jit.get(), matrix }; + }); + testSingularMatrix>( + [&](const Group2SparseVectorMatrix& matrix) -> micm::JitLuDecomposition<2> { + return micm::JitLuDecomposition<2>{ jit.get(), matrix }; + }); + testSingularMatrix>( + [&](const Group3SparseVectorMatrix& matrix) -> micm::JitLuDecomposition<3> { + return micm::JitLuDecomposition<3>{ jit.get(), matrix }; + }); + testSingularMatrix>( + [&](const Group4SparseVectorMatrix& matrix) -> micm::JitLuDecomposition<4> { + return micm::JitLuDecomposition<4>{ jit.get(), matrix }; + }); +} + TEST(JitLuDecomposition, RandomMatrixVectorOrdering) { auto jit{ micm::JitCompiler::create() }; diff --git a/test/unit/solver/test_lu_decomposition.cpp b/test/unit/solver/test_lu_decomposition.cpp index 1c0807538..6588083f2 100644 --- a/test/unit/solver/test_lu_decomposition.cpp +++ b/test/unit/solver/test_lu_decomposition.cpp @@ -24,6 +24,12 @@ TEST(LuDecomposition, DenseMatrixStandardOrdering) [](const SparseMatrixTest& matrix) -> micm::LuDecomposition { return micm::LuDecomposition{ matrix }; }); } +TEST(LuDecomposition, SingularMatrixStandardOrdering) +{ + testSingularMatrix( + [](const SparseMatrixTest& matrix) -> micm::LuDecomposition { return micm::LuDecomposition{ matrix }; }); +} + TEST(LuDecomposition, RandomMatrixStandardOrdering) { testRandomMatrix( @@ -52,6 +58,22 @@ TEST(LuDecomposition, DenseMatrixVectorOrdering) { return micm::LuDecomposition{ matrix }; }); } +TEST(LuDecomposition, SingluarMatrixVectorOrdering) +{ + testSingularMatrix( + [](const Group1SparseVectorMatrix& matrix) -> micm::LuDecomposition + { return micm::LuDecomposition{ matrix }; }); + testSingularMatrix( + [](const Group2SparseVectorMatrix& matrix) -> micm::LuDecomposition + { return micm::LuDecomposition{ matrix }; }); + testSingularMatrix( + [](const Group3SparseVectorMatrix& matrix) -> micm::LuDecomposition + { return micm::LuDecomposition{ matrix }; }); + testSingularMatrix( + [](const Group4SparseVectorMatrix& matrix) -> micm::LuDecomposition + { return micm::LuDecomposition{ matrix }; }); +} + TEST(LuDecomposition, RandomMatrixVectorOrdering) { testRandomMatrix( diff --git a/test/unit/solver/test_lu_decomposition_policy.hpp b/test/unit/solver/test_lu_decomposition_policy.hpp index 42484de33..ec375242a 100644 --- a/test/unit/solver/test_lu_decomposition_policy.hpp +++ b/test/unit/solver/test_lu_decomposition_policy.hpp @@ -104,6 +104,31 @@ void testDenseMatrix(const std::function void { EXPECT_NEAR(a, b, 1.0e-5); }); } +template class SparseMatrixPolicy, class LuDecompositionPolicy> +void testSingularMatrix(const std::function&)> create_lu_decomp) +{ + SparseMatrixPolicy A = SparseMatrixPolicy(SparseMatrixPolicy::create(2) + .initial_value(1.0e-30) + .with_element(0, 0) + .with_element(0, 1) + .with_element(1, 0) + .with_element(1, 1)); + + A[0][0][0] = 0; + A[0][0][1] = 1; + A[0][1][0] = 1; + A[0][1][1] = 1; + + LuDecompositionPolicy lud = create_lu_decomp(A); + auto LU = micm::LuDecomposition::GetLUMatrices(A, 1.0E-30); + bool is_singular{ false }; + lud.template Decompose(A, LU.first, LU.second, is_singular); + EXPECT_TRUE(is_singular); + A[0][0][0] = 12; + lud.template Decompose(A, LU.first, LU.second, is_singular); + EXPECT_FALSE(is_singular); +} + template class SparseMatrixPolicy, class LuDecompositionPolicy> void testRandomMatrix( const std::function&)> create_lu_decomp, From 10ed2ac14ea6452464002b92c81712a6c128dade Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 20 Oct 2023 14:40:53 -0700 Subject: [PATCH 155/318] add rosenbrock test with singularity check enabled --- .github/workflows/clang_format.yml | 8 ++++---- test/integration/terminator.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml index 86420017e..6c2f804ef 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/clang_format.yml @@ -29,12 +29,12 @@ jobs: - name: Check for changes id: check-changes run: | - git diff --exit-code || exit 0 + 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 == 'failure' + if: steps.check-changes.outcome != 'success' run: | git config --global user.name "GitHub Actions" git config --global user.email "actions@github.com" @@ -42,13 +42,13 @@ jobs: - name: Push changes to main-formatting branch # a failue of this step means changes were detected - if: steps.check-changes.outcome == 'failure' + 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 == 'failure' + if: steps.check-changes.outcome != 'success' uses: peter-evans/create-pull-request@v3 with: token: ${{ secrets.GITHUB_TOKEN }} diff --git a/test/integration/terminator.cpp b/test/integration/terminator.cpp index b08446b00..b72f7c69e 100644 --- a/test/integration/terminator.cpp +++ b/test/integration/terminator.cpp @@ -27,6 +27,18 @@ void RunTerminatorTest(std::size_t number_of_grid_cells) return micm::RosenbrockSolver{ s, p, solver_params }; }, number_of_grid_cells); + TestTerminator>( + [&](const micm::System& s, const std::vector& p) + -> micm::RosenbrockSolver + { + auto solver_params = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, true); + solver_params.absolute_tolerance_ = 1.0e-20; + solver_params.relative_tolerance_ = 1.0e-8; + solver_params.max_number_of_steps_ = 100000; + solver_params.check_singularity_ = true; + return micm::RosenbrockSolver{ s, p, solver_params }; + }, + number_of_grid_cells); } TEST(RosenbrockSolver, Terminator) From 678f8a6c074e3c4f4c217d58ff922c7f48f8bd61 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 20 Oct 2023 15:14:47 -0700 Subject: [PATCH 156/318] update jit classes for singularity check --- include/micm/solver/jit_linear_solver.hpp | 6 ++++++ include/micm/solver/jit_linear_solver.inl | 7 +++++++ include/micm/solver/jit_lu_decomposition.hpp | 8 ++++++++ include/micm/solver/jit_lu_decomposition.inl | 11 +++++++++++ 4 files changed, 32 insertions(+) diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index 1c087422f..11a6d2fe7 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -44,6 +44,12 @@ namespace micm ~JitLinearSolver(); /// @brief Decompose the matrix into upper and lower triangular matrices and general JIT functions + /// @param matrix Matrix that will be factored into lower and upper triangular matrices + /// @param is_singular Flag that will be set to true if matrix is singular; false otherwise + void Factor(SparseMatrix>& matrix, bool& is_singular); + + /// @brief Decompose the matrix into upper and lower triangular matrices and general JIT functions + /// @param matrix Matrix that will be factored into lower and upper triangular matrices void Factor(SparseMatrix>& matrix); /// @brief Solve for x in Ax = b diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index 35d1cf98f..195ec27a1 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -56,6 +56,13 @@ namespace micm } } + template class SparseMatrixPolicy, class LuDecompositionPolicy> + inline void JitLinearSolver::Factor( + SparseMatrix> &matrix, bool& is_singular) + { + LinearSolver::Factor(matrix, is_singular); + } + template class SparseMatrixPolicy, class LuDecompositionPolicy> inline void JitLinearSolver::Factor( SparseMatrix> &matrix) diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index f9a2aca5e..6878a4b0c 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -39,6 +39,14 @@ namespace micm ~JitLuDecomposition(); + /// @brief Create sparse L and U matrices for a given A matrix + /// @param A Sparse matrix that will be decomposed + /// @param lower The lower triangular matrix created by decomposition + /// @param upper The upper triangular matrix created by decomposition + /// @param is_singular Flag that will be set to true if A is singular; false otherwise + template class SparseMatrixPolicy> + void Decompose(const SparseMatrixPolicy &A, SparseMatrixPolicy &lower, SparseMatrixPolicy &upper, bool& is_singular) const; + /// @brief Create sparse L and U matrices for a given A matrix /// @param A Sparse matrix that will be decomposed /// @param lower The lower triangular matrix created by decomposition diff --git a/include/micm/solver/jit_lu_decomposition.inl b/include/micm/solver/jit_lu_decomposition.inl index a9c0b038e..a1a3cbb28 100644 --- a/include/micm/solver/jit_lu_decomposition.inl +++ b/include/micm/solver/jit_lu_decomposition.inl @@ -183,6 +183,17 @@ namespace micm decompose_function_resource_tracker_ = target.first; } + template + template class SparseMatrixPolicy> + void JitLuDecomposition::Decompose( + const SparseMatrixPolicy &A, + SparseMatrixPolicy &lower, + SparseMatrixPolicy &upper, + bool& is_singular) const + { + LuDecomposition::Decompose(A, lower, upper, is_singular); + } + template template class SparseMatrixPolicy> void JitLuDecomposition::Decompose( From bde04bc6704fe1e6f483f51947555d588bba139c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 13:20:16 -0700 Subject: [PATCH 157/318] Auto-format code changes (#323) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/process/cuda_process_set.hpp | 80 ++-- include/micm/process/process.hpp | 27 +- include/micm/process/process_set.hpp | 37 +- include/micm/solver/cuda_lu_decomposition.hpp | 114 +++--- include/micm/solver/jit_lu_decomposition.hpp | 6 +- include/micm/solver/linear_solver.hpp | 12 +- include/micm/solver/lu_decomposition.hpp | 11 +- include/micm/solver/rosenbrock.hpp | 4 +- include/micm/solver/state.hpp | 3 +- include/micm/system/phase.hpp | 2 +- include/micm/system/species.hpp | 2 +- include/micm/util/cuda_param.hpp | 120 +++--- src/solver/lu_decomposition.cu | 364 +++++++++--------- test/integration/cmake/test_micm.cpp | 12 +- test/integration/e5.hpp | 5 +- test/integration/hires.hpp | 5 +- test/integration/oregonator.hpp | 5 +- test/kpp/test_kpp_to_micm.cpp | 47 ++- test/tutorial/test_README_example.cpp | 14 +- test/tutorial/test_but_how_fast_is_it.cpp | 7 +- test/tutorial/test_multiple_grid_cells.cpp | 8 +- test/tutorial/test_openmp.cpp | 11 +- ...st_rate_constants_user_defined_by_hand.cpp | 4 +- ...ate_constants_user_defined_with_config.cpp | 4 +- test/tutorial/test_solver_configuration.cpp | 40 +- test/unit/openmp/test_openmp.cpp | 9 +- test/unit/process/test_cuda_process_set.cpp | 3 +- test/unit/process/test_process.cpp | 17 +- .../solver/test_cuda_lu_decomposition.cpp | 49 +-- test/unit/solver/test_lu_decomposition.cpp | 4 +- test/unit/solver/test_rosenbrock.cpp | 12 +- 31 files changed, 520 insertions(+), 518 deletions(-) diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index f7ced87ab..e3f352fcc 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -5,7 +5,6 @@ #include #include -#include #ifdef USE_CUDA # include @@ -54,26 +53,24 @@ namespace micm MatrixPolicy& forcing) const { CudaMatrixParam matrix; - matrix.rate_constants_ = rate_constants.AsVector().data(); - matrix.state_variables_ = state_variables.AsVector().data(); - matrix.forcing_ = forcing.AsVector().data(); - matrix.n_grids_ = rate_constants.size(); - matrix.n_reactions_ = rate_constants[0].size(); + matrix.rate_constants_ = rate_constants.AsVector().data(); + matrix.state_variables_ = state_variables.AsVector().data(); + matrix.forcing_ = forcing.AsVector().data(); + matrix.n_grids_ = rate_constants.size(); + matrix.n_reactions_ = rate_constants[0].size(); matrix.n_species_ = state_variables[0].size(); - CudaProcessSetParam processSet; - processSet.number_of_reactants_ = number_of_reactants_.data(); - processSet.reactant_ids_ = reactant_ids_.data(); - processSet.reactant_ids_size_ = reactant_ids_.size(); - processSet.number_of_products_ = number_of_products_.data(); - processSet.product_ids_ = product_ids_.data(); - processSet.product_ids_size_ = product_ids_.size(); - processSet.yields_ = yields_.data(); - processSet.yields_size_ = yields_.size(); + CudaProcessSetParam processSet; + processSet.number_of_reactants_ = number_of_reactants_.data(); + processSet.reactant_ids_ = reactant_ids_.data(); + processSet.reactant_ids_size_ = reactant_ids_.size(); + processSet.number_of_products_ = number_of_products_.data(); + processSet.product_ids_ = product_ids_.data(); + processSet.product_ids_size_ = product_ids_.size(); + processSet.yields_ = yields_.data(); + processSet.yields_size_ = yields_.size(); - std::chrono::nanoseconds kernel_duration = micm::cuda::AddForcingTermsKernelDriver( - matrix, - processSet); + std::chrono::nanoseconds kernel_duration = micm::cuda::AddForcingTermsKernelDriver(matrix, processSet); return kernel_duration; // time performance of kernel function } template class MatrixPolicy, template class SparseMatrixPolicy> @@ -83,31 +80,28 @@ namespace micm const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const { - CudaMatrixParam matrix; - matrix.rate_constants_ = rate_constants.AsVector().data(); - matrix.state_variables_ = state_variables.AsVector().data(); - matrix.n_grids_ = rate_constants.size(); - matrix.n_reactions_ = rate_constants[0].size(); - matrix.n_species_ = state_variables[0].size(); - - CudaSparseMatrixParam sparseMatrix; - sparseMatrix.jacobian_ = jacobian.AsVector().data(); - sparseMatrix.jacobian_size_ = jacobian.AsVector().size(); - - CudaProcessSetParam processSet; - processSet.number_of_reactants_ = number_of_reactants_.data(); - processSet.reactant_ids_ = reactant_ids_.data(); - processSet.reactant_ids_size_ = reactant_ids_.size(); - processSet.number_of_products_ = number_of_products_.data(); - processSet.yields_ = yields_.data(); - processSet.yields_size_ = yields_.size(); - processSet.jacobian_flat_ids_ = jacobian_flat_ids_.data(); - processSet.jacobian_flat_ids_size_ = jacobian_flat_ids_.size(); - - std::chrono::nanoseconds kernel_duration = micm::cuda::AddJacobianTermsKernelDriver( - matrix, - sparseMatrix, - processSet); + CudaMatrixParam matrix; + matrix.rate_constants_ = rate_constants.AsVector().data(); + matrix.state_variables_ = state_variables.AsVector().data(); + matrix.n_grids_ = rate_constants.size(); + matrix.n_reactions_ = rate_constants[0].size(); + matrix.n_species_ = state_variables[0].size(); + + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.jacobian_ = jacobian.AsVector().data(); + sparseMatrix.jacobian_size_ = jacobian.AsVector().size(); + + CudaProcessSetParam processSet; + processSet.number_of_reactants_ = number_of_reactants_.data(); + processSet.reactant_ids_ = reactant_ids_.data(); + processSet.reactant_ids_size_ = reactant_ids_.size(); + processSet.number_of_products_ = number_of_products_.data(); + processSet.yields_ = yields_.data(); + processSet.yields_size_ = yields_.size(); + processSet.jacobian_flat_ids_ = jacobian_flat_ids_.data(); + processSet.jacobian_flat_ids_size_ = jacobian_flat_ids_.size(); + + std::chrono::nanoseconds kernel_duration = micm::cuda::AddJacobianTermsKernelDriver(matrix, sparseMatrix, processSet); return kernel_duration; // time performance of kernel function } } // namespace micm diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index a93a70c8c..f19386c9b 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -5,7 +5,6 @@ #pragma once #include -#include #include #include #include @@ -44,11 +43,13 @@ namespace micm /// @param processes The set of processes being solved /// @param state The solver state to update template class MatrixPolicy> - requires(!VectorizableDense>) - static void UpdateState(const std::vector& processes, State& state); + requires(!VectorizableDense>) static void UpdateState( + const std::vector& processes, + State& state); template class MatrixPolicy> - requires(VectorizableDense>) - static void UpdateState(const std::vector& processes, State& state); + requires(VectorizableDense>) static void UpdateState( + const std::vector& processes, + State& state); friend class ProcessBuilder; static ProcessBuilder create(); @@ -65,8 +66,10 @@ namespace micm rate_constant_(std::move(rate_constant)), phase_(phase) { - if (dynamic_cast(rate_constant_.get())) { - if (reactants_.size() > 1) { + if (dynamic_cast(rate_constant_.get())) + { + if (reactants_.size() > 1) + { throw std::runtime_error("A surface rate constant can only have one reactant"); } } @@ -103,8 +106,9 @@ namespace micm }; template class MatrixPolicy> - requires(!VectorizableDense>) - void Process::UpdateState(const std::vector& processes, State& state) + requires(!VectorizableDense>) void Process::UpdateState( + const std::vector& processes, + State& state) { for (std::size_t i{}; i < state.custom_rate_parameters_.size(); ++i) { @@ -125,8 +129,9 @@ namespace micm } template class MatrixPolicy> - requires(VectorizableDense>) - void Process::UpdateState(const std::vector& processes, State& state) + requires(VectorizableDense>) void Process::UpdateState( + const std::vector& processes, + State& state) { const auto& v_custom_parameters = state.custom_rate_parameters_.AsVector(); auto& v_rate_constants = state.rate_constants_.AsVector(); diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 39d1e5126..5942157cb 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -47,13 +47,12 @@ namespace micm /// @param state_variables Current state variable values (grid cell, state variable) /// @param forcing Forcing terms for each state variable (grid cell, state variable) template typename MatrixPolicy> - requires(!VectorizableDense>) - void AddForcingTerms( + requires(!VectorizableDense>) void AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, MatrixPolicy& forcing) const; template typename MatrixPolicy> - requires VectorizableDense> + requires VectorizableDense> void AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, @@ -64,14 +63,12 @@ namespace micm /// @param state_variables Current state variable values (grid cell, state variable) /// @param jacobian Jacobian matrix for the system (grid cell, dependent variable, independent variable) template class MatrixPolicy, template class SparseMatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) - void AddJacobianTerms( + requires(!VectorizableDense> || !VectorizableSparse>) void AddJacobianTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const; template class MatrixPolicy, template class SparseMatrixPolicy> - requires(VectorizableDense> && VectorizableSparse>) - void AddJacobianTerms( + requires(VectorizableDense>&& VectorizableSparse>) void AddJacobianTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const; @@ -158,8 +155,7 @@ namespace micm } template typename MatrixPolicy> - requires(!VectorizableDense>) - inline void ProcessSet::AddForcingTerms( + requires(!VectorizableDense>) inline void ProcessSet::AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, MatrixPolicy& forcing) const @@ -195,7 +191,7 @@ namespace micm }; template typename MatrixPolicy> - requires VectorizableDense> + requires VectorizableDense> inline void ProcessSet::AddForcingTerms( const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, @@ -236,11 +232,12 @@ namespace micm } template class MatrixPolicy, template class SparseMatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) - inline void ProcessSet::AddJacobianTerms( - const MatrixPolicy& rate_constants, - const MatrixPolicy& state_variables, - SparseMatrixPolicy& jacobian) const + requires( + !VectorizableDense> || !VectorizableSparse>) inline void ProcessSet:: + AddJacobianTerms( + const MatrixPolicy& rate_constants, + const MatrixPolicy& state_variables, + SparseMatrixPolicy& jacobian) const { auto cell_jacobian = jacobian.AsVector().begin(); @@ -282,11 +279,11 @@ namespace micm } template class MatrixPolicy, template class SparseMatrixPolicy> - requires(VectorizableDense> && VectorizableSparse>) - inline void ProcessSet::AddJacobianTerms( - const MatrixPolicy& rate_constants, - const MatrixPolicy& state_variables, - SparseMatrixPolicy& jacobian) const + requires(VectorizableDense>&& VectorizableSparse>) inline void ProcessSet:: + AddJacobianTerms( + const MatrixPolicy& rate_constants, + const MatrixPolicy& state_variables, + SparseMatrixPolicy& jacobian) const { const auto& v_rate_constants = rate_constants.AsVector(); const auto& v_state_variables = state_variables.AsVector(); diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index f5aa469cd..7f2d33e17 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -2,69 +2,67 @@ // // SPDX-License-Identifier: Apache-2.0 #pragma once -#include -#include -#include #include +#include +#include +#include #ifdef USE_CUDA -#include -#endif +# include +#endif #ifdef USE_CUDA -namespace micm{ - class CudaLuDecomposition: public LuDecomposition{ - public: - CudaLuDecomposition(){}; - +namespace micm +{ + class CudaLuDecomposition : public LuDecomposition + { + public: + CudaLuDecomposition(){}; + template typename SparseMatrixPolicy> - requires VectorizableSparse> - std::chrono::nanoseconds Decompose( - const SparseMatrixPolicy&A, - SparseMatrixPolicy& L, - SparseMatrixPolicy& U) const; - }; + requires VectorizableSparse> std::chrono::nanoseconds + Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) + const; + }; + + template class SparseMatrixPolicy> + requires(VectorizableSparse>) std::chrono::nanoseconds + CudaLuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) + const + { + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.A_ = A.AsVector().data(); + sparseMatrix.A_size_ = A.AsVector().size(); + sparseMatrix.L_ = L.AsVector().data(); + sparseMatrix.L_size_ = L.AsVector().size(); + sparseMatrix.U_ = U.AsVector().data(); + sparseMatrix.U_size_ = U.AsVector().size(); + sparseMatrix.n_grids_ = A.size(); + + CudaSolverParam solver; + solver.do_aik_ = do_aik_.data(); + solver.do_aik_size_ = do_aik_.size(); + solver.aik_ = aik_.data(); + solver.aik_size_ = aik_.size(); + solver.do_aki_ = do_aki_.data(); + solver.do_aki_size_ = do_aki_.size(); + solver.aki_ = aki_.data(); + solver.aki_size_ = aki_.size(); + solver.uii_ = uii_.data(); + solver.uii_size_ = uii_.size(); - template class SparseMatrixPolicy> - requires(VectorizableSparse>) - std::chrono::nanoseconds CudaLuDecomposition::Decompose( - const SparseMatrixPolicy& A, - SparseMatrixPolicy& L, - SparseMatrixPolicy& U) const - { - CudaSparseMatrixParam sparseMatrix; - sparseMatrix.A_ = A.AsVector().data(); - sparseMatrix.A_size_ = A.AsVector().size(); - sparseMatrix.L_ = L.AsVector().data(); - sparseMatrix.L_size_ = L.AsVector().size(); - sparseMatrix.U_ = U.AsVector().data(); - sparseMatrix.U_size_ = U.AsVector().size(); - sparseMatrix.n_grids_ = A.size(); - - CudaSolverParam solver; - solver.do_aik_ = do_aik_.data(); - solver.do_aik_size_ = do_aik_.size(); - solver.aik_ = aik_.data(); - solver.aik_size_ = aik_.size(); - solver.do_aki_ = do_aki_.data(); - solver.do_aki_size_ = do_aki_.size(); - solver.aki_ = aki_.data(); - solver.aki_size_ = aki_.size(); - solver.uii_ = uii_.data(); - solver.uii_size_ = uii_.size(); - - solver.niLU_ = niLU_.data(); - solver.niLU_size_ = niLU_.size(); - solver.uik_nkj_ = uik_nkj_.data(); - solver.uik_nkj_size_ = uik_nkj_.size(); - solver.lij_ujk_ = lij_ujk_.data(); - solver.lij_ujk_size_ = lij_ujk_.size(); - solver.lki_nkj_ = lki_nkj_.data(); - solver.lki_nkj_size_ = lki_nkj_.size(); - solver.lkj_uji_ = lkj_uji_.data(); - solver.lkj_uji_size_ = lkj_uji_.size(); + solver.niLU_ = niLU_.data(); + solver.niLU_size_ = niLU_.size(); + solver.uik_nkj_ = uik_nkj_.data(); + solver.uik_nkj_size_ = uik_nkj_.size(); + solver.lij_ujk_ = lij_ujk_.data(); + solver.lij_ujk_size_ = lij_ujk_.size(); + solver.lki_nkj_ = lki_nkj_.data(); + solver.lki_nkj_size_ = lki_nkj_.size(); + solver.lkj_uji_ = lkj_uji_.data(); + solver.lkj_uji_size_ = lkj_uji_.size(); - //calling kernelSetup function - return micm::cuda::DecomposeKernelDriver(sparseMatrix, solver); - } -}//end micm + // calling kernelSetup function + return micm::cuda::DecomposeKernelDriver(sparseMatrix, solver); + } +} // namespace micm #endif \ No newline at end of file diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index 6878a4b0c..5d44d26f4 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -45,7 +45,11 @@ namespace micm /// @param upper The upper triangular matrix created by decomposition /// @param is_singular Flag that will be set to true if A is singular; false otherwise template class SparseMatrixPolicy> - void Decompose(const SparseMatrixPolicy &A, SparseMatrixPolicy &lower, SparseMatrixPolicy &upper, bool& is_singular) const; + void Decompose( + const SparseMatrixPolicy &A, + SparseMatrixPolicy &lower, + SparseMatrixPolicy &upper, + bool &is_singular) const; /// @brief Create sparse L and U matrices for a given A matrix /// @param A Sparse matrix that will be decomposed diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index cd86f107b..c292f098e 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -74,7 +74,7 @@ namespace micm /// @param matrix Matrix to decompose into lower and upper triangular matrices /// @param is_singular Flag that is set to true if matrix is singular; false otherwise void Factor(const SparseMatrixPolicy& matrix); - + /// @brief Decompose the matrix into upper and lower triangular matrices /// @param matrix Matrix to decompose into lower and upper triangular matrices /// @param is_singular Flag that is set to true if matrix is singular; false otherwise @@ -82,11 +82,13 @@ namespace micm /// @brief Solve for x in Ax = b template class MatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) - void Solve(const MatrixPolicy& b, MatrixPolicy& x); + requires(!VectorizableDense> || !VectorizableSparse>) void Solve( + const MatrixPolicy& b, + MatrixPolicy& x); template class MatrixPolicy> - requires(VectorizableDense> && VectorizableSparse>) - void Solve(const MatrixPolicy& b, MatrixPolicy& x); + requires(VectorizableDense>&& VectorizableSparse>) void Solve( + const MatrixPolicy& b, + MatrixPolicy& x); }; } // namespace micm diff --git a/include/micm/solver/lu_decomposition.hpp b/include/micm/solver/lu_decomposition.hpp index fb2b8f693..79681e2e4 100644 --- a/include/micm/solver/lu_decomposition.hpp +++ b/include/micm/solver/lu_decomposition.hpp @@ -39,7 +39,7 @@ namespace micm std::vector> niLU_; /// True when A[i][k] is non-zero for each iteration of the middle (k) loop for the upper /// triangular matrix; False otherwise. Used data type char instead of bool because vector representation - ///does not suppor easy retrieval of memory address using data() function. + /// does not suppor easy retrieval of memory address using data() function. std::vector do_aik_; /// Index in A.data_ for A[i][k] for each iteration of the middle (k) loop for the upper /// triangular matrix when A[i][k] is non-zero @@ -53,10 +53,10 @@ namespace micm std::vector> lij_ujk_; /// True when A[k][i] is non-zero for each iteration of the middle (k) loop for the lower /// triangular matrix; False otherwise. Used data type char instead of bool because vector representation - ///does not suppor easy retrieval of memory address using data() function. + /// does not suppor easy retrieval of memory address using data() function. std::vector do_aki_; /// Index in A.data_ for A[k][i] for each iteration of the middle (k) loop for the lower - /// triangular matrix when A[k][i] is non-zero. + /// triangular matrix when A[k][i] is non-zero. std::vector aki_; /// Index in L.data_ for L[k][i] for each iteration of the middle (k) loop for the lower /// triangular matrix when L[k][i] is non-zero, and the corresponding number of elements @@ -91,10 +91,7 @@ namespace micm /// @param L The lower triangular matrix created by decomposition /// @param U The upper triangular matrix created by decomposition template class SparseMatrixPolicy> - void Decompose( - const SparseMatrixPolicy& A, - SparseMatrixPolicy& L, - SparseMatrixPolicy& U) const; + void Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) const; /// @brief Perform an LU decomposition on a given A matrix /// @param A Sparse matrix to decompose diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 4ad24239b..9dda76770 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -266,9 +266,9 @@ namespace micm /// @param jacobian Jacobian matrix (dforce_dy) /// @param alpha void AlphaMinusJacobian(SparseMatrixPolicy& jacobian, const double& alpha) const - requires(!VectorizableSparse>); + requires(!VectorizableSparse>); void AlphaMinusJacobian(SparseMatrixPolicy& jacobian, const double& alpha) const - requires(VectorizableSparse>); + requires(VectorizableSparse>); /// @brief Update the rate constants for the environment state /// @param state The current state of the chemical system diff --git a/include/micm/solver/state.hpp b/include/micm/solver/state.hpp index da59c2fa0..ac2674bcb 100644 --- a/include/micm/solver/state.hpp +++ b/include/micm/solver/state.hpp @@ -48,8 +48,7 @@ namespace micm /// @brief Set species' concentrations /// @param species_to_concentration - void SetConcentrations( - const std::unordered_map>& species_to_concentration); + void SetConcentrations(const std::unordered_map>& species_to_concentration); /// @brief Set a single species concentration /// @param species the species to set the concentration for diff --git a/include/micm/system/phase.hpp b/include/micm/system/phase.hpp index c922acaf2..f81add256 100644 --- a/include/micm/system/phase.hpp +++ b/include/micm/system/phase.hpp @@ -4,8 +4,8 @@ */ #pragma once -#include #include +#include #include namespace micm diff --git a/include/micm/system/species.hpp b/include/micm/system/species.hpp index a0b68bbab..6dd194383 100644 --- a/include/micm/system/species.hpp +++ b/include/micm/system/species.hpp @@ -4,9 +4,9 @@ #pragma once #include +#include #include #include -#include #include "conditions.hpp" diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index 8db6aa01d..2d95a1ec3 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -1,63 +1,67 @@ -#include +#include #ifndef CUDA_PARAM_HPP -#define CUDA_PARAM_HPP - //member data of class CudaProcessSet grouped in struct passing to kernel driver function - const size_t BLOCK_SIZE = 320; - struct CudaProcessSetParam{ - const size_t* number_of_reactants_; - const size_t* reactant_ids_; - size_t reactant_ids_size_; - const size_t* number_of_products_; - const size_t* product_ids_; - size_t product_ids_size_; - const double* yields_; - size_t yields_size_; - const size_t* jacobian_flat_ids_; - size_t jacobian_flat_ids_size_; -}; +# define CUDA_PARAM_HPP +// member data of class CudaProcessSet grouped in struct passing to kernel driver function +const size_t BLOCK_SIZE = 320; +struct CudaProcessSetParam +{ + const size_t* number_of_reactants_; + const size_t* reactant_ids_; + size_t reactant_ids_size_; + const size_t* number_of_products_; + const size_t* product_ids_; + size_t product_ids_size_; + const double* yields_; + size_t yields_size_; + const size_t* jacobian_flat_ids_; + size_t jacobian_flat_ids_size_; +}; - struct CudaSolverParam{ - const std::pair* niLU_; - size_t niLU_size_; - const char* do_aik_; - size_t do_aik_size_; - const size_t* aik_; - size_t aik_size_; - const std::pair* uik_nkj_; - size_t uik_nkj_size_; - const std::pair* lij_ujk_; - size_t lij_ujk_size_; - const char* do_aki_; - size_t do_aki_size_; - const size_t* aki_; - size_t aki_size_; - const std::pair* lki_nkj_; - size_t lki_nkj_size_; - const std::pair* lkj_uji_; - size_t lkj_uji_size_; - const size_t* uii_; - size_t uii_size_; - }; - //different matrix data grouped in struct passing to kernel driver function - struct CudaMatrixParam{ - const double* rate_constants_; - const double* state_variables_; - double* forcing_; - size_t n_grids_; - size_t n_reactions_; - size_t n_species_; -}; -//sparseMatrix data grouped in struct passing to kernel driver function -struct CudaSparseMatrixParam{ - double* jacobian_; - size_t jacobian_size_; - const double* A_; - size_t A_size_; - double* L_; - size_t L_size_; - double* U_; - size_t U_size_; - size_t n_grids_; +struct CudaSolverParam +{ + const std::pair* niLU_; + size_t niLU_size_; + const char* do_aik_; + size_t do_aik_size_; + const size_t* aik_; + size_t aik_size_; + const std::pair* uik_nkj_; + size_t uik_nkj_size_; + const std::pair* lij_ujk_; + size_t lij_ujk_size_; + const char* do_aki_; + size_t do_aki_size_; + const size_t* aki_; + size_t aki_size_; + const std::pair* lki_nkj_; + size_t lki_nkj_size_; + const std::pair* lkj_uji_; + size_t lkj_uji_size_; + const size_t* uii_; + size_t uii_size_; +}; +// different matrix data grouped in struct passing to kernel driver function +struct CudaMatrixParam +{ + const double* rate_constants_; + const double* state_variables_; + double* forcing_; + size_t n_grids_; + size_t n_reactions_; + size_t n_species_; +}; +// sparseMatrix data grouped in struct passing to kernel driver function +struct CudaSparseMatrixParam +{ + double* jacobian_; + size_t jacobian_size_; + const double* A_; + size_t A_size_; + double* L_; + size_t L_size_; + double* U_; + size_t U_size_; + size_t n_grids_; }; #endif \ No newline at end of file diff --git a/src/solver/lu_decomposition.cu b/src/solver/lu_decomposition.cu index 4d9a4b43b..1381e71d7 100644 --- a/src/solver/lu_decomposition.cu +++ b/src/solver/lu_decomposition.cu @@ -1,184 +1,198 @@ // Copyright (C) 2023 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 -#include -#include #include -#include +#include +#include +#include -//grouped parameters passing to DecomposeKernel() -struct DecomposeDevice{ - double* A_; - double* L_; - double* U_; - char* do_aik_; - size_t* aik_; - char* do_aki_; - size_t* aki_; - size_t* uii_; - std::pair* niLU_; - std::pair* uik_nkj_; - std::pair* lij_ujk_; - std::pair* lki_nkj_; - std::pair* lkj_uji_; - size_t n_grids_; - size_t niLU_size_; -}; -namespace micm{ - namespace cuda{ - __global__ void DecomposeKernel(DecomposeDevice* device) +// grouped parameters passing to DecomposeKernel() +struct DecomposeDevice +{ + double* A_; + double* L_; + double* U_; + char* do_aik_; + size_t* aik_; + char* do_aki_; + size_t* aki_; + size_t* uii_; + std::pair* niLU_; + std::pair* uik_nkj_; + std::pair* lij_ujk_; + std::pair* lki_nkj_; + std::pair* lkj_uji_; + size_t n_grids_; + size_t niLU_size_; +}; +namespace micm +{ + namespace cuda + { + __global__ void DecomposeKernel(DecomposeDevice* device) + { + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + double* A = device->A_; + double* L = device->L_; + double* U = device->U_; + std::pair* lkj_uji = device->lkj_uji_; + std::pair* uik_nkj = device->uik_nkj_; + std::pair* lij_ujk = device->lij_ujk_; + std::pair* lki_nkj = device->lki_nkj_; + size_t do_aik_offset = 0; // boolean vector + size_t aik_offset = 0; + size_t uik_nkj_offset = 0; + size_t lij_ujk_offset = 0; + size_t do_aki_offset = 0; // boolean vector + size_t aki_offset = 0; + size_t lki_nkj_offset = 0; + size_t lkj_uji_offset = 0; + size_t uii_offset = 0; + + if (tid < device->n_grids_) + { + // loop through every element in niLU + for (size_t i = 0; i < device->niLU_size_; i++) { - size_t tid = blockIdx.x * blockDim.x + threadIdx.x; - double* A = device->A_; - double* L = device->L_; - double* U = device->U_; - std::pair* lkj_uji = device->lkj_uji_; - std::pair* uik_nkj = device->uik_nkj_; - std::pair* lij_ujk = device->lij_ujk_; - std::pair* lki_nkj = device->lki_nkj_; - size_t do_aik_offset = 0; //boolean vector - size_t aik_offset = 0; - size_t uik_nkj_offset = 0; - size_t lij_ujk_offset = 0; - size_t do_aki_offset = 0; //boolean vector - size_t aki_offset = 0; - size_t lki_nkj_offset = 0; - size_t lkj_uji_offset = 0; - size_t uii_offset = 0; - - if (tid < device->n_grids_){ - //loop through every element in niLU - for (size_t i = 0; i < device->niLU_size_; i++){ - //upper triangular matrix - auto inLU = device->niLU_[i]; - for (size_t iU = 0; iU < inLU.second; ++iU){ - if(device->do_aik_[do_aik_offset++]){ - size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; - size_t A_idx = device->aik_[aik_offset++]+ tid; - U[U_idx] = A[A_idx]; - } - - for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj){ - size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; - size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; - size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; - U[U_idx_1] -= L[L_idx] * U[U_idx_2]; - ++lij_ujk_offset; - } - ++uik_nkj_offset; - } - // lower triangular matrix - - L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; - - for (size_t iL = 0; iL do_aki_[do_aki_offset++]){ - size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; - size_t A_idx = device->aki_[aki_offset++] + tid; - L[L_idx] = A[A_idx]; - } - for(size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second;++ikj){ - size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; - size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; - size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; - L[L_idx_1] -= L[L_idx_2] * U[U_idx]; - ++lkj_uji_offset; - } - L[lki_nkj[lki_nkj_offset].first + tid]/=U[device->uii_[uii_offset] + tid]; - ++lki_nkj_offset; - ++uii_offset; - } - } + // upper triangular matrix + auto inLU = device->niLU_[i]; + for (size_t iU = 0; iU < inLU.second; ++iU) + { + if (device->do_aik_[do_aik_offset++]) + { + size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; + size_t A_idx = device->aik_[aik_offset++] + tid; + U[U_idx] = A[A_idx]; } - }// end of kernel - - std::chrono::nanoseconds DecomposeKernelDriver( - CudaSparseMatrixParam& sparseMatrix, - CudaSolverParam& solver){ - //create device pointers and allocate device memory - double* d_A; - double* d_L; - double* d_U; - bool* d_do_aik; - size_t* d_aik; - bool* d_do_aki; - size_t* d_aki; - size_t* d_uii; - std::pair* d_niLU; - std::pair* d_uik_nkj; - std::pair* d_lij_ujk; - std::pair* d_lki_nkj; - std::pair* d_lkj_uji; - DecomposeDevice* device; - - cudaMalloc(&d_A,sizeof(double)* sparseMatrix.A_size_); - cudaMalloc(&d_L,sizeof(double)* sparseMatrix.L_size_); - cudaMalloc(&d_U,sizeof(double)* sparseMatrix.U_size_); - cudaMalloc(&d_do_aik,sizeof(char)* solver.do_aik_size_); - cudaMalloc(&d_aik,sizeof(size_t)* solver.aik_size_); - cudaMalloc(&d_do_aki,sizeof(char)* solver.do_aki_size_); - cudaMalloc(&d_aki,sizeof(size_t)* solver.aki_size_); - cudaMalloc(&d_uii,sizeof(size_t)* solver.uii_size_); - cudaMalloc(&d_niLU,sizeof(std::pair)* solver.niLU_size_); - cudaMalloc(&d_uik_nkj,sizeof(std::pair)* solver.uik_nkj_size_); - cudaMalloc(&d_lij_ujk,sizeof(std::pair)* solver.lij_ujk_size_); - cudaMalloc(&d_lki_nkj,sizeof(std::pair)* solver.lki_nkj_size_); - cudaMalloc(&d_lkj_uji,sizeof(std::pair)* solver.lkj_uji_size_); - cudaMalloc(&device, sizeof(DecomposeDevice)); - //transfer data from host to device - cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double)* sparseMatrix.A_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char)* solver.do_aik_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_aik, solver.aik_, sizeof(size_t)* solver.aik_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char)* solver.do_aki_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_aki, solver.aki_, sizeof(size_t)*solver.aki_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_uii, solver.uii_, sizeof(size_t)* solver.uii_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair)*solver.niLU_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_uik_nkj, solver.uik_nkj_, sizeof(std::pair)*solver.uik_nkj_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_lij_ujk, solver.lij_ujk_, sizeof(std::pair)*solver.lij_ujk_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_lki_nkj, solver.lki_nkj_, sizeof(std::pair)*solver.lki_nkj_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_lkj_uji, solver.lkj_uji_, sizeof(std::pair)*solver.lkj_uji_size_, cudaMemcpyHostToDevice); - cudaMemcpy(&(device->A_),&d_A, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->L_),&d_L, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->U_),&d_U, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->do_aki_),&d_do_aki,sizeof(char*),cudaMemcpyHostToDevice); - cudaMemcpy(&(device->aki_),&d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); - - //total number of threads is number of blocks in sparseMatrix A - size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - device->n_grids_ = sparseMatrix.n_grids_; - device->niLU_size_ = solver.niLU_size_; + for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj) + { + size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; + size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; + size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; + U[U_idx_1] -= L[L_idx] * U[U_idx_2]; + ++lij_ujk_offset; + } + ++uik_nkj_offset; + } + // lower triangular matrix + + L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; + + for (size_t iL = 0; iL < inLU.first; ++iL) + { + if (device->do_aki_[do_aki_offset++]) + { + size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; + size_t A_idx = device->aki_[aki_offset++] + tid; + L[L_idx] = A[A_idx]; + } + for (size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second; ++ikj) + { + size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; + size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; + size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; + L[L_idx_1] -= L[L_idx_2] * U[U_idx]; + ++lkj_uji_offset; + } + L[lki_nkj[lki_nkj_offset].first + tid] /= U[device->uii_[uii_offset] + tid]; + ++lki_nkj_offset; + ++uii_offset; + } + } + } + } // end of kernel + + std::chrono::nanoseconds DecomposeKernelDriver(CudaSparseMatrixParam& sparseMatrix, CudaSolverParam& solver) + { + // create device pointers and allocate device memory + double* d_A; + double* d_L; + double* d_U; + bool* d_do_aik; + size_t* d_aik; + bool* d_do_aki; + size_t* d_aki; + size_t* d_uii; + std::pair* d_niLU; + std::pair* d_uik_nkj; + std::pair* d_lij_ujk; + std::pair* d_lki_nkj; + std::pair* d_lkj_uji; + DecomposeDevice* device; + + cudaMalloc(&d_A, sizeof(double) * sparseMatrix.A_size_); + cudaMalloc(&d_L, sizeof(double) * sparseMatrix.L_size_); + cudaMalloc(&d_U, sizeof(double) * sparseMatrix.U_size_); + cudaMalloc(&d_do_aik, sizeof(char) * solver.do_aik_size_); + cudaMalloc(&d_aik, sizeof(size_t) * solver.aik_size_); + cudaMalloc(&d_do_aki, sizeof(char) * solver.do_aki_size_); + cudaMalloc(&d_aki, sizeof(size_t) * solver.aki_size_); + cudaMalloc(&d_uii, sizeof(size_t) * solver.uii_size_); + cudaMalloc(&d_niLU, sizeof(std::pair) * solver.niLU_size_); + cudaMalloc(&d_uik_nkj, sizeof(std::pair) * solver.uik_nkj_size_); + cudaMalloc(&d_lij_ujk, sizeof(std::pair) * solver.lij_ujk_size_); + cudaMalloc(&d_lki_nkj, sizeof(std::pair) * solver.lki_nkj_size_); + cudaMalloc(&d_lkj_uji, sizeof(std::pair) * solver.lkj_uji_size_); + cudaMalloc(&device, sizeof(DecomposeDevice)); + + // transfer data from host to device + cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double) * sparseMatrix.A_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double) * sparseMatrix.L_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double) * sparseMatrix.U_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char) * solver.do_aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aik, solver.aik_, sizeof(size_t) * solver.aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char) * solver.do_aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aki, solver.aki_, sizeof(size_t) * solver.aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_uii, solver.uii_, sizeof(size_t) * solver.uii_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair) * solver.niLU_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_uik_nkj, solver.uik_nkj_, sizeof(std::pair) * solver.uik_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_lij_ujk, solver.lij_ujk_, sizeof(std::pair) * solver.lij_ujk_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_lki_nkj, solver.lki_nkj_, sizeof(std::pair) * solver.lki_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_lkj_uji, solver.lkj_uji_, sizeof(std::pair) * solver.lkj_uji_size_, cudaMemcpyHostToDevice); + cudaMemcpy(&(device->A_), &d_A, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->L_), &d_L, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->U_), &d_U, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aki_), &d_do_aki, sizeof(char*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aki_), &d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); + + // total number of threads is number of blocks in sparseMatrix A + size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + device->n_grids_ = sparseMatrix.n_grids_; + device->niLU_size_ = solver.niLU_size_; + + // call kernel + auto startTime = std::chrono::high_resolution_clock::now(); + DecomposeKernel<<>>(device); + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double) * sparseMatrix.L_size_, cudaMemcpyDeviceToHost); + cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double) * sparseMatrix.U_size_, cudaMemcpyDeviceToHost); - // call kernel - auto startTime = std::chrono::high_resolution_clock::now(); - DecomposeKernel<<>>(device); - cudaDeviceSynchronize(); - auto endTime = std::chrono::high_resolution_clock::now(); - auto kernel_duration = std::chrono::duration_cast(endTime - startTime); - cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyDeviceToHost); - cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyDeviceToHost); - - //clean up - cudaFree(d_A); - cudaFree(d_L); - cudaFree(d_U); - cudaFree(d_do_aik); - cudaFree(d_aik); - cudaFree(d_do_aki); - cudaFree(d_aki); - cudaFree(d_uii); - cudaFree(device); - return kernel_duration; - }//end kernelDriver - }//end cuda -}//end micm \ No newline at end of file + // clean up + cudaFree(d_A); + cudaFree(d_L); + cudaFree(d_U); + cudaFree(d_do_aik); + cudaFree(d_aik); + cudaFree(d_do_aki); + cudaFree(d_aki); + cudaFree(d_uii); + cudaFree(device); + return kernel_duration; + } // end kernelDriver + } // namespace cuda +} // namespace micm \ No newline at end of file diff --git a/test/integration/cmake/test_micm.cpp b/test/integration/cmake/test_micm.cpp index 03974f612..5db6c1890 100644 --- a/test/integration/cmake/test_micm.cpp +++ b/test/integration/cmake/test_micm.cpp @@ -18,11 +18,9 @@ void print_state(double time, State& state) oldState.copyfmt(std::cout); std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) - << std::setw(10) << state.variables_[0][state.variable_map_["A"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["C"]] - << std::endl; + std::cout << std::scientific << std::setprecision(2) << std::setw(10) << state.variables_[0][state.variable_map_["A"]] + << "," << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["C"]] << std::endl; std::cout.copyfmt(oldState); } @@ -46,8 +44,8 @@ int main() auto reactions = solver_params.processes_; RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; State state = solver.GetState(); // mol m-3 diff --git a/test/integration/e5.hpp b/test/integration/e5.hpp index 098895121..9eb7d4cb8 100644 --- a/test/integration/e5.hpp +++ b/test/integration/e5.hpp @@ -2,7 +2,10 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +template< + template class MatrixPolicy = micm::Matrix, + template class SparseMatrixPolicy = micm::SparseMatrix, + class LinearSolverPolicy = micm::LinearSolver> class E5 : public micm::RosenbrockSolver { public: diff --git a/test/integration/hires.hpp b/test/integration/hires.hpp index 59db5712f..9fb638c26 100644 --- a/test/integration/hires.hpp +++ b/test/integration/hires.hpp @@ -2,7 +2,10 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +template< + template class MatrixPolicy = micm::Matrix, + template class SparseMatrixPolicy = micm::SparseMatrix, + class LinearSolverPolicy = micm::LinearSolver> class HIRES : public micm::RosenbrockSolver { public: diff --git a/test/integration/oregonator.hpp b/test/integration/oregonator.hpp index be6619d57..42a1e0449 100644 --- a/test/integration/oregonator.hpp +++ b/test/integration/oregonator.hpp @@ -2,7 +2,10 @@ #include -template class MatrixPolicy = micm::Matrix, template class SparseMatrixPolicy = micm::SparseMatrix, class LinearSolverPolicy = micm::LinearSolver> +template< + template class MatrixPolicy = micm::Matrix, + template class SparseMatrixPolicy = micm::SparseMatrix, + class LinearSolverPolicy = micm::LinearSolver> class Oregonator : public micm::RosenbrockSolver { public: diff --git a/test/kpp/test_kpp_to_micm.cpp b/test/kpp/test_kpp_to_micm.cpp index bcff1d90a..05ad70117 100644 --- a/test/kpp/test_kpp_to_micm.cpp +++ b/test/kpp/test_kpp_to_micm.cpp @@ -24,28 +24,21 @@ void print_state(double time, micm::State& state) std::cout << std::setw(5) << time << "," << std::flush; - std::cout << std::scientific << std::setw(10) << std::setprecision(3) - << state.variables_[0][state.variable_map_["M"]] - << "," << std::setw(11) << std::setprecision(3) - << state.variables_[0][state.variable_map_["O2"]] - << "," << std::setw(11) << std::setprecision(3) - << state.variables_[0][state.variable_map_["O3"]] - << "," << std::setw(11) << std::setprecision(3) - << state.variables_[0][state.variable_map_["O"]] - << "," << std::setw(11) << std::setprecision(3) - << state.variables_[0][state.variable_map_["O1D"]] << std::endl; + std::cout << std::scientific << std::setw(10) << std::setprecision(3) << state.variables_[0][state.variable_map_["M"]] + << "," << std::setw(11) << std::setprecision(3) << state.variables_[0][state.variable_map_["O2"]] << "," + << std::setw(11) << std::setprecision(3) << state.variables_[0][state.variable_map_["O3"]] << "," + << std::setw(11) << std::setprecision(3) << state.variables_[0][state.variable_map_["O"]] << "," << std::setw(11) + << std::setprecision(3) << state.variables_[0][state.variable_map_["O1D"]] << std::endl; std::cout.copyfmt(oldState); } -int main(const int argc, const char *argv[]) +int main(const int argc, const char* argv[]) { - micm::SolverConfig solver_config; // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse( - "./configs/kpp_chapman"); + micm::ConfigParseStatus status = solver_config.ReadAndParse("./configs/kpp_chapman"); if (status != micm::ConfigParseStatus::Success) { @@ -57,10 +50,11 @@ int main(const int argc, const char *argv[]) auto& process_vector = solver_params.processes_; // Print reactions from reactions configuration - for (int i = 0; i < process_vector.size(); i++) { - + for (int i = 0; i < process_vector.size(); i++) + { int n_reactants = process_vector[i].reactants_.size(); - for (int j = 0; j < n_reactants - 1; j++) { + for (int j = 0; j < n_reactants - 1; j++) + { std::cout << process_vector[i].reactants_[j].name_ << " + "; } std::cout << process_vector[i].reactants_[n_reactants - 1].name_; @@ -68,13 +62,15 @@ int main(const int argc, const char *argv[]) std::cout << " --> "; int n_products = process_vector[i].products_.size(); - for (int j = 0; j < n_products - 1; j++) { + for (int j = 0; j < n_products - 1; j++) + { std::cout << process_vector[i].products_[j].first.name_ << " + "; } std::cout << process_vector[i].products_[n_products - 1].first.name_; std::vector param_labels = process_vector[i].rate_constant_->CustomParameters(); - for (int k = 0; k < param_labels.size(); k++) { + for (int k = 0; k < param_labels.size(); k++) + { std::cout << " " << param_labels[k]; } @@ -85,8 +81,8 @@ int main(const int argc, const char *argv[]) auto reactions = solver_params.processes_; micm::RosenbrockSolver solver{ - chemical_system, reactions, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; micm::State state = solver.GetState(); @@ -102,7 +98,7 @@ int main(const int argc, const char *argv[]) double N_Avogadro = 6.02214076e23; // molecules cm-3 -> mol m-3, z = 30 km, S&P3e table 5.1 p. 121 double n_M = 3.1e17 * 1.0e6 / N_Avogadro; - double n_O2 = 0.21 * n_M; // [O2] ~ 0.21 [M] + double n_O2 = 0.21 * n_M; // [O2] ~ 0.21 [M] // typical [O3] mid-latitude z ~ 30 km double n_O3 = 2.0e12 * 1.0e6 / N_Avogadro; // [O] / [O3] ~ 3e-5, S&P3e p. 124 @@ -125,11 +121,12 @@ int main(const int argc, const char *argv[]) print_header(); print_state(0, state); - for (int i = 0; i < nstep; ++i) { - + for (int i = 0; i < nstep; ++i) + { double elapsed_solve_time = 0; - while (elapsed_solve_time < time_step) { + while (elapsed_solve_time < time_step) + { auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; state.variables_[0] = result.result_.AsVector(); diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index fba9cefb2..f84e9df59 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -37,20 +37,16 @@ int main(const int argc, const char *argv[]) state.conditions_[0].pressure_ = 101319.9; // Pa state.SetConcentration(foo, 20.0); // mol m-3 - std::cout << std::setw(5) << "time [s]," - << std::setw(13) << "foo, " - << std::setw(12) << "bar, " - << std::setw(10) << "baz" << std::endl; + std::cout << std::setw(5) << "time [s]," << std::setw(13) << "foo, " << std::setw(12) << "bar, " << std::setw(10) << "baz" + << std::endl; for (int i = 0; i < 10; ++i) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - std::cout << std::setfill(' ') << std::fixed - << std::setw(8) << std::setprecision(2) << i * 500.0 << ", " + std::cout << std::setfill(' ') << std::fixed << std::setw(8) << std::setprecision(2) << i * 500.0 << ", " << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Foo"]] << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Bar"]] << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Baz"]] - << std::endl; + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Bar"]] << ", " + << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Baz"]] << std::endl; } return 0; diff --git a/test/tutorial/test_but_how_fast_is_it.cpp b/test/tutorial/test_but_how_fast_is_it.cpp index cdc114fd3..209296878 100644 --- a/test/tutorial/test_but_how_fast_is_it.cpp +++ b/test/tutorial/test_but_how_fast_is_it.cpp @@ -1,8 +1,8 @@ +#include #include #include #include #include -#include // Use our namespace so that this example is easier to read using namespace micm; @@ -34,9 +34,8 @@ int main() .phase(gas_phase); RosenbrockSolver<> solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters( - 3, false) }; + std::vector{ r1, r2, r3 }, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) }; State state = solver.GetState(); diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index bf0216108..988d814a1 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -70,11 +70,9 @@ int main() .rate_constant(micm::UserDefinedRateConstant({ .label_ = "r3" })) .phase(gas_phase); - micm::RosenbrockSolver<> solver{ - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) - }; + micm::RosenbrockSolver<> solver{ micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) }; State state = solver.GetState(); diff --git a/test/tutorial/test_openmp.cpp b/test/tutorial/test_openmp.cpp index ead3829ff..16a935cab 100644 --- a/test/tutorial/test_openmp.cpp +++ b/test/tutorial/test_openmp.cpp @@ -17,11 +17,8 @@ void print_results(std::vector results) std::ios oldState(nullptr); oldState.copyfmt(std::cout); - std::cout << std::scientific << std::setprecision(2) - << std::setw(10) << results[0] << "," - << std::setw(10) << results[1] << "," - << std::setw(10) << results[2] - << std::endl; + std::cout << std::scientific << std::setprecision(2) << std::setw(10) << results[0] << "," << std::setw(10) << results[1] + << "," << std::setw(10) << results[2] << std::endl; std::cout.copyfmt(oldState); } @@ -29,8 +26,8 @@ void print_results(std::vector results) std::vector test_solver_on_thread(System chemical_system, std::vector reactions) { RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; State state = solver.GetState(); // mol m-3 diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index df997e096..5ca59be84 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -146,9 +146,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = System(micm::SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3, r4, r5, r6, r7, r8, r9, r10 }; - RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); state.conditions_[0].temperature_ = 287.45; // K diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index 60ba430e4..59ddbb5d8 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -62,9 +62,7 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp index 611fadecc..e52b5d76b 100644 --- a/test/tutorial/test_solver_configuration.cpp +++ b/test/tutorial/test_solver_configuration.cpp @@ -1,6 +1,6 @@ +#include #include #include -#include #include #include @@ -22,11 +22,9 @@ void print_state(double time, State& state) oldState.copyfmt(std::cout); std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) - << std::setw(10) << state.variables_[0][state.variable_map_["A"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," - << std::setw(10) << state.variables_[0][state.variable_map_["C"]] - << std::endl; + std::cout << std::scientific << std::setprecision(2) << std::setw(10) << state.variables_[0][state.variable_map_["A"]] + << "," << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) + << state.variables_[0][state.variable_map_["C"]] << std::endl; std::cout.copyfmt(oldState); } @@ -37,7 +35,7 @@ void test_solver_type(T solver) State state = solver.GetState(); // mol m-3 - state.variables_[0] = {1, 0, 0}; + state.variables_[0] = { 1, 0, 0 }; double k1 = 0.04; double k2 = 3e7; @@ -61,8 +59,8 @@ void test_solver_type(T solver) print_state(0, state); typename T::SolverStats total_stats; - std::chrono::duration total_solve_time = std::chrono::nanoseconds::zero();; - + std::chrono::duration total_solve_time = std::chrono::nanoseconds::zero(); + ; // solve for ten iterations for (int i = 0; i < 10; ++i) @@ -144,25 +142,19 @@ int main() auto system = System(SystemParameters{ .gas_phase_ = gas_phase }); auto reactions = std::vector{ r1, r2, r3 }; - RosenbrockSolver<> two_stage{ - system, reactions, RosenbrockSolverParameters::two_stage_rosenbrock_parameters() - }; + RosenbrockSolver<> two_stage{ system, reactions, RosenbrockSolverParameters::two_stage_rosenbrock_parameters() }; - RosenbrockSolver<> three_stage{ - system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + RosenbrockSolver<> three_stage{ system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - RosenbrockSolver<> four_stage{ - system, reactions, RosenbrockSolverParameters::four_stage_rosenbrock_parameters() - }; + RosenbrockSolver<> four_stage{ system, reactions, RosenbrockSolverParameters::four_stage_rosenbrock_parameters() }; - RosenbrockSolver<> four_stage_da{ - system, reactions, RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters() - }; + RosenbrockSolver<> four_stage_da{ system, + reactions, + RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters() }; - RosenbrockSolver<> six_stage_da{ - system, reactions, RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters() - }; + RosenbrockSolver<> six_stage_da{ system, + reactions, + RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters() }; std::cout << "Two stages: " << std::endl; test_solver_type(two_stage); diff --git a/test/unit/openmp/test_openmp.cpp b/test/unit/openmp/test_openmp.cpp index c398143e5..94707145f 100644 --- a/test/unit/openmp/test_openmp.cpp +++ b/test/unit/openmp/test_openmp.cpp @@ -12,9 +12,7 @@ using SparseMatrixPolicy = SparseMatrix; std::vector test_solver_on_thread(System chemical_system, std::vector reactions) { std::cout << "Running solver on thread " << omp_get_thread_num() << std::endl; - RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; State state = solver.GetState(); // mol m-3 @@ -76,10 +74,11 @@ TEST(OpenMP, OneFileReadThreeThreads) { std::vector result = test_solver_on_thread(chemical_system, reactions); results[omp_get_thread_num()] = result; -#pragma omp barrier +#pragma omp barrier } - for(int i = 0; i < results[0].size(); ++i) { + for (int i = 0; i < results[0].size(); ++i) + { EXPECT_EQ(results[0][i], results[1][i]); EXPECT_EQ(results[0][i], results[2][i]); EXPECT_EQ(results[1][i], results[2][i]); diff --git a/test/unit/process/test_cuda_process_set.cpp b/test/unit/process/test_cuda_process_set.cpp index 8448c7c06..2fbdc9bd2 100644 --- a/test/unit/process/test_cuda_process_set.cpp +++ b/test/unit/process/test_cuda_process_set.cpp @@ -1,4 +1,5 @@ #include + #include #include #include @@ -178,5 +179,5 @@ TEST(RandomCudaProcessSet, Forcing) } TEST(RandomCudaProcessSet, Jacobian) { - testRandomSystemAddJacobianTerms(10000,500,400); + testRandomSystemAddJacobianTerms(10000, 500, 400); } diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index bf5088e74..a3112e573 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -95,13 +95,12 @@ TEST(Process, SurfaceRateConstantOnlyHasOneReactant) { micm::Species c("c", { { "molecular weight [kg mol-1]", 0.025 }, { "diffusion coefficient [m2 s-1]", 2.3e2 } }); micm::Species e("e"); - - micm::Phase gas_phase({c, e}); - EXPECT_ANY_THROW( - micm::Process r = micm::Process::create() - .reactants({ c, c }) - .products({ yields(e, 1) }) - .rate_constant(micm::SurfaceRateConstant({ .label_ = "c", .species_ = c, .reaction_probability_ = 0.90 })) - .phase(gas_phase); - ); + + micm::Phase gas_phase({ c, e }); + EXPECT_ANY_THROW(micm::Process r = micm::Process::create() + .reactants({ c, c }) + .products({ yields(e, 1) }) + .rate_constant(micm::SurfaceRateConstant( + { .label_ = "c", .species_ = c, .reaction_probability_ = 0.90 })) + .phase(gas_phase);); } diff --git a/test/unit/solver/test_cuda_lu_decomposition.cpp b/test/unit/solver/test_cuda_lu_decomposition.cpp index fa1cf4ed7..06512d939 100644 --- a/test/unit/solver/test_cuda_lu_decomposition.cpp +++ b/test/unit/solver/test_cuda_lu_decomposition.cpp @@ -1,9 +1,10 @@ #pragma once #include -#include + #include +#include #include -#include +#include #include #include #include @@ -51,22 +52,24 @@ void check_results( template class SparseMatrixPolicy> void gpu_validation( const SparseMatrixPolicy& gpu_L, - const SparseMatrixPolicy& cpu_L, + const SparseMatrixPolicy& cpu_L, const SparseMatrixPolicy& gpu_U, const SparseMatrixPolicy& cpu_U) { - size_t L_size = cpu_L.AsVector().size(); - size_t U_size = cpu_U.AsVector().size(); - std::vector gpu_L_vector = gpu_L.AsVector(); - std::vector cpu_L_vector = cpu_L.AsVector(); - std::vector gpu_U_vector = gpu_U.AsVector(); - std::vector cpu_U_vector = cpu_U.AsVector(); - for (int i = 0; i < L_size; i++){ - EXPECT_EQ(gpu_L_vector[i], cpu_L_vector[i]); - }; - for (int j = 0; j < U_size; j++){ - EXPECT_EQ(gpu_U_vector[j], cpu_U_vector[j]); - }; + size_t L_size = cpu_L.AsVector().size(); + size_t U_size = cpu_U.AsVector().size(); + std::vector gpu_L_vector = gpu_L.AsVector(); + std::vector cpu_L_vector = cpu_L.AsVector(); + std::vector gpu_U_vector = gpu_U.AsVector(); + std::vector cpu_U_vector = cpu_U.AsVector(); + for (int i = 0; i < L_size; i++) + { + EXPECT_EQ(gpu_L_vector[i], cpu_L_vector[i]); + }; + for (int j = 0; j < U_size; j++) + { + EXPECT_EQ(gpu_U_vector[j], cpu_U_vector[j]); + }; } template class SparseMatrixPolicy> @@ -89,19 +92,19 @@ void testRandomMatrix(size_t n_grids) for (std::size_t i_block = 0; i_block < n_grids; ++i_block) A[i_block][i][j] = get_double(); - micm::LuDecomposition gpu_lud(A); - auto gpu_LU = micm::CudaLuDecomposition::GetLUMatrices(A, 1.0e-30); - gpu_lud.Decompose(A, gpu_LU.first, gpu_LU.second); + micm::LuDecomposition gpu_lud(A); + auto gpu_LU = micm::CudaLuDecomposition::GetLUMatrices(A, 1.0e-30); + gpu_lud.Decompose(A, gpu_LU.first, gpu_LU.second); check_results( - A, gpu_LU.first, gpu_LU.second, [&](const double a, const double b) -> void {EXPECT_NEAR(a, b, 1.0e-5); }); + A, gpu_LU.first, gpu_LU.second, [&](const double a, const double b) -> void { EXPECT_NEAR(a, b, 1.0e-5); }); micm::LuDecomposition cpu_lud(A); auto cpu_LU = micm::LuDecomposition::GetLUMatrices(A, 1.0e-30); cpu_lud.Decompose(A, cpu_LU.first, cpu_LU.second); - - //checking GPU result again CPU - gpu_validation(gpu_LU.first, cpu_LU.first, gpu_LU.second, cpu_LU.second); -} + + // checking GPU result again CPU + gpu_validation(gpu_LU.first, cpu_LU.first, gpu_LU.second, cpu_LU.second); +} template using Group1SparseVectorMatrix = micm::SparseMatrix>; diff --git a/test/unit/solver/test_lu_decomposition.cpp b/test/unit/solver/test_lu_decomposition.cpp index 6588083f2..5a91e43e3 100644 --- a/test/unit/solver/test_lu_decomposition.cpp +++ b/test/unit/solver/test_lu_decomposition.cpp @@ -26,8 +26,8 @@ TEST(LuDecomposition, DenseMatrixStandardOrdering) TEST(LuDecomposition, SingularMatrixStandardOrdering) { - testSingularMatrix( - [](const SparseMatrixTest& matrix) -> micm::LuDecomposition { return micm::LuDecomposition{ matrix }; }); + testSingularMatrix( + [](const SparseMatrixTest& matrix) -> micm::LuDecomposition { return micm::LuDecomposition{ matrix }; }); } TEST(LuDecomposition, RandomMatrixStandardOrdering) diff --git a/test/unit/solver/test_rosenbrock.cpp b/test/unit/solver/test_rosenbrock.cpp index e2e019b26..48b44ede2 100644 --- a/test/unit/solver/test_rosenbrock.cpp +++ b/test/unit/solver/test_rosenbrock.cpp @@ -124,10 +124,14 @@ using Group4SparseVectorMatrix = micm::SparseMatrix>(1); - testAlphaMinusJacobian>(4); - testAlphaMinusJacobian>(3); - testAlphaMinusJacobian>(2); + testAlphaMinusJacobian>( + 1); + testAlphaMinusJacobian>( + 4); + testAlphaMinusJacobian>( + 3); + testAlphaMinusJacobian>( + 2); } TEST(RosenbrockSolver, Timing) From eab5c0276cddee394d6c09ba347e6cda58244879 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Mon, 23 Oct 2023 21:15:38 -0600 Subject: [PATCH 158/318] Populate processes.phase with species array. --- include/micm/configure/camp_config.hpp | 8 ++++++++ test/unit/configure/test_camp_config.cpp | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 62d2f709c..d20302031 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -196,6 +196,14 @@ namespace micm std::cout << "Parse species_arr_ size " << species_arr_.size() << std::endl; gas_phase_ = Phase(species_arr_); + for (auto& p : processes_) + { + for (const auto& s : species_arr_) + { + p.phase_.species_.push_back(s); + } + } + return status; } diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index f245e3c3e..ccd51f60e 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -84,7 +84,7 @@ TEST(SolverConfig, ReadAndParseProcessObjects) EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); if (num_products_in_each_process[idx] > 0) EXPECT_EQ(p.products_[0].second, yield_value_of_first_product_in_each_process[idx]); - // EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); + EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); idx++; } @@ -194,7 +194,7 @@ TEST(SolverConfig, ReadAndParseProcessObjectsfromMZ326) { EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); - // EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); + EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); idx++; } From c984438711f2362c39cc1c3dd104658a735f871e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 23 Oct 2023 21:23:02 -0600 Subject: [PATCH 159/318] Removed CAMP invalid file unit test. --- test/unit/configure/test_camp_config.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index ccd51f60e..43e40ab8f 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -2,15 +2,6 @@ #include -/* -TEST(SolverConfig, DetectsInvalidConfigFile) -{ - micm::SolverConfig solverConfig{}; - auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); -} -*/ - TEST(SolverConfig, ReadAndParseCAMPFiles) { micm::SolverConfig solverConfig{}; From ea61fda31d65e7a50f3a9d29134a7f63771374b9 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 24 Oct 2023 09:25:30 -0600 Subject: [PATCH 160/318] Commented some debug statements. --- include/micm/configure/camp_config.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index d20302031..04eabe183 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -172,7 +172,6 @@ namespace micm for (const auto& camp_file : camp_files) { - std::cout << "JsonReaderPolicy.Parse CAMP file " << camp_file << std::endl; json config_subset = json::parse(std::ifstream(camp_file)); if (config_subset.contains(CAMP_DATA)) @@ -193,7 +192,6 @@ namespace micm status = ParseObjectArray(objects); // Assign the parsed 'Species' to 'Phase' - std::cout << "Parse species_arr_ size " << species_arr_.size() << std::endl; gas_phase_ = Phase(species_arr_); for (auto& p : processes_) @@ -222,8 +220,9 @@ namespace micm } std::string type = object[TYPE].get(); - std::cout << type << std::endl; - std::cout << object.dump(4) << std::endl; + // debug statements + // std::cout << type << std::endl; + // std::cout << object.dump(4) << std::endl; if (type == "CHEM_SPEC") { From 55c0ddd952dc1bfb79f8aa24f69bd32277c28bc8 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 24 Oct 2023 14:30:06 -0600 Subject: [PATCH 161/318] Added unit_config config.json files. --- include/micm/configure/camp_config.hpp | 27 ---------------------- test/unit/unit_configs/MZ326/config.json | 1 + test/unit/unit_configs/chapman/config.json | 1 + 3 files changed, 2 insertions(+), 27 deletions(-) create mode 100644 test/unit/unit_configs/MZ326/config.json create mode 100644 test/unit/unit_configs/chapman/config.json diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 04eabe183..0168a2429 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -103,10 +103,6 @@ namespace micm // Constants // Configure files static const inline std::string CAMP_CONFIG = "config.json"; - static const inline std::string SPECIES_CONFIG = "species.json"; - static const inline std::string MECHANISM_CONFIG = "mechanism.json"; - static const inline std::string REACTIONS_CONFIG = "reactions.json"; - static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; // Common JSON static const inline std::string CAMP_DATA = "camp-data"; @@ -135,29 +131,6 @@ namespace micm camp_files.push_back(config_dir / element.get()); } } - else - { - std::filesystem::path species_config(config_dir / SPECIES_CONFIG); - std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); - std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); - std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); - if (std::filesystem::exists(species_config)) - { - camp_files.push_back(species_config); - } - if (std::filesystem::exists(mechanism_config)) - { - camp_files.push_back(mechanism_config); - } - if (std::filesystem::exists(reactions_config)) - { - camp_files.push_back(reactions_config); - } - if (std::filesystem::exists(tolerance_config)) - { - camp_files.push_back(tolerance_config); - } - } // No config files found if (camp_files.size() < 1) diff --git a/test/unit/unit_configs/MZ326/config.json b/test/unit/unit_configs/MZ326/config.json new file mode 100644 index 000000000..60b3f556f --- /dev/null +++ b/test/unit/unit_configs/MZ326/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "tolerance.json", "reactions.json"]} diff --git a/test/unit/unit_configs/chapman/config.json b/test/unit/unit_configs/chapman/config.json new file mode 100644 index 000000000..045297e2b --- /dev/null +++ b/test/unit/unit_configs/chapman/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "mechanism.json"]} From 458a2e857bad3a00543dd46db2805858a5cd266d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 10:13:36 -0600 Subject: [PATCH 162/318] Switch to configuration file argument to Parse, extract configuration dir from file path. --- include/micm/configure/camp_config.hpp | 15 +++++++-------- test/unit/configure/test_camp_config.cpp | 10 +++++----- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 0168a2429..797d5bd94 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -100,26 +100,25 @@ namespace micm std::unordered_map phases_; std::vector processes_; - // Constants - // Configure files - static const inline std::string CAMP_CONFIG = "config.json"; - // Common JSON - static const inline std::string CAMP_DATA = "camp-data"; static const inline std::string CAMP_FILES = "camp-files"; + static const inline std::string CAMP_DATA = "camp-data"; static const inline std::string TYPE = "type"; // Functions /// @brief Parse configures - /// @param config_dir Path to a the configuration directory + /// @param config_file Path to a the configuration file /// @return True for successful parsing - ConfigParseStatus Parse(const std::filesystem::path& config_dir) + ConfigParseStatus Parse(const std::filesystem::path& config_file) { + // Extract configuration dir from configuration file path + std::filesystem::path config_dir = config_file.parent_path(); + std::vector camp_files; // Look for CAMP config file - std::filesystem::path camp_config(config_dir / CAMP_CONFIG); + std::filesystem::path camp_config(config_file); if (std::filesystem::exists(camp_config)) { json camp_data = json::parse(std::ifstream(camp_config)); diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 43e40ab8f..934ae6ca1 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -7,7 +7,7 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) micm::SolverConfig solverConfig{}; // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); } @@ -16,7 +16,7 @@ TEST(SolverConfig, ReadAndParseSystemObject) micm::SolverConfig solverConfig; // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); // Get solver parameters ('System', the collection of 'Process') @@ -49,7 +49,7 @@ TEST(SolverConfig, ReadAndParseProcessObjects) micm::SolverConfig solverConfig; // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); // Get solver parameters ('System', the collection of 'Process') @@ -133,7 +133,7 @@ TEST(SolverConfig, ReadAndParseProcessObjects) TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) { micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); // Get solver parameters ('System', the collection of 'Process') @@ -163,7 +163,7 @@ TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) TEST(SolverConfig, ReadAndParseProcessObjectsfromMZ326) { micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); // Get solver parameters ('System', the collection of 'Process') From 50414f11b6863a49f8f5c5655992b9a29664bf2b Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 10:28:16 -0600 Subject: [PATCH 163/318] Removed tolerance.json from CAMP file list for solver_config unit tests. --- test/unit/unit_configs/MZ326/config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/unit_configs/MZ326/config.json b/test/unit/unit_configs/MZ326/config.json index 60b3f556f..61165b00d 100644 --- a/test/unit/unit_configs/MZ326/config.json +++ b/test/unit/unit_configs/MZ326/config.json @@ -1 +1 @@ -{"camp-files": ["species.json", "tolerance.json", "reactions.json"]} +{"camp-files": ["species.json", "reactions.json"]} From ad21ac1ee47fe629c635468a1b47996241960d7e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 10:52:19 -0600 Subject: [PATCH 164/318] Check for invalid CAMP file path. --- include/micm/configure/camp_config.hpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 797d5bd94..5a4c370b0 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -108,26 +108,33 @@ namespace micm // Functions /// @brief Parse configures - /// @param config_file Path to a the configuration file + /// @param config_file Path to a the CAMP configuration file /// @return True for successful parsing ConfigParseStatus Parse(const std::filesystem::path& config_file) { + if (!std::filesystem::exists(config_file)) + { + std::string msg = "Invalid CAMP configuration file path"; + std::cerr << msg << std::endl; + return ConfigParseStatus::InvalidCAMPFilePath; + } + // Extract configuration dir from configuration file path std::filesystem::path config_dir = config_file.parent_path(); std::vector camp_files; // Look for CAMP config file - std::filesystem::path camp_config(config_file); - if (std::filesystem::exists(camp_config)) - { - json camp_data = json::parse(std::ifstream(camp_config)); - if (!camp_data.contains(CAMP_FILES)) - return ConfigParseStatus::CAMPFilesSectionNotFound; + json camp_data = json::parse(std::ifstream(config_file)); + if (!camp_data.contains(CAMP_FILES)) + return ConfigParseStatus::CAMPFilesSectionNotFound; - for (const auto& element : camp_data[CAMP_FILES]) + for (const auto& element : camp_data[CAMP_FILES]) + { + std::filesystem::path camp_file = config_dir / element.get(); + if (std::filesystem::exists(camp_file)) { - camp_files.push_back(config_dir / element.get()); + camp_files.push_back(camp_file); } } From 0e3a4e4427f92d25b40dc2937927cb1de26cfc9e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 11:09:30 -0600 Subject: [PATCH 165/318] Added some unit tests for config files not found. --- include/micm/configure/camp_config.hpp | 7 +++++-- test/unit/configure/test_camp_config.cpp | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 5a4c370b0..6736e0107 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -112,6 +112,7 @@ namespace micm /// @return True for successful parsing ConfigParseStatus Parse(const std::filesystem::path& config_file) { + // Look for CAMP config file if (!std::filesystem::exists(config_file)) { std::string msg = "Invalid CAMP configuration file path"; @@ -122,9 +123,10 @@ namespace micm // Extract configuration dir from configuration file path std::filesystem::path config_dir = config_file.parent_path(); + // The CAMP file list std::vector camp_files; - // Look for CAMP config file + // Load the CAMP file list JSON json camp_data = json::parse(std::ifstream(config_file)); if (!camp_data.contains(CAMP_FILES)) return ConfigParseStatus::CAMPFilesSectionNotFound; @@ -136,12 +138,13 @@ namespace micm { camp_files.push_back(camp_file); } + // Error return here if CAMP files list has a missing file? } // No config files found if (camp_files.size() < 1) { - std::string msg = "No config files found"; + std::string msg = "No CAMP list files found"; std::cerr << msg << std::endl; return ConfigParseStatus::NoConfigFilesFound; } diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 934ae6ca1..b839c6fad 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -2,6 +2,20 @@ #include +TEST(SolverConfig, DetectsInvalidConfigFile) +{ + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("not_a_config_file"); + EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); +} + +TEST(SolverConfig, NoConfigFilesFound) +{ + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid/config.json"); + EXPECT_EQ(micm::ConfigParseStatus::NoConfigFilesFound, status); +} + TEST(SolverConfig, ReadAndParseCAMPFiles) { micm::SolverConfig solverConfig{}; From 91ea2df1a28712a9745c9b9e5d25f2f754bcc4f2 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 11:33:58 -0600 Subject: [PATCH 166/318] Clear all vectors and maps within JsonReader before parsing the object list. --- include/micm/configure/camp_config.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 6736e0107..3553fb477 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -171,6 +171,19 @@ namespace micm ConfigParseStatus status; + // Clear vectors and maps + species_arr_.clear(); + user_defined_rate_arr_.clear(); + arrhenius_rate_arr_.clear(); + troe_rate_arr_.clear(); + branched_rate_arr_.clear(); + surface_rate_arr_.clear(); + ternary_rate_arr_.clear(); + tunneling_rate_arr_.clear(); + phases_.clear(); + processes_.clear(); + + // Parse data object list status = ParseObjectArray(objects); // Assign the parsed 'Species' to 'Phase' From a3734c4e8fec36103bddf8f932e952fbe83c41fb Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 11:46:23 -0600 Subject: [PATCH 167/318] Call configStatusToString for error messages. --- include/micm/configure/camp_config.hpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 3553fb477..23b5c985f 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -112,12 +112,16 @@ namespace micm /// @return True for successful parsing ConfigParseStatus Parse(const std::filesystem::path& config_file) { + // Parse status + ConfigParseStatus status; + // Look for CAMP config file if (!std::filesystem::exists(config_file)) { - std::string msg = "Invalid CAMP configuration file path"; + status = ConfigParseStatus::InvalidCAMPFilePath; + std::string msg = configParseStatusToString(status); std::cerr << msg << std::endl; - return ConfigParseStatus::InvalidCAMPFilePath; + return status; } // Extract configuration dir from configuration file path @@ -129,7 +133,12 @@ namespace micm // Load the CAMP file list JSON json camp_data = json::parse(std::ifstream(config_file)); if (!camp_data.contains(CAMP_FILES)) - return ConfigParseStatus::CAMPFilesSectionNotFound; + { + status = ConfigParseStatus::CAMPFilesSectionNotFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } for (const auto& element : camp_data[CAMP_FILES]) { @@ -144,9 +153,10 @@ namespace micm // No config files found if (camp_files.size() < 1) { - std::string msg = "No CAMP list files found"; + status = ConfigParseStatus::NoConfigFilesFound; + std::string msg = configParseStatusToString(status); std::cerr << msg << std::endl; - return ConfigParseStatus::NoConfigFilesFound; + return status; } // Iterate CAMP file list and form CAMP data object list @@ -169,7 +179,6 @@ namespace micm } } - ConfigParseStatus status; // Clear vectors and maps species_arr_.clear(); From 5ee2a66afbbe7b18a741f0ca8483cf341cdecd42 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 14:36:20 -0600 Subject: [PATCH 168/318] Added test/surface_rxt subdir. --- test/CMakeLists.txt | 1 + test/surface_rxn/CMakeLists.txt | 21 +++++++++++++++++++++ test/surface_rxn/config.json | 1 + test/surface_rxn/test_surface_rxn.cpp | 11 +++++++++++ 4 files changed, 34 insertions(+) create mode 100644 test/surface_rxn/CMakeLists.txt create mode 100644 test/surface_rxn/config.json create mode 100644 test/surface_rxn/test_surface_rxn.cpp diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a87f8b2f7..37f830a1e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,3 +6,4 @@ add_subdirectory(regression) add_subdirectory(integration) add_subdirectory(tutorial) add_subdirectory(kpp) +add_subdirectory(surface_rxn) diff --git a/test/surface_rxn/CMakeLists.txt b/test/surface_rxn/CMakeLists.txt new file mode 100644 index 000000000..79a20318c --- /dev/null +++ b/test/surface_rxn/CMakeLists.txt @@ -0,0 +1,21 @@ +################################################################################ +# Test utilities + +include(test_util) + +################################################################################ +# Tests + +if(ENABLE_JSON) + create_standard_test(NAME surface_rxn SOURCES test_surface_rxn.cpp) +endif() + +################################################################################ + +################################################################################ +# Copy test data + +add_custom_target(copy_kpp_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_CURRENT_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) + +################################################################################ diff --git a/test/surface_rxn/config.json b/test/surface_rxn/config.json new file mode 100644 index 000000000..045297e2b --- /dev/null +++ b/test/surface_rxn/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "mechanism.json"]} diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp new file mode 100644 index 000000000..bc82ccf17 --- /dev/null +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -0,0 +1,11 @@ +#include +#include +#include + + +int main(const int argc, const char* argv[]) +{ + micm::SolverConfig solver_config; + + return 0; +} From f12db33c0d60ed2b4131f31134380fb6420b59a1 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 14:42:19 -0600 Subject: [PATCH 169/318] Added CAMP config list. --- test/surface_rxn/configs/config.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/surface_rxn/configs/config.json diff --git a/test/surface_rxn/configs/config.json b/test/surface_rxn/configs/config.json new file mode 100644 index 000000000..045297e2b --- /dev/null +++ b/test/surface_rxn/configs/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "mechanism.json"]} From 7213156ba975a4de5c5f4d7e710f67a5e3fd9336 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 14:45:57 -0600 Subject: [PATCH 170/318] Fixed CMakeLists. --- test/surface_rxn/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/surface_rxn/CMakeLists.txt b/test/surface_rxn/CMakeLists.txt index 79a20318c..70b717586 100644 --- a/test/surface_rxn/CMakeLists.txt +++ b/test/surface_rxn/CMakeLists.txt @@ -15,7 +15,7 @@ endif() ################################################################################ # Copy test data -add_custom_target(copy_kpp_configs ALL ${CMAKE_COMMAND} -E copy_directory +add_custom_target(copy_surface_rxn_configs ALL ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) ################################################################################ From 3e4024c2dd63de1d2094aac9530bc6f4c43246d6 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 25 Oct 2023 16:16:07 -0500 Subject: [PATCH 171/318] 164 prevent non standard keys in json (#317) * adding a schema validator * preventing nonstandard keys and adding tests for troe and arrhenius * checking qty and yield on products and reactants * checking for invalid quantity and yeild for every reaction type... * no nonstandard keys are allowed in reaction types now * added all optional keys for chemical species * correcting names * setting a thing to make nvidia happy * correcting comment --- include/micm/configure/solver_config.hpp | 283 ++++++++++++++---- test/unit/configure/process/CMakeLists.txt | 2 + .../process/test_arrhenius_config.cpp | 114 +++++++ .../process/test_branched_config.cpp | 27 ++ .../process/test_emission_config.cpp | 8 + .../process/test_first_order_loss_config.cpp | 8 + .../process/test_photolysis_config.cpp | 24 ++ .../configure/process/test_surface_config.cpp | 17 ++ ...est_ternary_chemical_activation_config.cpp | 24 ++ .../configure/process/test_troe_config.cpp | 98 ++++++ .../process/test_tunneling_config.cpp | 24 ++ test/unit/configure/test_solver_config.cpp | 127 -------- test/unit/unit_configs/MZ326/reactions.json | 87 ------ test/unit/unit_configs/MZ326/species.json | 40 --- .../contains_nonstandard_key/reactions.json | 24 ++ .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key}/tolerance.json | 0 .../arrhenius/missing_products/reactions.json | 17 ++ .../arrhenius/missing_products/species.json | 16 + .../arrhenius/missing_products/tolerance.json | 8 + .../missing_reactants/reactions.json | 17 ++ .../arrhenius/missing_reactants/species.json | 16 + .../missing_reactants/tolerance.json | 8 + .../mutually_exclusive/reactions.json | 27 ++ .../arrhenius/mutually_exclusive/species.json | 20 ++ .../mutually_exclusive/tolerance.json | 8 + .../nonstandard_product_coef/reactions.json | 20 ++ .../nonstandard_product_coef/species.json | 20 ++ .../nonstandard_product_coef/tolerance.json | 8 + .../nonstandard_reactant_coef/reactions.json | 20 ++ .../nonstandard_reactant_coef/species.json | 20 ++ .../nonstandard_reactant_coef/tolerance.json | 8 + .../process/arrhenius/valid/reactions.json | 54 ++++ .../process/arrhenius/valid/species.json | 20 ++ .../process/arrhenius/valid/tolerance.json | 8 + .../contains_nonstandard_key/reactions.json | 52 ++++ .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../branched/missing_reactants/reactions.json | 6 +- .../reactions.json | 27 ++ .../species.json | 20 ++ .../tolerance.json | 8 + .../reactions.json | 27 ++ .../species.json | 20 ++ .../tolerance.json | 8 + .../nonstandard_reactant_coef/reactions.json | 27 ++ .../nonstandard_reactant_coef/species.json | 20 ++ .../nonstandard_reactant_coef/tolerance.json | 8 + .../process/branched/valid/reactions.json | 1 + .../contains_nonstandard_key/reactions.json | 16 + .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../process/emission/valid/reactions.json | 1 + .../contains_nonstandard_key/reactions.json | 16 + .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../first_order_loss/valid/reactions.json | 1 + .../contains_nonstandard_key/reactions.json | 33 ++ .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../nonstandard_product_coef/reactions.json | 21 ++ .../nonstandard_product_coef/species.json | 20 ++ .../nonstandard_product_coef/tolerance.json | 8 + .../nonstandard_reactant_coef/reactions.json | 21 ++ .../nonstandard_reactant_coef/species.json | 20 ++ .../nonstandard_reactant_coef/tolerance.json | 8 + .../contains_nonstandard_key/reactions.json | 20 ++ .../contains_nonstandard_key/species.json | 24 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../nonstandard_product_coef/reactions.json | 20 ++ .../nonstandard_product_coef/species.json | 24 ++ .../nonstandard_product_coef/tolerance.json | 8 + .../process/surface/valid/reactions.json | 1 + .../contains_nonstandard_key/reactions.json | 40 +++ .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../nonstandard_product_coef/reactions.json | 22 ++ .../nonstandard_product_coef/species.json | 20 ++ .../nonstandard_product_coef/tolerance.json | 8 + .../nonstandard_reactant_coef/reactions.json | 22 ++ .../nonstandard_reactant_coef/species.json | 20 ++ .../nonstandard_reactant_coef/tolerance.json | 8 + .../valid/reactions.json | 1 + .../contains_nonstandard_key/reactions.json | 40 +++ .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../troe/missing_products/reactions.json | 17 ++ .../troe/missing_products/species.json | 16 + .../troe/missing_products/tolerance.json | 8 + .../troe/missing_reactants/reactions.json | 17 ++ .../troe/missing_reactants/species.json | 16 + .../troe/missing_reactants/tolerance.json | 8 + .../nonstandard_product_coef/reactions.json | 22 ++ .../nonstandard_product_coef/species.json | 20 ++ .../nonstandard_product_coef/tolerance.json | 8 + .../nonstandard_reactant_coef/reactions.json | 22 ++ .../nonstandard_reactant_coef/species.json | 20 ++ .../nonstandard_reactant_coef/tolerance.json | 8 + .../process/troe/valid/reactions.json | 41 +++ .../process/troe/valid/species.json | 20 ++ .../process/troe/valid/tolerance.json | 8 + .../contains_nonstandard_key/reactions.json | 22 ++ .../contains_nonstandard_key/species.json | 20 ++ .../contains_nonstandard_key/tolerance.json | 8 + .../nonstandard_product_coef/reactions.json | 22 ++ .../nonstandard_product_coef/species.json | 20 ++ .../nonstandard_product_coef/tolerance.json | 8 + .../nonstandard_reactant_coef/reactions.json | 22 ++ .../nonstandard_reactant_coef/species.json | 20 ++ .../nonstandard_reactant_coef/tolerance.json | 8 + .../process/tunneling/valid/reactions.json | 1 + 111 files changed, 2170 insertions(+), 317 deletions(-) create mode 100644 test/unit/configure/process/test_arrhenius_config.cpp create mode 100644 test/unit/configure/process/test_troe_config.cpp delete mode 100644 test/unit/unit_configs/MZ326/reactions.json delete mode 100644 test/unit/unit_configs/MZ326/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/species.json rename test/unit/unit_configs/{MZ326 => process/arrhenius/contains_nonstandard_key}/tolerance.json (100%) create mode 100644 test/unit/unit_configs/process/arrhenius/missing_products/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/missing_products/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/missing_products/tolerance.json create mode 100644 test/unit/unit_configs/process/arrhenius/missing_reactants/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/missing_reactants/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/missing_reactants/tolerance.json create mode 100644 test/unit/unit_configs/process/arrhenius/mutually_exclusive/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/mutually_exclusive/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/mutually_exclusive/tolerance.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/arrhenius/valid/reactions.json create mode 100644 test/unit/unit_configs/process/arrhenius/valid/species.json create mode 100644 test/unit/unit_configs/process/arrhenius/valid/tolerance.json create mode 100644 test/unit/unit_configs/process/branched/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/branched/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/branched/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/species.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/species.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_reactant_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/emission/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/emission/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/emission/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/photolysis/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/photolysis/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/photolysis/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/surface/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/surface/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/surface/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/surface/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/surface/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/surface/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/troe/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/troe/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/troe/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/troe/missing_products/reactions.json create mode 100644 test/unit/unit_configs/process/troe/missing_products/species.json create mode 100644 test/unit/unit_configs/process/troe/missing_products/tolerance.json create mode 100644 test/unit/unit_configs/process/troe/missing_reactants/reactions.json create mode 100644 test/unit/unit_configs/process/troe/missing_reactants/species.json create mode 100644 test/unit/unit_configs/process/troe/missing_reactants/tolerance.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_reactant_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/troe/valid/reactions.json create mode 100644 test/unit/unit_configs/process/troe/valid/species.json create mode 100644 test/unit/unit_configs/process/troe/valid/tolerance.json create mode 100644 test/unit/unit_configs/process/tunneling/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/tunneling/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/tunneling/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/tolerance.json diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index ea004b40e..b4df2a88c 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -39,7 +39,9 @@ namespace micm CAMPDataSectionNotFound, InvalidMechanism, ObjectTypeNotFound, - RequiredKeyNotFound + RequiredKeyNotFound, + ContainsNonStandardKey, + MutuallyExclusiveOption }; inline std::string configParseStatusToString(const ConfigParseStatus& status) @@ -59,6 +61,8 @@ namespace micm 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"; } } @@ -330,14 +334,12 @@ namespace micm // required keys const std::string NAME = "name"; - std::array required_keys = { NAME }; - - // Check if it contains the required key(s) - for (const auto& key : required_keys) + auto status = ValidateSchema(object, { NAME, "type" }, { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } + std::string name = object[NAME].get(); // Load remaining keys as properties @@ -359,12 +361,12 @@ namespace micm ConfigParseStatus ParseMechanism(const json& object) { - std::vector required_keys = { "name", "reactions" }; - for (const auto& key : required_keys) + auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } + std::vector objects; for (const auto& element : object["reactions"]) { @@ -374,28 +376,42 @@ namespace micm return ParseObjectArray(objects); } - std::vector ParseReactants(const json& object) + std::pair> ParseReactants(const json& object) { const std::string QTY = "qty"; std::vector reactants; + + ConfigParseStatus status = ConfigParseStatus::Success; + for (auto& [key, value] : object.items()) { std::size_t qty = 1; + auto new_status = ValidateSchema(value, {}, { "qty" }); + if (new_status != ConfigParseStatus::Success) { + status = new_status; + } if (value.contains(QTY)) qty = value[QTY]; for (std::size_t i = 0; i < qty; ++i) reactants.push_back(Species(key)); } - return reactants; + return std::make_pair(status, reactants); } - std::vector> ParseProducts(const json& object) + std::pair>> ParseProducts(const json& object) { const std::string YIELD = "yield"; + + ConfigParseStatus status = ConfigParseStatus::Success; + constexpr double DEFAULT_YEILD = 1.0; std::vector> products; for (auto& [key, value] : object.items()) { + auto new_status = ValidateSchema(value, {}, { "yield" }); + if (new_status != ConfigParseStatus::Success) { + status = new_status; + } if (value.contains(YIELD)) { products.push_back(std::make_pair(Species(key), value[YIELD])); @@ -405,7 +421,7 @@ namespace micm products.push_back(std::make_pair(Species(key), DEFAULT_YEILD)); } } - return products; + return std::make_pair(status, products); } ConfigParseStatus ParsePhotolysis(const json& object) @@ -414,22 +430,32 @@ namespace micm const std::string PRODUCTS = "products"; const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + std::string name = "PHOTO." + object[MUSICA_NAME].get(); user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); std::unique_ptr rate_ptr = std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -439,16 +465,25 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + ArrheniusRateConstantParameters parameters; if (object.contains("A")) { @@ -472,6 +507,11 @@ namespace micm } if (object.contains("Ea")) { + if (parameters.C_ != 0) + { + std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl; + return ConfigParseStatus::MutuallyExclusiveOption; + } // Calculate 'C' using 'Ea' parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; } @@ -480,7 +520,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -495,17 +535,29 @@ namespace micm const std::string A0 = "a0"; const std::string N = "n"; - // Check required json objects exist - for (const auto& key : { REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }) + auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (alkoxy_products.first != ConfigParseStatus::Success) + { + return alkoxy_products.first; + } + if (nitrate_products.first != ConfigParseStatus::Success) + { + return nitrate_products.first; + } + BranchedRateConstantParameters parameters; parameters.X_ = object[X].get(); parameters.Y_ = object[Y].get(); @@ -516,13 +568,13 @@ namespace micm parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; branched_rate_arr_.push_back(BranchedRateConstant(parameters)); std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); // Nitrate branch parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; branched_rate_arr_.push_back(BranchedRateConstant(parameters)); rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -532,15 +584,23 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } TroeRateConstantParameters parameters; if (object.contains("k0_A")) @@ -580,7 +640,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -590,15 +650,23 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } TernaryChemicalActivationRateConstantParameters parameters; if (object.contains("k0_A")) @@ -639,7 +707,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -649,15 +717,22 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } TunnelingRateConstantParameters parameters; if (object.contains("A")) @@ -677,7 +752,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -686,18 +761,27 @@ namespace micm { const std::string SPECIES = "species"; const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { SPECIES, MUSICA_NAME }) + + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } std::string species = object["species"].get(); json reactants_object{}; json products_object{}; - products_object[species] = { { "YIELD", 1.0 } }; + products_object[species] = { { "yield", 1.0 } }; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(products_object); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } std::string name = "EMIS." + object[MUSICA_NAME].get(); @@ -705,7 +789,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -714,18 +798,27 @@ namespace micm { const std::string SPECIES = "species"; const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { SPECIES, MUSICA_NAME }) + + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } std::string species = object["species"].get(); json reactants_object{}; json products_object{}; - reactants_object[species] = { {} }; + reactants_object[species] = { }; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(products_object); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } std::string name = "LOSS." + object[MUSICA_NAME].get(); @@ -733,7 +826,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -744,17 +837,26 @@ namespace micm const std::string PRODUCTS = "gas-phase products"; const std::string MUSICA_NAME = "MUSICA name"; const std::string PROBABILITY = "reaction probability"; - for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } std::string species_name = object[REACTANTS].get(); json reactants_object{}; - reactants_object[species_name] = { {} }; + reactants_object[species_name] = { }; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } Species reactant_species = Species(""); for (auto& species : species_arr_) @@ -776,8 +878,69 @@ namespace micm surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + /// @brief Search for nonstandard keys. Only nonstandard keys starting with __ are allowed. Others are considered typos + /// @param object the object whose keys need to be validated + /// @param required_keys The required keys + /// @param optional_keys The optional keys + /// @return true if only standard keys are found + ConfigParseStatus ValidateSchema( + const json& object, + const std::vector& required_keys, + const std::vector& optional_keys) + { + // standard keys are: + // those in required keys + // those in optional keys + // starting with __ + // anything else is reported as an error so that typos are caught, specifically for optional keys + std::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())) + { + 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("__")) + { + return ConfigParseStatus::ContainsNonStandardKey; + } + } return ConfigParseStatus::Success; } }; diff --git a/test/unit/configure/process/CMakeLists.txt b/test/unit/configure/process/CMakeLists.txt index 78bcd48eb..ffbdb9239 100644 --- a/test/unit/configure/process/CMakeLists.txt +++ b/test/unit/configure/process/CMakeLists.txt @@ -6,10 +6,12 @@ include(test_util) ################################################################################ # Tests +create_standard_test(NAME arrhenius_config SOURCES test_arrhenius_config.cpp) create_standard_test(NAME branched_config SOURCES test_branched_config.cpp) create_standard_test(NAME emission_config SOURCES test_emission_config.cpp) create_standard_test(NAME first_order_loss_config SOURCES test_first_order_loss_config.cpp) create_standard_test(NAME photolysis_config SOURCES test_photolysis_config.cpp) create_standard_test(NAME surface_config SOURCES test_surface_config.cpp) create_standard_test(NAME ternary_chemical_activation_config SOURCES test_ternary_chemical_activation_config.cpp) +create_standard_test(NAME troe_config SOURCES test_troe_config.cpp) create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp) \ No newline at end of file diff --git a/test/unit/configure/process/test_arrhenius_config.cpp b/test/unit/configure/process/test_arrhenius_config.cpp new file mode 100644 index 000000000..37e751988 --- /dev/null +++ b/test/unit/configure/process/test_arrhenius_config.cpp @@ -0,0 +1,114 @@ +#include + +#include + +TEST(ArrheniusConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/mutually_exclusive"); + EXPECT_EQ(micm::ConfigParseStatus::MutuallyExclusiveOption, status); +} + +TEST(ArrheniusConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::ArrheniusRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.A_, 1.0); + EXPECT_EQ(params.B_, 0.0); + EXPECT_EQ(params.C_, 0.0); + EXPECT_EQ(params.D_, 300); + EXPECT_EQ(params.E_, 0.0); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::ArrheniusRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.B_, -2.3); + EXPECT_EQ(params.C_, 102.3); + EXPECT_EQ(params.D_, 63.4); + EXPECT_EQ(params.E_, -1.3); + } + + // third reaction + { + EXPECT_EQ(process_vector[2].reactants_.size(), 2); + EXPECT_EQ(process_vector[2].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[2].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[2].products_.size(), 2); + EXPECT_EQ(process_vector[2].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[2].products_[0].second, 0.5); + EXPECT_EQ(process_vector[2].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[2].products_[1].second, 1.0); + micm::ArrheniusRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[2].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.B_, -2.3); + EXPECT_EQ(params.C_, -1 * 2e23 / BOLTZMANN_CONSTANT); + EXPECT_EQ(params.D_, 63.4); + EXPECT_EQ(params.E_, -1.3); + } +} + +TEST(ArrheniusConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(ArrheniusConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(ArrheniusConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} \ No newline at end of file diff --git a/test/unit/configure/process/test_branched_config.cpp b/test/unit/configure/process/test_branched_config.cpp index 8e6286803..6b140cb8f 100644 --- a/test/unit/configure/process/test_branched_config.cpp +++ b/test/unit/configure/process/test_branched_config.cpp @@ -101,3 +101,30 @@ TEST(BranchedConfig, ParseConfig) EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Nitrate); } } + +TEST(BranchedConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(BranchedConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_alkoxy_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); + + status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_nitrate_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(BranchedConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} \ No newline at end of file diff --git a/test/unit/configure/process/test_emission_config.cpp b/test/unit/configure/process/test_emission_config.cpp index 73acb6c62..949f41f2f 100644 --- a/test/unit/configure/process/test_emission_config.cpp +++ b/test/unit/configure/process/test_emission_config.cpp @@ -47,4 +47,12 @@ TEST(EmissionConfig, ParseConfig) EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.bar"); } +} + +TEST(EmissionConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } \ No newline at end of file diff --git a/test/unit/configure/process/test_first_order_loss_config.cpp b/test/unit/configure/process/test_first_order_loss_config.cpp index 925ac1818..823697bc1 100644 --- a/test/unit/configure/process/test_first_order_loss_config.cpp +++ b/test/unit/configure/process/test_first_order_loss_config.cpp @@ -45,4 +45,12 @@ TEST(FirstOrderLossConfig, ParseConfig) EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.bar"); } +} + +TEST(FirstOrderLossConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } \ No newline at end of file diff --git a/test/unit/configure/process/test_photolysis_config.cpp b/test/unit/configure/process/test_photolysis_config.cpp index ba363e969..1e8831d9f 100644 --- a/test/unit/configure/process/test_photolysis_config.cpp +++ b/test/unit/configure/process/test_photolysis_config.cpp @@ -55,4 +55,28 @@ TEST(PhotolysisConfig, ParseConfig) EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jbar"); } +} + +TEST(PhotolysisConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(PhotolysisConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(PhotolysisConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } \ No newline at end of file diff --git a/test/unit/configure/process/test_surface_config.cpp b/test/unit/configure/process/test_surface_config.cpp index 2c14cc1db..f539e004f 100644 --- a/test/unit/configure/process/test_surface_config.cpp +++ b/test/unit/configure/process/test_surface_config.cpp @@ -64,3 +64,20 @@ TEST(SurfaceConfig, ParseConfig) EXPECT_EQ(surface_rate_constant->mean_free_speed_factor_, 8.0 * GAS_CONSTANT / (M_PI * 0.321)); } } + + +TEST(SurfaceConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(SurfaceConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} \ No newline at end of file diff --git a/test/unit/configure/process/test_ternary_chemical_activation_config.cpp b/test/unit/configure/process/test_ternary_chemical_activation_config.cpp index e1918f69b..1d46e2348 100644 --- a/test/unit/configure/process/test_ternary_chemical_activation_config.cpp +++ b/test/unit/configure/process/test_ternary_chemical_activation_config.cpp @@ -72,3 +72,27 @@ TEST(TernaryChemicalActivationConfig, ParseConfig) EXPECT_EQ(params.N_, 32.1); } } + +TEST(TernaryChemicalActivationConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TernaryChemicalActivationConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TernaryChemicalActivationConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} \ No newline at end of file diff --git a/test/unit/configure/process/test_troe_config.cpp b/test/unit/configure/process/test_troe_config.cpp new file mode 100644 index 000000000..dddcb7d89 --- /dev/null +++ b/test/unit/configure/process/test_troe_config.cpp @@ -0,0 +1,98 @@ +#include + +#include + +TEST(TroeConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/troe/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(TroeConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::TroeRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.k0_A_, 1.0); + EXPECT_EQ(params.k0_B_, 0.0); + EXPECT_EQ(params.k0_C_, 0.0); + EXPECT_EQ(params.kinf_A_, 1.0); + EXPECT_EQ(params.kinf_B_, 0.0); + EXPECT_EQ(params.kinf_C_, 0.0); + EXPECT_EQ(params.Fc_, 0.6); + EXPECT_EQ(params.N_, 1.0); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::TroeRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.k0_A_, 32.1); + EXPECT_EQ(params.k0_B_, -2.3); + EXPECT_EQ(params.k0_C_, 102.3); + EXPECT_EQ(params.kinf_A_, 63.4); + EXPECT_EQ(params.kinf_B_, -1.3); + EXPECT_EQ(params.kinf_C_, 908.5); + EXPECT_EQ(params.Fc_, 1.3); + EXPECT_EQ(params.N_, 32.1); + } +} + +TEST(TroeConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TroeConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TroeConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} \ No newline at end of file diff --git a/test/unit/configure/process/test_tunneling_config.cpp b/test/unit/configure/process/test_tunneling_config.cpp index 5ebd3ddab..ca77fdb26 100644 --- a/test/unit/configure/process/test_tunneling_config.cpp +++ b/test/unit/configure/process/test_tunneling_config.cpp @@ -61,3 +61,27 @@ TEST(TunnelingConfig, ParseConfig) EXPECT_EQ(params.C_, 102.3); } } + +TEST(TunnelingConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TunnelingConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TunnelingConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} \ No newline at end of file diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index c9e5b7e4d..55c7fe82c 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -154,130 +154,3 @@ TEST(SolverConfig, GettingSolverParamsThrowsExceptionWithFailedParsing) EXPECT_NE(micm::ConfigParseStatus::Success, status); EXPECT_ANY_THROW(solverConfig.GetSolverParams()); } - -// -// Tests for MZ326 configure files -// -TEST(SolverConfig, ReadAndParseSystemObjectfromMZ326) -{ - micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - // Check 'gas_phase' in 'System' - EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 6); - - // Check 'phases' in 'System' - EXPECT_EQ(solver_params.system_.phases_.size(), 0); - - // Check 'name' and molecular_weight from 'properties' in 'Species' - std::vector> species_name_and_molecular_weight = { - std::make_pair("ALKNIT", 0.133141), std::make_pair("BZOOH", 0.124135), std::make_pair("C6H5OOH", 0.110109), - std::make_pair("COF2", 0.0), std::make_pair("O2", 0.0), std::make_pair("FUR2O2", 0.0) - }; - - short idx = 0; - for (const auto& s : solver_params.system_.gas_phase_.species_) - { - EXPECT_EQ(s.name_, species_name_and_molecular_weight[idx].first); - EXPECT_EQ(s.properties_.at("molecular weight [kg mol-1]"), species_name_and_molecular_weight[idx].second); - idx++; - } -} - -TEST(SolverConfig, ReadAndParseProcessObjectsfromMZ326) -{ - micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/MZ326"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // Check the number of 'Process' created - EXPECT_EQ(process_vector.size(), 5); - - // Check the number of 'reactants' and 'products' in each 'Process' - // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' - int num_reactants_in_each_process[] = { 2, 2, 2, 1, 1 }; - int num_products_in_each_process[] = { 5, 5, 2, 3, 2 }; - int num_phase_in_each_process = 6; - - short idx = 0; - for (const auto& p : process_vector) - { - EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); - EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); - EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); - idx++; - } - - // Check the parameters for 'TroeRateConstant' - micm::TroeRateConstant* troe_rate_const = nullptr; - double k0_A_param[] = { 5.5e-30, 1.07767 }; - double k0_B_param[] = { 0.0, -5.6 }; - double k0_C_param[] = { 0.0, -14000 }; - double kinf_A_param[] = { 8.3e-13, 1.03323e+17 }; - double kinf_B_param[] = { 0.0, 0.0 }; - double kinf_C_param[] = { 0.0, -14000 }; - double Fc_param[] = { 0.6, 0.6 }; - double N_param[] = { -2, 1.5 }; - - idx = 0; - for (short i : { 0, 4 }) - { - troe_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(troe_rate_const != nullptr); - EXPECT_EQ(troe_rate_const->parameters_.k0_A_, k0_A_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.k0_B_, k0_B_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.k0_C_, k0_C_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.kinf_A_, kinf_A_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.kinf_B_, kinf_B_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.kinf_C_, kinf_C_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.Fc_, Fc_param[idx]); - EXPECT_EQ(troe_rate_const->parameters_.N_, N_param[idx]); - - idx++; - } - - // Check the name for 'UserDefinedRateConstant' - micm::UserDefinedRateConstant* photolysis_rate_const = nullptr; - std::string photolysis_name = "PHOTO.jterpnit"; - - for (short i : { 3 }) - { - photolysis_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(photolysis_rate_const != nullptr); - EXPECT_EQ(photolysis_rate_const->CustomParameters()[0], photolysis_name); - } - - // Check the parameters for 'ArrheniusRateConstant' - micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; - double A_param[] = { 2.0e-12, 3.8e-12 }; - double B_param[] = { 0.0, 0.0 }; - double C_param[] = { -1 * -6.90325e-21 / 1.3806505e-23, -1 * -2.7613e-21 / 1.3806505e-23 }; - double D_param[] = { 300.0, 300.0 }; - double E_param[] = { 0.0, 0.0 }; - - idx = 0; - for (short i : { 1, 2 }) - { - arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(arrhenius_rate_const != nullptr); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.A_, A_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.B_, B_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.C_, C_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.D_, D_param[idx]); - EXPECT_DOUBLE_EQ(arrhenius_rate_const->parameters_.E_, E_param[idx]); - - idx++; - } -} diff --git a/test/unit/unit_configs/MZ326/reactions.json b/test/unit/unit_configs/MZ326/reactions.json deleted file mode 100644 index 1903b5e94..000000000 --- a/test/unit/unit_configs/MZ326/reactions.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "comments": [ "This mechanism may contain refactored reactions based on custom", - "...." - ], - "camp-data": [ - { - "name": "MZ326_TS1.3_20230106", - "type": "MECHANISM", - "reactions": [ - { - "type": "TROE", - "k0_A": 5.5e-30, - "kinf_A": 8.3e-13, - "N": -2, - "reactants": { - "C2H2": { }, - "OH": { } - }, - "products": { - "GLYOXAL": { "yield": 0.65 }, - "OH": { "yield": 0.65 }, - "HCOOH": { "yield": 0.35 }, - "HO2": { "yield": 0.35 }, - "CO": { "yield": 0.35 } - } - }, - { - "type": "ARRHENIUS", - "A": 2.0e-12, - "Ea": -6.90325e-21, - "reactants": { - "CH3O2": { }, - "TERPO2": { } - }, - "products": { - "TERPROD1": { }, - "CH2O": { "yield": 0.95 }, - "CH3OH": { "yield": 0.25 }, - "HO2": { }, - "CH3COCH3": { "yield": 0.025 } - } - }, - { - "type": "ARRHENIUS", - "A": 3.8e-12, - "Ea": -2.7613e-21, - "reactants": { - "C3H7OOH": { }, - "OH": { } - }, - "products": { - "H2O": { }, - "C3H7O2": { } - } - }, - { - "type": "PHOTOLYSIS", - "MUSICA name": "jterpnit", - "reactants": { - "TERPNIT": { } - }, - "products": { - "TERPROD1": { }, - "NO2": { }, - "HO2": { } - } - }, - { - "type": "TROE", - "k0_A": 1.07767, - "k0_B": -5.6, - "k0_C": -14000, - "kinf_A": 1.03323e+17, - "kinf_C": -14000, - "N": 1.5, - "reactants": { - "PAN": { } - }, - "products": { - "CH3CO3": { }, - "NO2": { } - } - } - ] - }] -} - diff --git a/test/unit/unit_configs/MZ326/species.json b/test/unit/unit_configs/MZ326/species.json deleted file mode 100644 index 42572e3db..000000000 --- a/test/unit/unit_configs/MZ326/species.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "camp-data": [ - { - "name": "ALKNIT", - "type": "CHEM_SPEC", - "description": "standard alkyl nitrate from BIGALK+OH chemistry", - "molecular weight [kg mol-1]": 0.133141 - }, - { - "name": "BZOOH", - "type": "CHEM_SPEC", - "description": "hydroperoxide from toluene oxidation", - "molecular weight [kg mol-1]": 0.124135 - }, - { - "name": "C6H5OOH", - "type": "CHEM_SPEC", - "description": "phenyl hydroperoxide", - "molecular weight [kg mol-1]": 0.110109 - }, - { - "name": "COF2", - "type": "CHEM_SPEC", - "description": "", - "molecular weight [kg mol-1]": 0.0 - }, - { - "name": "O2", - "type": "CHEM_SPEC", - "description": "", - "molecular weight [kg mol-1]": 0.0 - }, - { - "name": "FUR2O2", - "type": "CHEM_SPEC", - "description": "peroxy radical from furanone", - "molecular weight [kg mol-1]": 0.0 - } - ] -} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..b2dc8ab3a --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/reactions.json @@ -0,0 +1,24 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "ARRHENIUS", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "a": 32.1, + "b": -2.3, + "c": 102.3 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/MZ326/tolerance.json b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/tolerance.json similarity index 100% rename from test/unit/unit_configs/MZ326/tolerance.json rename to test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/tolerance.json diff --git a/test/unit/unit_configs/process/arrhenius/missing_products/reactions.json b/test/unit/unit_configs/process/arrhenius/missing_products/reactions.json new file mode 100644 index 000000000..a9fa047ce --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_products/reactions.json @@ -0,0 +1,17 @@ +{ + "camp-data": [ + { + "name": "missing products", + "type": "MECHANISM", + "reactions": [ + { + "type": "ARRHENIUS", + "reactants": { + "bar": { }, + "baz": { "qty": 2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/missing_products/species.json b/test/unit/unit_configs/process/arrhenius/missing_products/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_products/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/missing_products/tolerance.json b/test/unit/unit_configs/process/arrhenius/missing_products/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_products/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/arrhenius/missing_reactants/reactions.json b/test/unit/unit_configs/process/arrhenius/missing_reactants/reactions.json new file mode 100644 index 000000000..cabdecc24 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_reactants/reactions.json @@ -0,0 +1,17 @@ +{ + "camp-data": [ + { + "name": "missing reactants", + "type": "MECHANISM", + "reactions": [ + { + "type": "ARRHENIUS", + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/missing_reactants/species.json b/test/unit/unit_configs/process/arrhenius/missing_reactants/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_reactants/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/missing_reactants/tolerance.json b/test/unit/unit_configs/process/arrhenius/missing_reactants/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_reactants/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/arrhenius/mutually_exclusive/reactions.json b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/reactions.json new file mode 100644 index 000000000..a4ff750f1 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/reactions.json @@ -0,0 +1,27 @@ +{ + "camp-data": [ + { + "name": "Mutually exclusive arrhenius reaction config", + "type": "MECHANISM", + "reactions": [ + { + "type": "ARRHENIUS", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "A": 32.1, + "B": -2.3, + "C": 102.3, + "D": 63.4, + "E": -1.3, + "Ea": 102.3 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/mutually_exclusive/species.json b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/mutually_exclusive/tolerance.json b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..c112f766c --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/reactions.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "invaild yield name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "ARRHENIUS", + "reactants": { + "foo" : { } + }, + "products": { + "bar": { "Yield": 1.0 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..1c6306e53 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "invalid quantity name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "ARRHENIUS", + "reactants": { + "foo" : { "Qty": 2.0 } + }, + "products": { + "bar": {} + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/arrhenius/valid/reactions.json b/test/unit/unit_configs/process/arrhenius/valid/reactions.json new file mode 100644 index 000000000..36427b9ad --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/valid/reactions.json @@ -0,0 +1,54 @@ +{ + "camp-data": [ + { + "name": "Valid ternary chemical activation reactions", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "ARRHENIUS", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + }, + { + "type": "ARRHENIUS", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "A": 32.1, + "B": -2.3, + "C": 102.3, + "D": 63.4, + "E": -1.3 + }, + { + "type": "ARRHENIUS", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "A": 32.1, + "B": -2.3, + "Ea": 2e23, + "D": 63.4, + "E": -1.3 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/valid/species.json b/test/unit/unit_configs/process/arrhenius/valid/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/valid/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/arrhenius/valid/tolerance.json b/test/unit/unit_configs/process/arrhenius/valid/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/valid/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/branched/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/branched/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..e6acc9101 --- /dev/null +++ b/test/unit/unit_configs/process/branched/contains_nonstandard_key/reactions.json @@ -0,0 +1,52 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "BRANCHED", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "alkoxy products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + }, + "Alkoxy products": { + "baz": { } + }, + "nitrate products": { + "quz": { "yield": 1.0 } + }, + "Y" : 42.3, + "X" : 12.3, + "a0" : 1.0e-5, + "n" : 3 + }, + { + "type": "WENNBERG_NO_RO2", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "alkoxy products": { + "baz": { } + }, + "Alkoxy products": { + "baz": { } + }, + "nitrate products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "Y" : 2.3e8, + "X" : 0.32, + "a0" : 0.423, + "n" : 6 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/branched/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/branched/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/branched/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/branched/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/branched/missing_reactants/reactions.json b/test/unit/unit_configs/process/branched/missing_reactants/reactions.json index 13d0cb5fc..1d66529f7 100644 --- a/test/unit/unit_configs/process/branched/missing_reactants/reactions.json +++ b/test/unit/unit_configs/process/branched/missing_reactants/reactions.json @@ -1,16 +1,16 @@ { "camp-data": [ { - "name": "Branched missing reactants", + "name": "missing reacants", "type": "MECHANISM", "reactions": [ { "type": "WENNBERG_NO_RO2", "alkoxy products": { - "foo": { } + "foo": { "yield" : 2.3 } }, "nitrate products": { - "baz": { "yield" : 2.3 } + "baz": {} } } ] diff --git a/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/reactions.json b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/reactions.json new file mode 100644 index 000000000..7b2fe41d2 --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/reactions.json @@ -0,0 +1,27 @@ +{ + "camp-data": [ + { + "name": "capital Y", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "BRANCHED", + "reactants": { + "foo" : { } + }, + "alkoxy products": { + "bar": { "Yield": 1.0 } + }, + "nitrate products": { + "quz": { "yield": 1.0 } + }, + "Y" : 42.3, + "X" : 12.3, + "a0" : 1.0e-5, + "n" : 3 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/species.json b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/tolerance.json b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/reactions.json b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/reactions.json new file mode 100644 index 000000000..9068be3ba --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/reactions.json @@ -0,0 +1,27 @@ +{ + "camp-data": [ + { + "name": "capital Y", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "BRANCHED", + "reactants": { + "foo" : { } + }, + "alkoxy products": { + "bar": {} + }, + "nitrate products": { + "quz": { "Yield": 1.0 } + }, + "Y" : 42.3, + "X" : 12.3, + "a0" : 1.0e-5, + "n" : 3 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/species.json b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/tolerance.json b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..daf53fd00 --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,27 @@ +{ + "camp-data": [ + { + "name": "captial Q", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "BRANCHED", + "reactants": { + "foo" : { "Qty": 2.0 } + }, + "alkoxy products": { + "bar": {} + }, + "nitrate products": { + "quz": { "yield": 1.0 } + }, + "Y" : 42.3, + "X" : 12.3, + "a0" : 1.0e-5, + "n" : 3 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/branched/valid/reactions.json b/test/unit/unit_configs/process/branched/valid/reactions.json index d5b968113..630c4fed6 100644 --- a/test/unit/unit_configs/process/branched/valid/reactions.json +++ b/test/unit/unit_configs/process/branched/valid/reactions.json @@ -5,6 +5,7 @@ "type": "MECHANISM", "reactions": [ { + "__my comment": {}, "type": "BRANCHED", "reactants": { "foo" : { }, diff --git a/test/unit/unit_configs/process/emission/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/emission/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..f034307ed --- /dev/null +++ b/test/unit/unit_configs/process/emission/contains_nonstandard_key/reactions.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "nonstandard emission", + "type": "MECHANISM", + "reactions": [ + { + "type": "EMISSION", + "species": "foo", + "MUSICA name": "foo", + "mUSICA name": "foo" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/emission/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/emission/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/emission/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/emission/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/emission/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/emission/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/emission/valid/reactions.json b/test/unit/unit_configs/process/emission/valid/reactions.json index e212d34f8..6320878e5 100644 --- a/test/unit/unit_configs/process/emission/valid/reactions.json +++ b/test/unit/unit_configs/process/emission/valid/reactions.json @@ -5,6 +5,7 @@ "type": "MECHANISM", "reactions": [ { + "__my comment": {}, "type": "EMISSION", "species": "foo", "MUSICA name": "foo" diff --git a/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..d99e1adc5 --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/reactions.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "nonstandard keys", + "type": "MECHANISM", + "reactions": [ + { + "type": "FIRST_ORDER_LOSS", + "species": "foo", + "MUSICA name": "foo", + "mUSICA name": "foo" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/first_order_loss/valid/reactions.json b/test/unit/unit_configs/process/first_order_loss/valid/reactions.json index 067a30a65..7d5cb807d 100644 --- a/test/unit/unit_configs/process/first_order_loss/valid/reactions.json +++ b/test/unit/unit_configs/process/first_order_loss/valid/reactions.json @@ -5,6 +5,7 @@ "type": "MECHANISM", "reactions": [ { + "__my comment": {}, "type": "FIRST_ORDER_LOSS", "species": "foo", "MUSICA name": "foo" diff --git a/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..bbe34676a --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/reactions.json @@ -0,0 +1,33 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "foo" : { } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + }, + "MUSICA name": "jfoo" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "bar" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "MUSICA name": "jbar", + "mUSICA name": "jbar" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..8a2a72f17 --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/reactions.json @@ -0,0 +1,21 @@ +{ + "camp-data": [ + { + "name": "capital Y", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "PHOTOLYSIS", + "reactants": { + "foo" : { } + }, + "products": { + "bar": { "Yield": 1.0 } + }, + "MUSICA name": "jfoo" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..2e38a7d6d --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,21 @@ +{ + "camp-data": [ + { + "name": "captial Q", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "PHOTOLYSIS", + "reactants": { + "foo" : { "Qty": 2.0 } + }, + "products": { + "bar": {} + }, + "MUSICA name": "jfoo" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/surface/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/surface/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..3e9640450 --- /dev/null +++ b/test/unit/unit_configs/process/surface/contains_nonstandard_key/reactions.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "SURFACE", + "gas-phase reactant": "foo", + "gas-phase products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + }, + "MUSICA name": "kfoo", + "Reaction probability": 0.5 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/surface/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/surface/contains_nonstandard_key/species.json new file mode 100644 index 000000000..50a902e10 --- /dev/null +++ b/test/unit/unit_configs/process/surface/contains_nonstandard_key/species.json @@ -0,0 +1,24 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC", + "molecular weight [kg mol-1]" : 0.123, + "diffusion coefficient [m2 s-1]" : 2.3e-4 + }, + { + "name": "bar", + "type": "CHEM_SPEC", + "molecular weight [kg mol-1]" : 0.321, + "diffusion coefficient [m2 s-1]" : 0.4e-5 + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/surface/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/surface/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/surface/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/surface/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/surface/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..8181819b1 --- /dev/null +++ b/test/unit/unit_configs/process/surface/nonstandard_product_coef/reactions.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "Invalid yield name", + "type": "MECHANISM", + "reactions": [ + { + "type": "SURFACE", + "gas-phase reactant": "bar", + "gas-phase products": { + "bar": { "Yield": 0.5 }, + "foo": { } + }, + "reaction probability": 0.5, + "MUSICA name": "kbar" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/surface/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/surface/nonstandard_product_coef/species.json new file mode 100644 index 000000000..50a902e10 --- /dev/null +++ b/test/unit/unit_configs/process/surface/nonstandard_product_coef/species.json @@ -0,0 +1,24 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC", + "molecular weight [kg mol-1]" : 0.123, + "diffusion coefficient [m2 s-1]" : 2.3e-4 + }, + { + "name": "bar", + "type": "CHEM_SPEC", + "molecular weight [kg mol-1]" : 0.321, + "diffusion coefficient [m2 s-1]" : 0.4e-5 + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/surface/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/surface/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/surface/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/surface/valid/reactions.json b/test/unit/unit_configs/process/surface/valid/reactions.json index a86b68473..bb0348806 100644 --- a/test/unit/unit_configs/process/surface/valid/reactions.json +++ b/test/unit/unit_configs/process/surface/valid/reactions.json @@ -5,6 +5,7 @@ "type": "MECHANISM", "reactions": [ { + "__my comment": "asdf", "type": "SURFACE", "gas-phase reactant": "foo", "gas-phase products": { diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..b8a849484 --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/reactions.json @@ -0,0 +1,40 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + }, + { + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "K0_A": 32.1, + "K0_B": -2.3, + "K0_C": 102.3, + "Kinf_A": 63.4, + "Kinf_B": -1.3, + "Kinf_C": 908.5, + "fc": 1.3, + "n": 32.1 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..19087ad3b --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "Invalid yield name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "Yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..a91f41db4 --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "Invalid quantity name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TERNARY_CHEMICAL_ACTIVATION", + "reactants": { + "foo" : { }, + "quz" : { "Qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/valid/reactions.json b/test/unit/unit_configs/process/ternary_chemical_activation/valid/reactions.json index cf6baab08..b8eea9f91 100644 --- a/test/unit/unit_configs/process/ternary_chemical_activation/valid/reactions.json +++ b/test/unit/unit_configs/process/ternary_chemical_activation/valid/reactions.json @@ -5,6 +5,7 @@ "type": "MECHANISM", "reactions": [ { + "__my comment": {}, "type": "TERNARY_CHEMICAL_ACTIVATION", "reactants": { "foo" : { }, diff --git a/test/unit/unit_configs/process/troe/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/troe/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..274b53c4a --- /dev/null +++ b/test/unit/unit_configs/process/troe/contains_nonstandard_key/reactions.json @@ -0,0 +1,40 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "TROE", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + }, + { + "type": "TROE", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "K0_A": 32.1, + "K0_B": -2.3, + "K0_C": 102.3, + "Kinf_A": 63.4, + "Kinf_B": -1.3, + "Kinf_C": 908.5, + "fc": 1.3, + "n": 32.1 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/troe/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/troe/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/troe/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/troe/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/troe/missing_products/reactions.json b/test/unit/unit_configs/process/troe/missing_products/reactions.json new file mode 100644 index 000000000..a3271a043 --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_products/reactions.json @@ -0,0 +1,17 @@ +{ + "camp-data": [ + { + "name": "missing products", + "type": "MECHANISM", + "reactions": [ + { + "type": "TROE", + "reactants": { + "bar": { }, + "baz": { "qty": 2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/missing_products/species.json b/test/unit/unit_configs/process/troe/missing_products/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_products/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/missing_products/tolerance.json b/test/unit/unit_configs/process/troe/missing_products/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_products/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/troe/missing_reactants/reactions.json b/test/unit/unit_configs/process/troe/missing_reactants/reactions.json new file mode 100644 index 000000000..025c19927 --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_reactants/reactions.json @@ -0,0 +1,17 @@ +{ + "camp-data": [ + { + "name": "missing reactants", + "type": "MECHANISM", + "reactions": [ + { + "type": "TROE", + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/missing_reactants/species.json b/test/unit/unit_configs/process/troe/missing_reactants/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_reactants/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/missing_reactants/tolerance.json b/test/unit/unit_configs/process/troe/missing_reactants/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_reactants/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/troe/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/troe/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..4db3245bb --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_product_coef/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "Product with invalid yield name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TROE", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "Yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/troe/nonstandard_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/troe/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..67c25c79c --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "Reactant with invalid quantity name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TROE", + "reactants": { + "foo" : { }, + "quz" : { "Qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/troe/valid/reactions.json b/test/unit/unit_configs/process/troe/valid/reactions.json new file mode 100644 index 000000000..73ca1a80a --- /dev/null +++ b/test/unit/unit_configs/process/troe/valid/reactions.json @@ -0,0 +1,41 @@ +{ + "camp-data": [ + { + "name": "Valid troe chemical activation reactions", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TROE", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + }, + { + "type": "TROE", + "reactants": { + "bar" : { }, + "baz" : { } + }, + "products": { + "bar": { "yield": 0.5 }, + "foo": { } + }, + "k0_A": 32.1, + "k0_B": -2.3, + "k0_C": 102.3, + "kinf_A": 63.4, + "kinf_B": -1.3, + "kinf_C": 908.5, + "Fc": 1.3, + "N": 32.1 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/valid/species.json b/test/unit/unit_configs/process/troe/valid/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/troe/valid/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/troe/valid/tolerance.json b/test/unit/unit_configs/process/troe/valid/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/troe/valid/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..066d1a7a8 --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "nonstandard key", + "type": "MECHANISM", + "reactions": [ + { + "type": "TUNNELING", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + }, + "a": 0 + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..7840c4bf9 --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "Invalid yield name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TUNNELING", + "reactants": { + "foo" : { }, + "quz" : { "qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "Yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..7c6a7d605 --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,22 @@ +{ + "camp-data": [ + { + "name": "Invalid yield name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "TUNNELING", + "reactants": { + "foo" : { }, + "quz" : { "Qty" :2 } + }, + "products": { + "bar": { "yield": 1.0 }, + "baz": { "yield": 3.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/tunneling/valid/reactions.json b/test/unit/unit_configs/process/tunneling/valid/reactions.json index 970e84cae..ee0507762 100644 --- a/test/unit/unit_configs/process/tunneling/valid/reactions.json +++ b/test/unit/unit_configs/process/tunneling/valid/reactions.json @@ -5,6 +5,7 @@ "type": "MECHANISM", "reactions": [ { + "__my comment": {}, "type": "TUNNELING", "reactants": { "foo" : { }, From 292f9a1a657a05c3c2361f82e4277544b5a88252 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 25 Oct 2023 15:23:03 -0700 Subject: [PATCH 172/318] Auto-format code changes (#328) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/configure/solver_config.hpp | 20 ++++++++++++------- .../process/test_arrhenius_config.cpp | 3 +-- .../process/test_branched_config.cpp | 3 ++- .../process/test_first_order_loss_config.cpp | 3 ++- .../configure/process/test_surface_config.cpp | 1 - ...est_ternary_chemical_activation_config.cpp | 9 ++++++--- .../configure/process/test_troe_config.cpp | 3 +-- 7 files changed, 25 insertions(+), 17 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index b4df2a88c..15f547eca 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -334,7 +334,10 @@ namespace micm // required keys const std::string NAME = "name"; - auto status = ValidateSchema(object, { NAME, "type" }, { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); + auto status = ValidateSchema( + object, + { NAME, "type" }, + { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); if (status != ConfigParseStatus::Success) { return status; @@ -387,7 +390,8 @@ namespace micm { std::size_t qty = 1; auto new_status = ValidateSchema(value, {}, { "qty" }); - if (new_status != ConfigParseStatus::Success) { + if (new_status != ConfigParseStatus::Success) + { status = new_status; } if (value.contains(QTY)) @@ -401,7 +405,7 @@ namespace micm std::pair>> ParseProducts(const json& object) { const std::string YIELD = "yield"; - + ConfigParseStatus status = ConfigParseStatus::Success; constexpr double DEFAULT_YEILD = 1.0; @@ -409,7 +413,8 @@ namespace micm for (auto& [key, value] : object.items()) { auto new_status = ValidateSchema(value, {}, { "yield" }); - if (new_status != ConfigParseStatus::Success) { + if (new_status != ConfigParseStatus::Success) + { status = new_status; } if (value.contains(YIELD)) @@ -465,7 +470,8 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); + auto status = + ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); if (status != ConfigParseStatus::Success) { return status; @@ -808,7 +814,7 @@ namespace micm std::string species = object["species"].get(); json reactants_object{}; json products_object{}; - reactants_object[species] = { }; + reactants_object[species] = {}; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(products_object); if (reactants.first != ConfigParseStatus::Success) @@ -846,7 +852,7 @@ namespace micm std::string species_name = object[REACTANTS].get(); json reactants_object{}; - reactants_object[species_name] = { }; + reactants_object[species_name] = {}; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(object[PRODUCTS]); if (reactants.first != ConfigParseStatus::Success) diff --git a/test/unit/configure/process/test_arrhenius_config.cpp b/test/unit/configure/process/test_arrhenius_config.cpp index 37e751988..d2ab313eb 100644 --- a/test/unit/configure/process/test_arrhenius_config.cpp +++ b/test/unit/configure/process/test_arrhenius_config.cpp @@ -7,8 +7,7 @@ TEST(ArrheniusConfig, DetectsInvalidConfig) micm::SolverConfig solver_config; // Read and parse the configure files - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_reactants"); + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_reactants"); EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_products"); EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); diff --git a/test/unit/configure/process/test_branched_config.cpp b/test/unit/configure/process/test_branched_config.cpp index 6b140cb8f..0e6618167 100644 --- a/test/unit/configure/process/test_branched_config.cpp +++ b/test/unit/configure/process/test_branched_config.cpp @@ -114,7 +114,8 @@ TEST(BranchedConfig, DetectsNonstandardProductCoefficient) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_alkoxy_product_coef"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_alkoxy_product_coef"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_nitrate_product_coef"); diff --git a/test/unit/configure/process/test_first_order_loss_config.cpp b/test/unit/configure/process/test_first_order_loss_config.cpp index 823697bc1..0046ea7a0 100644 --- a/test/unit/configure/process/test_first_order_loss_config.cpp +++ b/test/unit/configure/process/test_first_order_loss_config.cpp @@ -51,6 +51,7 @@ TEST(FirstOrderLossConfig, DetectsNonstandardKeys) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/contains_nonstandard_key"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/first_order_loss/contains_nonstandard_key"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } \ No newline at end of file diff --git a/test/unit/configure/process/test_surface_config.cpp b/test/unit/configure/process/test_surface_config.cpp index f539e004f..e77c4cefa 100644 --- a/test/unit/configure/process/test_surface_config.cpp +++ b/test/unit/configure/process/test_surface_config.cpp @@ -65,7 +65,6 @@ TEST(SurfaceConfig, ParseConfig) } } - TEST(SurfaceConfig, DetectsNonstandardKeys) { micm::SolverConfig solver_config; diff --git a/test/unit/configure/process/test_ternary_chemical_activation_config.cpp b/test/unit/configure/process/test_ternary_chemical_activation_config.cpp index 1d46e2348..d4a416a16 100644 --- a/test/unit/configure/process/test_ternary_chemical_activation_config.cpp +++ b/test/unit/configure/process/test_ternary_chemical_activation_config.cpp @@ -77,7 +77,8 @@ TEST(TernaryChemicalActivationConfig, DetectsNonstandardKeys) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/contains_nonstandard_key"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/contains_nonstandard_key"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } @@ -85,7 +86,8 @@ TEST(TernaryChemicalActivationConfig, DetectsNonstandardProductCoefficient) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_product_coef"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_product_coef"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } @@ -93,6 +95,7 @@ TEST(TernaryChemicalActivationConfig, DetectsNonstandardReactantCoefficient) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } \ No newline at end of file diff --git a/test/unit/configure/process/test_troe_config.cpp b/test/unit/configure/process/test_troe_config.cpp index dddcb7d89..f7631359e 100644 --- a/test/unit/configure/process/test_troe_config.cpp +++ b/test/unit/configure/process/test_troe_config.cpp @@ -7,8 +7,7 @@ TEST(TroeConfig, DetectsInvalidConfig) micm::SolverConfig solver_config; // Read and parse the configure files - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/troe/missing_reactants"); + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_reactants"); EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_products"); EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); From 219f87c8f4c740f8bf3796a200403c600b769baf Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 25 Oct 2023 18:05:28 -0600 Subject: [PATCH 173/318] Start with CAMP JSON. --- test/surface_rxn/configs/test_surface.json | 80 ++++++++++++++++++++++ test/surface_rxn/test_surface_rxn.cpp | 7 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 test/surface_rxn/configs/test_surface.json diff --git a/test/surface_rxn/configs/test_surface.json b/test/surface_rxn/configs/test_surface.json new file mode 100644 index 000000000..6610d4f07 --- /dev/null +++ b/test/surface_rxn/configs/test_surface.json @@ -0,0 +1,80 @@ +{ + "camp-data" : [ + { + "type" : "RELATIVE_TOLERANCE", + "value" : 1.0e-15 + }, + { + "name" : "foo", + "type" : "CHEM_SPEC", + "diffusion coeff [m2 s-1]" : 0.95E-05, + "molecular weight [kg mol-1]" : 0.04607, + "absolute tolerance" : 1.0e-20 + }, + { + "name" : "bar", + "type" : "CHEM_SPEC" + }, + { + "name" : "baz", + "type" : "CHEM_SPEC" + }, + { + "name" : "aerosol stuff", + "type" : "CHEM_SPEC", + "phase" : "AEROSOL", + "molecular weight [kg mol-1]" : 0.5, + "density [kg m-3]" : 1000.0, + "absolute tolerance" : 1.0e-20 + }, + { + "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 + }, + { + "name" : "surface reacting phase", + "type" : "AERO_PHASE", + "species" : ["aerosol stuff", "more aerosol stuff"] + }, + { + "name" : "not surface reacting phase", + "type" : "AERO_PHASE", + "species": ["aerosol stuff"] + }, + { + "type" : "AERO_REP_SINGLE_PARTICLE", + "name" : "my aero rep 1", + "maximum computational particles" : 1 + }, + { + "type" : "AERO_REP_SINGLE_PARTICLE", + "name" : "my aero rep 2", + "maximum computational particles" : 1 + }, + { + "type" : "AERO_REP_SINGLE_PARTICLE", + "name" : "my aero rep 3", + "maximum computational particles" : 1 + }, + { + "name" : "surface mechanism", + "type" : "MECHANISM", + "reactions" : [ + { + "type" : "SURFACE", + "gas-phase reactant" : "foo", + "reaction probability" : 2.0e-2, + "gas-phase products" : { + "bar" : { }, + "baz" : { "yield" : 0.4 } + }, + "aerosol phase" : "surface reacting phase" + } + ] + } + ] +} diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index bc82ccf17..99200294a 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -1,11 +1,16 @@ #include -#include +#include +#include #include +#include +#include +// based on CAMP/test/unit_rxn_data/test_rxn_surface.F90 int main(const int argc, const char* argv[]) { micm::SolverConfig solver_config; + return 0; } From dd08b82e15b79d8142502d5688527e4a4307014b Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 10:40:03 -0600 Subject: [PATCH 174/318] Added CAMP config.json with camp-files list. --- test/unit/unit_configs/robertson/config.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/unit/unit_configs/robertson/config.json diff --git a/test/unit/unit_configs/robertson/config.json b/test/unit/unit_configs/robertson/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/unit/unit_configs/robertson/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} From b8c501ff47822b6fd44ed951a9d459e45bba468f Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 10:49:56 -0600 Subject: [PATCH 175/318] Allow for 2 or more config files in camp-list. --- include/micm/configure/solver_config.hpp | 4 ++-- test/unit/unit_configs/CAMP/camp_invalid/config.json | 2 +- .../process/arrhenius/contains_nonstandard_key/config.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 15f547eca..4bb06d3f3 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -148,9 +148,9 @@ namespace micm { camp_files.push_back(element.get()); } - if (camp_files.size() != 2) + if (camp_files.size() < 2) { - std::string err_msg = "CAMP file list should contain two files [species.json, mechanism.json]"; + std::string err_msg = "CAMP file list should contain at least two files [species.json, mechanism.json]"; std::cerr << err_msg << std::endl; return ConfigParseStatus::InvalidCAMPFileCount; } diff --git a/test/unit/unit_configs/CAMP/camp_invalid/config.json b/test/unit/unit_configs/CAMP/camp_invalid/config.json index d8250971c..201b3ff4d 100644 --- a/test/unit/unit_configs/CAMP/camp_invalid/config.json +++ b/test/unit/unit_configs/CAMP/camp_invalid/config.json @@ -1 +1 @@ -{"camp-files": ["camp_species.json", "camp_mechanism.json", "other.json"]} +{"camp-files": ["other.json"]} diff --git a/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/config.json index 61165b00d..38307636f 100644 --- a/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/config.json +++ b/test/unit/unit_configs/process/arrhenius/contains_nonstandard_key/config.json @@ -1 +1 @@ -{"camp-files": ["species.json", "reactions.json"]} +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} From 68535a2f1c3bdfa7e1ee15afca1e91fbb30d8219 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 11:04:32 -0600 Subject: [PATCH 176/318] Changed camp_files to be a vector of filesystem::path. --- include/micm/configure/camp_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 23b5c985f..77da4adee 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -128,7 +128,7 @@ namespace micm std::filesystem::path config_dir = config_file.parent_path(); // The CAMP file list - std::vector camp_files; + std::vector camp_files; // Load the CAMP file list JSON json camp_data = json::parse(std::ifstream(config_file)); From 2cf772c901dffabe16f5ef1bbb783b4864440182 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 12:05:00 -0600 Subject: [PATCH 177/318] Added config.json to arrhenius subdirs. --- .../unit_configs/process/arrhenius/missing_products/config.json | 1 + .../unit_configs/process/arrhenius/missing_reactants/config.json | 1 + .../process/arrhenius/mutually_exclusive/config.json | 1 + .../process/arrhenius/nonstandard_product_coef/config.json | 1 + .../process/arrhenius/nonstandard_reactant_coef/config.json | 1 + test/unit/unit_configs/process/arrhenius/valid/config.json | 1 + 6 files changed, 6 insertions(+) create mode 100644 test/unit/unit_configs/process/arrhenius/missing_products/config.json create mode 100644 test/unit/unit_configs/process/arrhenius/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/arrhenius/mutually_exclusive/config.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/arrhenius/valid/config.json diff --git a/test/unit/unit_configs/process/arrhenius/missing_products/config.json b/test/unit/unit_configs/process/arrhenius/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/arrhenius/missing_reactants/config.json b/test/unit/unit_configs/process/arrhenius/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/arrhenius/mutually_exclusive/config.json b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/mutually_exclusive/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/arrhenius/valid/config.json b/test/unit/unit_configs/process/arrhenius/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/arrhenius/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} From a548ae9ee0ddc630acef598c105e8d2862502fab Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 12:16:17 -0600 Subject: [PATCH 178/318] Adding config.json files. --- .../process/branched/contains_nonstandard_key/config.json | 1 + .../process/branched/missing_alkoxy_products/config.json | 1 + .../process/branched/missing_nitrate_products/config.json | 1 + .../unit_configs/process/branched/missing_reactants/config.json | 1 + .../process/branched/nonstandard_alkoxy_product_coef/config.json | 1 + .../branched/nonstandard_nitrate_product_coef/config.json | 1 + .../process/branched/nonstandard_reactant_coef/config.json | 1 + test/unit/unit_configs/process/branched/valid/config.json | 1 + 8 files changed, 8 insertions(+) create mode 100644 test/unit/unit_configs/process/branched/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/branched/missing_alkoxy_products/config.json create mode 100644 test/unit/unit_configs/process/branched/missing_nitrate_products/config.json create mode 100644 test/unit/unit_configs/process/branched/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/config.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/config.json create mode 100644 test/unit/unit_configs/process/branched/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/branched/valid/config.json diff --git a/test/unit/unit_configs/process/branched/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/branched/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/missing_alkoxy_products/config.json b/test/unit/unit_configs/process/branched/missing_alkoxy_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/missing_alkoxy_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/missing_nitrate_products/config.json b/test/unit/unit_configs/process/branched/missing_nitrate_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/missing_nitrate_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/missing_reactants/config.json b/test/unit/unit_configs/process/branched/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/config.json b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_alkoxy_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/config.json b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_nitrate_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/branched/valid/config.json b/test/unit/unit_configs/process/branched/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/branched/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} From 5e91227c6cbd737fe83458202425a0e993daf9ad Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 12:26:51 -0600 Subject: [PATCH 179/318] Adding config.json files to unit configs. --- .../process/emission/contains_nonstandard_key/config.json | 1 + .../process/emission/missing_MUSICA_name/config.json | 1 + .../unit_configs/process/emission/missing_products/config.json | 1 + test/unit/unit_configs/process/emission/valid/config.json | 1 + .../first_order_loss/contains_nonstandard_key/config.json | 1 + .../process/first_order_loss/missing_MUSICA_name/config.json | 1 + .../process/first_order_loss/missing_reactants/config.json | 1 + .../unit/unit_configs/process/first_order_loss/valid/config.json | 1 + .../process/photolysis/contains_nonstandard_key/config.json | 1 + .../process/photolysis/missing_MUSICA_name/config.json | 1 + .../unit_configs/process/photolysis/missing_products/config.json | 1 + .../process/photolysis/missing_reactants/config.json | 1 + .../process/photolysis/nonstandard_product_coef/config.json | 1 + .../process/photolysis/nonstandard_reactant_coef/config.json | 1 + test/unit/unit_configs/process/photolysis/valid/config.json | 1 + .../process/surface/contains_nonstandard_key/config.json | 1 + .../unit_configs/process/surface/missing_MUSICA_name/config.json | 1 + .../unit_configs/process/surface/missing_products/config.json | 1 + .../unit_configs/process/surface/missing_reactants/config.json | 1 + .../process/surface/nonstandard_product_coef/config.json | 1 + test/unit/unit_configs/process/surface/valid/config.json | 1 + .../contains_nonstandard_key/config.json | 1 + .../ternary_chemical_activation/missing_products/config.json | 1 + .../ternary_chemical_activation/missing_reactants/config.json | 1 + .../nonstandard_product_coef/config.json | 1 + .../nonstandard_reactant_coef/config.json | 1 + .../process/ternary_chemical_activation/valid/config.json | 1 + .../process/troe/contains_nonstandard_key/config.json | 1 + test/unit/unit_configs/process/troe/missing_products/config.json | 1 + .../unit/unit_configs/process/troe/missing_reactants/config.json | 1 + .../process/troe/nonstandard_product_coef/config.json | 1 + .../process/troe/nonstandard_reactant_coef/config.json | 1 + test/unit/unit_configs/process/troe/valid/config.json | 1 + .../process/tunneling/contains_nonstandard_key/config.json | 1 + .../unit_configs/process/tunneling/missing_products/config.json | 1 + .../unit_configs/process/tunneling/missing_reactants/config.json | 1 + .../process/tunneling/nonstandard_product_coef/config.json | 1 + .../process/tunneling/nonstandard_reactant_coef/config.json | 1 + test/unit/unit_configs/process/tunneling/valid/config.json | 1 + 39 files changed, 39 insertions(+) create mode 100644 test/unit/unit_configs/process/emission/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/emission/missing_MUSICA_name/config.json create mode 100644 test/unit/unit_configs/process/emission/missing_products/config.json create mode 100644 test/unit/unit_configs/process/emission/valid/config.json create mode 100644 test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/first_order_loss/missing_MUSICA_name/config.json create mode 100644 test/unit/unit_configs/process/first_order_loss/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/first_order_loss/valid/config.json create mode 100644 test/unit/unit_configs/process/photolysis/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/photolysis/missing_MUSICA_name/config.json create mode 100644 test/unit/unit_configs/process/photolysis/missing_products/config.json create mode 100644 test/unit/unit_configs/process/photolysis/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/photolysis/valid/config.json create mode 100644 test/unit/unit_configs/process/surface/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/surface/missing_MUSICA_name/config.json create mode 100644 test/unit/unit_configs/process/surface/missing_products/config.json create mode 100644 test/unit/unit_configs/process/surface/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/surface/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/surface/valid/config.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/missing_products/config.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/ternary_chemical_activation/valid/config.json create mode 100644 test/unit/unit_configs/process/troe/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/troe/missing_products/config.json create mode 100644 test/unit/unit_configs/process/troe/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/troe/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/troe/valid/config.json create mode 100644 test/unit/unit_configs/process/tunneling/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/tunneling/missing_products/config.json create mode 100644 test/unit/unit_configs/process/tunneling/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/tunneling/valid/config.json diff --git a/test/unit/unit_configs/process/emission/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/emission/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/emission/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/emission/missing_MUSICA_name/config.json b/test/unit/unit_configs/process/emission/missing_MUSICA_name/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/emission/missing_MUSICA_name/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/emission/missing_products/config.json b/test/unit/unit_configs/process/emission/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/emission/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/emission/valid/config.json b/test/unit/unit_configs/process/emission/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/emission/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/first_order_loss/missing_MUSICA_name/config.json b/test/unit/unit_configs/process/first_order_loss/missing_MUSICA_name/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/missing_MUSICA_name/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/first_order_loss/missing_reactants/config.json b/test/unit/unit_configs/process/first_order_loss/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/first_order_loss/valid/config.json b/test/unit/unit_configs/process/first_order_loss/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/first_order_loss/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/missing_MUSICA_name/config.json b/test/unit/unit_configs/process/photolysis/missing_MUSICA_name/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/missing_MUSICA_name/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/missing_products/config.json b/test/unit/unit_configs/process/photolysis/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/missing_reactants/config.json b/test/unit/unit_configs/process/photolysis/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/photolysis/valid/config.json b/test/unit/unit_configs/process/photolysis/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/photolysis/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/surface/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/surface/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/surface/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/surface/missing_MUSICA_name/config.json b/test/unit/unit_configs/process/surface/missing_MUSICA_name/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/surface/missing_MUSICA_name/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/surface/missing_products/config.json b/test/unit/unit_configs/process/surface/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/surface/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/surface/missing_reactants/config.json b/test/unit/unit_configs/process/surface/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/surface/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/surface/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/surface/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/surface/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/surface/valid/config.json b/test/unit/unit_configs/process/surface/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/surface/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/missing_products/config.json b/test/unit/unit_configs/process/ternary_chemical_activation/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/missing_reactants/config.json b/test/unit/unit_configs/process/ternary_chemical_activation/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/ternary_chemical_activation/valid/config.json b/test/unit/unit_configs/process/ternary_chemical_activation/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/ternary_chemical_activation/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/troe/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/troe/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/troe/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/troe/missing_products/config.json b/test/unit/unit_configs/process/troe/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/troe/missing_reactants/config.json b/test/unit/unit_configs/process/troe/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/troe/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/troe/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/troe/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/troe/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/troe/valid/config.json b/test/unit/unit_configs/process/troe/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/troe/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/tunneling/missing_products/config.json b/test/unit/unit_configs/process/tunneling/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/tunneling/missing_reactants/config.json b/test/unit/unit_configs/process/tunneling/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/tunneling/valid/config.json b/test/unit/unit_configs/process/tunneling/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/tunneling/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} From d062a5a23a96654d89497692477a435227db422a Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 13:12:33 -0600 Subject: [PATCH 180/318] Allow config_path to be either a file or a directory with a default config.json. --- include/micm/configure/camp_config.hpp | 26 ++++++++++++++++++------ test/unit/configure/test_camp_config.cpp | 9 ++++++++ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 77da4adee..688497b6b 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -101,6 +101,7 @@ namespace micm std::vector processes_; // Common JSON + static const inline std::string DEFAULT_CONFIG_FILE = "config.json"; static const inline std::string CAMP_FILES = "camp-files"; static const inline std::string CAMP_DATA = "camp-data"; static const inline std::string TYPE = "type"; @@ -108,15 +109,15 @@ namespace micm // Functions /// @brief Parse configures - /// @param config_file Path to a the CAMP configuration file + /// @param config_path Path to a the CAMP configuration directory or file /// @return True for successful parsing - ConfigParseStatus Parse(const std::filesystem::path& config_file) + ConfigParseStatus Parse(const std::filesystem::path& config_path) { // Parse status ConfigParseStatus status; - // Look for CAMP config file - if (!std::filesystem::exists(config_file)) + // Look for CAMP config path + if (!std::filesystem::exists(config_path)) { status = ConfigParseStatus::InvalidCAMPFilePath; std::string msg = configParseStatusToString(status); @@ -124,8 +125,21 @@ namespace micm return status; } - // Extract configuration dir from configuration file path - std::filesystem::path config_dir = config_file.parent_path(); + std::filesystem::path config_dir; + std::filesystem::path config_file; + + if (std::filesystem::is_directory(config_path)) + { + // If config path is a directory, use default config file name + config_dir = config_path; + config_file = config_dir / DEFAULT_CONFIG_FILE; + } + else + { + // Extract configuration dir from configuration file path + config_dir = config_path.parent_path(); + config_file = config_path; + } // The CAMP file list std::vector camp_files; diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp index 55d30f5be..0d9010a6d 100644 --- a/test/unit/configure/test_camp_config.cpp +++ b/test/unit/configure/test_camp_config.cpp @@ -25,6 +25,15 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) EXPECT_EQ(micm::ConfigParseStatus::Success, status); } +TEST(SolverConfig, ReadAndParseCAMPFilesFromDir) +{ + micm::SolverConfig solverConfig{}; + + // Read and parse the CAMP configure file + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); +} + TEST(SolverConfig, ReadAndParseSystemObject) { micm::SolverConfig solverConfig; From ba5e221dddde5e8a2911e2136c2ee1f8a9038bdb Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 13:18:44 -0600 Subject: [PATCH 181/318] Added config.json files to tutorials. --- test/tutorial/configs/rate_constants_no_user_defined/config.json | 1 + test/tutorial/configs/rate_constants_user_defined/config.json | 1 + test/tutorial/configs/robertson/config.json | 1 + 3 files changed, 3 insertions(+) create mode 100644 test/tutorial/configs/rate_constants_no_user_defined/config.json create mode 100644 test/tutorial/configs/rate_constants_user_defined/config.json create mode 100644 test/tutorial/configs/robertson/config.json diff --git a/test/tutorial/configs/rate_constants_no_user_defined/config.json b/test/tutorial/configs/rate_constants_no_user_defined/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/tutorial/configs/rate_constants_no_user_defined/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} diff --git a/test/tutorial/configs/rate_constants_user_defined/config.json b/test/tutorial/configs/rate_constants_user_defined/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/tutorial/configs/rate_constants_user_defined/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} diff --git a/test/tutorial/configs/robertson/config.json b/test/tutorial/configs/robertson/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/tutorial/configs/robertson/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} From a63d6619eb43ce341a00d9522d12757c2e3fb5db Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 13:21:09 -0600 Subject: [PATCH 182/318] Added config.json to kpp_chapman test. --- test/kpp/configs/kpp_chapman/config.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/kpp/configs/kpp_chapman/config.json diff --git a/test/kpp/configs/kpp_chapman/config.json b/test/kpp/configs/kpp_chapman/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/kpp/configs/kpp_chapman/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} From 50fd694e63b3821ed25ce7a4c2d507732b5590ab Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 13:56:07 -0600 Subject: [PATCH 183/318] Added ValidateScheme from solver_config.hpp. --- include/micm/configure/camp_config.hpp | 67 +++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 688497b6b..0356bf580 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -38,7 +38,9 @@ namespace micm InvalidSpecies, InvalidMechanism, ObjectTypeNotFound, - RequiredKeyNotFound + RequiredKeyNotFound, + ContainsNonStandardKey, + MutuallyExclusiveOption }; inline std::string configParseStatusToString(const ConfigParseStatus& status) @@ -57,6 +59,8 @@ namespace micm 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"; } } @@ -767,6 +771,67 @@ namespace micm return ConfigParseStatus::Success; } + + /// @brief Search for nonstandard keys. Only nonstandard keys starting with __ are allowed. Others are considered typos + /// @param object the object whose keys need to be validated + /// @param required_keys The required keys + /// @param optional_keys The optional keys + /// @return true if only standard keys are found + ConfigParseStatus ValidateSchema( + const json& object, + const std::vector& required_keys, + const std::vector& optional_keys) + { + // standard keys are: + // those in required keys + // those in optional keys + // starting with __ + // anything else is reported as an error so that typos are caught, specifically for optional keys + + std::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())) + { + 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("__")) + { + return ConfigParseStatus::ContainsNonStandardKey; + } + } + return ConfigParseStatus::Success; + } }; /// @brief Public interface to read and parse config From 330b6b1e027e9856e46f85c48fc9f4d5b08ab846 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 14:33:42 -0600 Subject: [PATCH 184/318] Adding ValidateSchema to parsers from solver_config.hpp. --- include/micm/configure/camp_config.hpp | 42 +++++++++++++++++++------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 0356bf580..a21a7f302 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -321,14 +321,16 @@ namespace micm { // required keys const std::string NAME = "name"; - std::array required_keys = { NAME }; - // Check if it contains the required key(s) - for (const auto& key : required_keys) + auto status = ValidateSchema( + object, + { NAME, "type" }, + { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } + std::string name = object[NAME].get(); // Load remaining keys as properties @@ -350,12 +352,12 @@ namespace micm ConfigParseStatus ParseMechanism(const json& object) { - std::vector required_keys = { "name", "reactions" }; - for (const auto& key : required_keys) + auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } + std::vector objects; for (const auto& element : object["reactions"]) { @@ -365,35 +367,53 @@ namespace micm return ParseObjectArray(objects); } + // std::pair> ParseReactants(const json& object) std::vector ParseReactants(const json& object) { const std::string QTY = "qty"; std::vector reactants; + + ConfigParseStatus status = ConfigParseStatus::Success; + for (auto& [key, value] : object.items()) { std::size_t qty = 1; + auto new_status = ValidateSchema(value, {}, { "qty" }); + if (new_status != ConfigParseStatus::Success) + { + status = new_status; + } if (value.contains(QTY)) qty = value[QTY]; for (std::size_t i = 0; i < qty; ++i) reactants.push_back(Species(key)); } + // return std::make_pair(status, reactants); return reactants; } std::vector> ParseProducts(const json& object) { const std::string YIELD = "yield"; - constexpr double DEFAULT_YEILD = 1.0; + + ConfigParseStatus status = ConfigParseStatus::Success; + + constexpr double DEFAULT_YIELD = 1.0; std::vector> products; for (auto& [key, value] : object.items()) { + auto new_status = ValidateSchema(value, {}, { "yield" }); + if (new_status != ConfigParseStatus::Success) + { + status = new_status; + } if (value.contains(YIELD)) { products.push_back(std::make_pair(Species(key), value[YIELD])); } else { - products.push_back(std::make_pair(Species(key), DEFAULT_YEILD)); + products.push_back(std::make_pair(Species(key), DEFAULT_YIELD)); } } return products; From a23974b841fa0ce207f8b4cf60c951f9369f6f2d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 16:36:56 -0600 Subject: [PATCH 185/318] Restoring more ValidateSchema checks. --- include/micm/configure/camp_config.hpp | 36 ++++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index a21a7f302..b0d5edd35 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -425,10 +425,10 @@ namespace micm const std::string PRODUCTS = "products"; const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); @@ -449,10 +449,11 @@ namespace micm { const std::string SPECIES = "species"; const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { SPECIES, MUSICA_NAME }) + + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } std::string species = object["species"].get(); @@ -477,10 +478,11 @@ namespace micm { const std::string SPECIES = "species"; const std::string MUSICA_NAME = "MUSICA name"; - for (const auto& key : { SPECIES, MUSICA_NAME }) + + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } std::string species = object["species"].get(); @@ -506,11 +508,11 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = + ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); @@ -557,11 +559,11 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); From 235ac69870dc7ad1486edbabe69f8d95253e59c3 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 16:52:45 -0600 Subject: [PATCH 186/318] Restoring more ValidateSchema checks. --- include/micm/configure/camp_config.hpp | 213 ++++++++++++------------- 1 file changed, 106 insertions(+), 107 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index b0d5edd35..3eafb7260 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -18,10 +18,10 @@ #include #include #include -#include -#include #include +#include #include +#include namespace micm { @@ -94,10 +94,10 @@ namespace micm std::vector user_defined_rate_arr_; std::vector arrhenius_rate_arr_; std::vector troe_rate_arr_; - std::vector branched_rate_arr_; - std::vector surface_rate_arr_; std::vector ternary_rate_arr_; + std::vector branched_rate_arr_; std::vector tunneling_rate_arr_; + std::vector surface_rate_arr_; // Specific for solver parameters Phase gas_phase_; @@ -203,10 +203,10 @@ namespace micm user_defined_rate_arr_.clear(); arrhenius_rate_arr_.clear(); troe_rate_arr_.clear(); - branched_rate_arr_.clear(); - surface_rate_arr_.clear(); ternary_rate_arr_.clear(); + branched_rate_arr_.clear(); tunneling_rate_arr_.clear(); + surface_rate_arr_.clear(); phases_.clear(); processes_.clear(); @@ -278,22 +278,22 @@ namespace micm { status = ParseTroe(object); } - else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") - { - status = ParseBranched(object); - } - else if (type == "SURFACE") - { - status = ParseSurface(object); - } else if (type == "TERNARY_CHEMICAL_ACTIVATION") { status = ParseTernaryChemicalActivation(object); } + else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") + { + status = ParseBranched(object); + } else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") { status = ParseTunneling(object); } + else if (type == "SURFACE") + { + status = ParseSurface(object); + } else { status = ConfigParseStatus::UnknownKey; @@ -612,101 +612,16 @@ namespace micm return ConfigParseStatus::Success; } - ConfigParseStatus ParseBranched(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string ALKOXY_PRODUCTS = "alkoxy products"; - const std::string NITRATE_PRODUCTS = "nitrate products"; - const std::string X = "X"; - const std::string Y = "Y"; - const std::string A0 = "a0"; - const std::string N = "n"; - - // Check required json objects exist - for (const auto& key : { REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); - auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); - - BranchedRateConstantParameters parameters; - parameters.X_ = object[X].get(); - parameters.Y_ = object[Y].get(); - parameters.a0_ = object[A0].get(); - parameters.n_ = object[N].get(); - - // Alkoxy branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); - - // Nitrate branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseSurface(const json& object) - { - const std::string REACTANTS = "gas-phase reactant"; - const std::string PRODUCTS = "gas-phase products"; - const std::string MUSICA_NAME = "MUSICA name"; - const std::string PROBABILITY = "reaction probability"; - for (const auto& key : { REACTANTS, PRODUCTS, MUSICA_NAME }) - { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; - } - - std::string species_name = object[REACTANTS].get(); - json reactants_object{}; - reactants_object[species_name] = { {} }; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(object[PRODUCTS]); - - Species reactant_species = Species(""); - for (auto& species : species_arr_) - { - if (species.name_ == species_name) - { - reactant_species = species; - break; - } - } - SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), - .species_ = reactant_species }; - - if (object.contains(PROBABILITY)) - { - parameters.reaction_probability_ = object[PROBABILITY].get(); - } - - surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - ConfigParseStatus ParseTernaryChemicalActivation(const json& object) { const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); @@ -756,16 +671,56 @@ namespace micm return ConfigParseStatus::Success; } + ConfigParseStatus ParseBranched(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string ALKOXY_PRODUCTS = "alkoxy products"; + const std::string NITRATE_PRODUCTS = "nitrate products"; + const std::string X = "X"; + const std::string Y = "Y"; + const std::string A0 = "a0"; + const std::string N = "n"; + + auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); + auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + + BranchedRateConstantParameters parameters; + parameters.X_ = object[X].get(); + parameters.Y_ = object[Y].get(); + parameters.a0_ = object[A0].get(); + parameters.n_ = object[N].get(); + + // Alkoxy branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); + + // Nitrate branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + ConfigParseStatus ParseTunneling(const json& object) { const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; - // Check required json objects exist - for (const auto& key : { REACTANTS, PRODUCTS }) + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); + if (status != ConfigParseStatus::Success) { - if (!ValidateJsonWithKey(object, key)) - return ConfigParseStatus::RequiredKeyNotFound; + return status; } auto reactants = ParseReactants(object[REACTANTS]); @@ -794,6 +749,50 @@ namespace micm return ConfigParseStatus::Success; } + ConfigParseStatus ParseSurface(const json& object) + { + const std::string REACTANTS = "gas-phase reactant"; + const std::string PRODUCTS = "gas-phase products"; + const std::string MUSICA_NAME = "MUSICA name"; + const std::string PROBABILITY = "reaction probability"; + + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); + if (status != ConfigParseStatus::Success) + { + return status; + } + + std::string species_name = object[REACTANTS].get(); + json reactants_object{}; + reactants_object[species_name] = { {} }; + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(object[PRODUCTS]); + + Species reactant_species = Species(""); + for (auto& species : species_arr_) + { + if (species.name_ == species_name) + { + reactant_species = species; + break; + } + } + SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), + .species_ = reactant_species }; + + if (object.contains(PROBABILITY)) + { + parameters.reaction_probability_ = object[PROBABILITY].get(); + } + + surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + /// @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 From d4af81545c0dfd0e1a8291c8cd64349c4dee9ebf Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 17:13:59 -0600 Subject: [PATCH 187/318] Completed merge from solver_config. --- include/micm/configure/camp_config.hpp | 130 +++++++++++++++++++++---- 1 file changed, 113 insertions(+), 17 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 3eafb7260..5373d503a 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -367,8 +367,7 @@ namespace micm return ParseObjectArray(objects); } - // std::pair> ParseReactants(const json& object) - std::vector ParseReactants(const json& object) + std::pair> ParseReactants(const json& object) { const std::string QTY = "qty"; std::vector reactants; @@ -388,11 +387,10 @@ namespace micm for (std::size_t i = 0; i < qty; ++i) reactants.push_back(Species(key)); } - // return std::make_pair(status, reactants); - return reactants; + return std::make_pair(status, reactants); } - std::vector> ParseProducts(const json& object) + std::pair>> ParseProducts(const json& object) { const std::string YIELD = "yield"; @@ -416,7 +414,7 @@ namespace micm products.push_back(std::make_pair(Species(key), DEFAULT_YIELD)); } } - return products; + return std::make_pair(status, products); } ConfigParseStatus ParsePhotolysis(const json& object) @@ -434,13 +432,23 @@ namespace micm auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + std::string name = "PHOTO." + object[MUSICA_NAME].get(); user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); std::unique_ptr rate_ptr = std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -460,16 +468,27 @@ namespace micm json reactants_object{}; json products_object{}; products_object[species] = { { "YIELD", 1.0 } }; + auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(products_object); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + std::string name = "EMIS." + object[MUSICA_NAME].get(); user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); std::unique_ptr rate_ptr = std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -489,16 +508,27 @@ namespace micm json reactants_object{}; json products_object{}; reactants_object[species] = { {} }; + auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(products_object); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + std::string name = "LOSS." + object[MUSICA_NAME].get(); user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); std::unique_ptr rate_ptr = std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -518,6 +548,16 @@ namespace micm auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + ArrheniusRateConstantParameters parameters; if (object.contains("A")) { @@ -549,7 +589,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -569,6 +609,16 @@ namespace micm auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + TroeRateConstantParameters parameters; if (object.contains("k0_A")) { @@ -607,7 +657,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -627,6 +677,16 @@ namespace micm auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + TernaryChemicalActivationRateConstantParameters parameters; if (object.contains("k0_A")) { @@ -666,7 +726,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -691,6 +751,21 @@ namespace micm auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (alkoxy_products.first != ConfigParseStatus::Success) + { + return alkoxy_products.first; + } + + if (nitrate_products.first != ConfigParseStatus::Success) + { + return nitrate_products.first; + } + BranchedRateConstantParameters parameters; parameters.X_ = object[X].get(); parameters.Y_ = object[Y].get(); @@ -701,13 +776,13 @@ namespace micm parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; branched_rate_arr_.push_back(BranchedRateConstant(parameters)); std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, alkoxy_products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); // Nitrate branch parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; branched_rate_arr_.push_back(BranchedRateConstant(parameters)); rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, nitrate_products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -726,6 +801,16 @@ namespace micm auto reactants = ParseReactants(object[REACTANTS]); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + TunnelingRateConstantParameters parameters; if (object.contains("A")) { @@ -744,7 +829,7 @@ namespace micm std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -765,9 +850,20 @@ namespace micm std::string species_name = object[REACTANTS].get(); json reactants_object{}; reactants_object[species_name] = { {} }; + auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(object[PRODUCTS]); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + Species reactant_species = Species(""); for (auto& species : species_arr_) { @@ -788,7 +884,7 @@ namespace micm surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants, products, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; } @@ -848,7 +944,7 @@ namespace micm { if (!key.starts_with("__")) { - return ConfigParseStatus::ContainsNonStandardKey; + // return ConfigParseStatus::ContainsNonStandardKey; } } return ConfigParseStatus::Success; From 1ffb0d9cc1ae54b0e4cebcad8eed38acc07d76af Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 18:00:34 -0600 Subject: [PATCH 188/318] Save. --- include/micm/configure/camp_config.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 5373d503a..3744e780a 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -944,7 +944,8 @@ namespace micm { if (!key.starts_with("__")) { - // return ConfigParseStatus::ContainsNonStandardKey; + std::cerr << key << std::endl; + return ConfigParseStatus::ContainsNonStandardKey; } } return ConfigParseStatus::Success; From 9a9325236a3dc499557b0bac39b1164c5d8f05ca Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Thu, 26 Oct 2023 18:10:38 -0600 Subject: [PATCH 189/318] Copied some unit tests for CAMP config debugging. --- test/unit/configure/process/CMakeLists.txt | 5 +- .../process/test_arrhenius_camp_config.cpp | 113 ++++++++++++++++++ .../process/test_surface_camp_config.cpp | 82 +++++++++++++ 3 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 test/unit/configure/process/test_arrhenius_camp_config.cpp create mode 100644 test/unit/configure/process/test_surface_camp_config.cpp diff --git a/test/unit/configure/process/CMakeLists.txt b/test/unit/configure/process/CMakeLists.txt index ffbdb9239..a0e1f69b8 100644 --- a/test/unit/configure/process/CMakeLists.txt +++ b/test/unit/configure/process/CMakeLists.txt @@ -14,4 +14,7 @@ create_standard_test(NAME photolysis_config SOURCES test_photolysis_config.cpp) create_standard_test(NAME surface_config SOURCES test_surface_config.cpp) create_standard_test(NAME ternary_chemical_activation_config SOURCES test_ternary_chemical_activation_config.cpp) create_standard_test(NAME troe_config SOURCES test_troe_config.cpp) -create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp) \ No newline at end of file +create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp) + +create_standard_test(NAME arrhenius_camp_config SOURCES test_arrhenius_camp_config.cpp) +create_standard_test(NAME surface_camp_config SOURCES test_surface_camp_config.cpp) diff --git a/test/unit/configure/process/test_arrhenius_camp_config.cpp b/test/unit/configure/process/test_arrhenius_camp_config.cpp new file mode 100644 index 000000000..2b6b7aaf5 --- /dev/null +++ b/test/unit/configure/process/test_arrhenius_camp_config.cpp @@ -0,0 +1,113 @@ +#include + +#include + +TEST(ArrheniusConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/mutually_exclusive"); + EXPECT_EQ(micm::ConfigParseStatus::MutuallyExclusiveOption, status); +} + +TEST(ArrheniusConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::ArrheniusRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.A_, 1.0); + EXPECT_EQ(params.B_, 0.0); + EXPECT_EQ(params.C_, 0.0); + EXPECT_EQ(params.D_, 300); + EXPECT_EQ(params.E_, 0.0); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::ArrheniusRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.B_, -2.3); + EXPECT_EQ(params.C_, 102.3); + EXPECT_EQ(params.D_, 63.4); + EXPECT_EQ(params.E_, -1.3); + } + + // third reaction + { + EXPECT_EQ(process_vector[2].reactants_.size(), 2); + EXPECT_EQ(process_vector[2].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[2].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[2].products_.size(), 2); + EXPECT_EQ(process_vector[2].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[2].products_[0].second, 0.5); + EXPECT_EQ(process_vector[2].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[2].products_[1].second, 1.0); + micm::ArrheniusRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[2].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.B_, -2.3); + EXPECT_EQ(params.C_, -1 * 2e23 / BOLTZMANN_CONSTANT); + EXPECT_EQ(params.D_, 63.4); + EXPECT_EQ(params.E_, -1.3); + } +} + +TEST(ArrheniusConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(ArrheniusConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(ArrheniusConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_surface_camp_config.cpp b/test/unit/configure/process/test_surface_camp_config.cpp new file mode 100644 index 000000000..b85e84a4d --- /dev/null +++ b/test/unit/configure/process/test_surface_camp_config.cpp @@ -0,0 +1,82 @@ +#include + +#include + +TEST(SurfaceConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/surface/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/surface/missing_MUSICA_name"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(SurfaceConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 1); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::SurfaceRateConstant* surface_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + EXPECT_EQ(surface_rate_constant->SizeCustomParameters(), 2); + EXPECT_EQ(surface_rate_constant->CustomParameters()[0], "SURF.kfoo.effective radius [m]"); + EXPECT_EQ(surface_rate_constant->CustomParameters()[1], "SURF.kfoo.particle number concentration [# m-3]"); + EXPECT_EQ(surface_rate_constant->parameters_.reaction_probability_, 1.0); + EXPECT_EQ(surface_rate_constant->diffusion_coefficient_, 2.3e-4); + EXPECT_EQ(surface_rate_constant->mean_free_speed_factor_, 8.0 * GAS_CONSTANT / (M_PI * 0.123)); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 1); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::SurfaceRateConstant* surface_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + EXPECT_EQ(surface_rate_constant->SizeCustomParameters(), 2); + EXPECT_EQ(surface_rate_constant->CustomParameters()[0], "SURF.kbar.effective radius [m]"); + EXPECT_EQ(surface_rate_constant->CustomParameters()[1], "SURF.kbar.particle number concentration [# m-3]"); + EXPECT_EQ(surface_rate_constant->parameters_.reaction_probability_, 0.5); + EXPECT_EQ(surface_rate_constant->diffusion_coefficient_, 0.4e-5); + EXPECT_EQ(surface_rate_constant->mean_free_speed_factor_, 8.0 * GAS_CONSTANT / (M_PI * 0.321)); + } +} + +TEST(SurfaceConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(SurfaceConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} From 27f2d26569d857ce6d54ef2aacbb653468f16e26 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Thu, 26 Oct 2023 19:04:06 -0600 Subject: [PATCH 190/318] Fixed ParseEmission default yield. --- include/micm/configure/camp_config.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 3744e780a..1a54eec79 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -145,6 +145,8 @@ namespace micm config_file = config_path; } + std::cout << "config_file " << config_file << std::endl; + // The CAMP file list std::vector camp_files; @@ -467,7 +469,7 @@ namespace micm std::string species = object["species"].get(); json reactants_object{}; json products_object{}; - products_object[species] = { { "YIELD", 1.0 } }; + products_object[species] = { { "yield", 1.0 } }; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(products_object); @@ -905,6 +907,8 @@ namespace micm // starting with __ // anything else is reported as an error so that typos are caught, specifically for optional keys + std::cout << object.dump(4) << std::endl; + std::vector sorted_object_keys; for (auto& [key, value] : object.items()) sorted_object_keys.push_back(key); @@ -944,7 +948,7 @@ namespace micm { if (!key.starts_with("__")) { - std::cerr << key << std::endl; + std::cerr << "non-standard key " << key << std::endl; return ConfigParseStatus::ContainsNonStandardKey; } } From 59f479fbf3036a8d9abc13e3931def14859c4d44 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Thu, 26 Oct 2023 19:38:02 -0600 Subject: [PATCH 191/318] Ignore null objects in ValidateSchema. --- include/micm/configure/camp_config.hpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 1a54eec79..54f814654 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -190,7 +190,11 @@ namespace micm { for (const auto& object : config_subset[CAMP_DATA]) { - objects.push_back(object); + if (!object.is_null()) + { + // std::cout << object.dump(4) << std::endl; + objects.push_back(object); + } } } else @@ -199,7 +203,6 @@ namespace micm } } - // Clear vectors and maps species_arr_.clear(); user_defined_rate_arr_.clear(); @@ -245,8 +248,8 @@ namespace micm std::string type = object[TYPE].get(); // debug statements - // std::cout << type << std::endl; - // std::cout << object.dump(4) << std::endl; + std::cout << type << std::endl; + std::cout << object.dump(4) << std::endl; if (type == "CHEM_SPEC") { @@ -907,7 +910,11 @@ namespace micm // starting with __ // anything else is reported as an error so that typos are caught, specifically for optional keys - std::cout << object.dump(4) << std::endl; + std::cout << "ValidateSchema object " << object.dump(4) << std::endl; + if (object.begin().value().is_null()) + { + return ConfigParseStatus::Success; + } std::vector sorted_object_keys; for (auto& [key, value] : object.items()) From d8fb1be682b763ee0be9b97cdc0596343d83693a Mon Sep 17 00:00:00 2001 From: dwfncar Date: Thu, 26 Oct 2023 19:45:29 -0600 Subject: [PATCH 192/318] Restored Arrhenius check for mutually exclusive option. --- include/micm/configure/camp_config.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 54f814654..84f8435ca 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -586,6 +586,11 @@ namespace micm } if (object.contains("Ea")) { + if (parameters.C_ != 0) + { + std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl; + return ConfigParseStatus::MutuallyExclusiveOption; + } // Calculate 'C' using 'Ea' parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; } From 22e1b6cfeb60abb585350248706116c5ae4a1ffa Mon Sep 17 00:00:00 2001 From: dwfncar Date: Thu, 26 Oct 2023 23:01:26 -0600 Subject: [PATCH 193/318] Save. --- include/micm/configure/camp_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 84f8435ca..f522451ac 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -916,7 +916,7 @@ namespace micm // anything else is reported as an error so that typos are caught, specifically for optional keys std::cout << "ValidateSchema object " << object.dump(4) << std::endl; - if (object.begin().value().is_null()) + if (!object.empty() && object.begin().value().is_null()) { return ConfigParseStatus::Success; } From a05079f33b2c0a716e2796a7cb883493fc7623c8 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Thu, 26 Oct 2023 23:11:16 -0600 Subject: [PATCH 194/318] Added clang fix. --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ea8bc748..8f37ccac5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,3 +94,9 @@ if(PROJECT_IS_TOP_LEVEL) endif() ################################################################################ + +# on ubuntu with clang, an incorrect version of the c++ standard library was being linked +if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # If the compiler is Clang, use libc++ + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +endif() From cb4c6181bb4b08e6371e52af86e52c080dc2d07d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 27 Oct 2023 06:48:19 -0600 Subject: [PATCH 195/318] Use complete unit test suite for camp_config. --- include/micm/configure/camp_config.hpp | 9 +- test/unit/configure/process/CMakeLists.txt | 7 + .../process/test_arrhenius_config.cpp | 2 +- .../process/test_branched_camp_config.cpp | 131 ++++++++++++++++++ .../process/test_emission_camp_config.cpp | 58 ++++++++ .../test_first_order_loss_camp_config.cpp | 57 ++++++++ .../process/test_photolysis_camp_config.cpp | 82 +++++++++++ ...ernary_chemical_activation_camp_config.cpp | 101 ++++++++++++++ .../process/test_troe_camp_config.cpp | 97 +++++++++++++ .../process/test_tunneling_camp_config.cpp | 87 ++++++++++++ 10 files changed, 626 insertions(+), 5 deletions(-) create mode 100644 test/unit/configure/process/test_branched_camp_config.cpp create mode 100644 test/unit/configure/process/test_emission_camp_config.cpp create mode 100644 test/unit/configure/process/test_first_order_loss_camp_config.cpp create mode 100644 test/unit/configure/process/test_photolysis_camp_config.cpp create mode 100644 test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp create mode 100644 test/unit/configure/process/test_troe_camp_config.cpp create mode 100644 test/unit/configure/process/test_tunneling_camp_config.cpp diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index f522451ac..2d05cd1aa 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -192,7 +192,6 @@ namespace micm { if (!object.is_null()) { - // std::cout << object.dump(4) << std::endl; objects.push_back(object); } } @@ -248,8 +247,8 @@ namespace micm std::string type = object[TYPE].get(); // debug statements - std::cout << type << std::endl; - std::cout << object.dump(4) << std::endl; + // std::cout << type << std::endl; + // std::cout << object.dump(4) << std::endl; if (type == "CHEM_SPEC") { @@ -915,7 +914,9 @@ namespace micm // starting with __ // anything else is reported as an error so that typos are caught, specifically for optional keys - std::cout << "ValidateSchema object " << object.dump(4) << std::endl; + // debug statement + // std::cout << "ValidateSchema object " << object.dump(4) << std::endl; + if (!object.empty() && object.begin().value().is_null()) { return ConfigParseStatus::Success; diff --git a/test/unit/configure/process/CMakeLists.txt b/test/unit/configure/process/CMakeLists.txt index a0e1f69b8..4043124d2 100644 --- a/test/unit/configure/process/CMakeLists.txt +++ b/test/unit/configure/process/CMakeLists.txt @@ -17,4 +17,11 @@ create_standard_test(NAME troe_config SOURCES test_troe_config.cpp) create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp) create_standard_test(NAME arrhenius_camp_config SOURCES test_arrhenius_camp_config.cpp) +create_standard_test(NAME branched_camp_config SOURCES test_branched_camp_config.cpp) +create_standard_test(NAME emission_camp_config SOURCES test_emission_camp_config.cpp) +create_standard_test(NAME first_order_loss_camp_config SOURCES test_first_order_loss_camp_config.cpp) +create_standard_test(NAME photolysis_camp_config SOURCES test_photolysis_camp_config.cpp) create_standard_test(NAME surface_camp_config SOURCES test_surface_camp_config.cpp) +create_standard_test(NAME ternary_chemical_activation_camp_config SOURCES test_ternary_chemical_activation_camp_config.cpp) +create_standard_test(NAME troe_camp_config SOURCES test_troe_camp_config.cpp) +create_standard_test(NAME tunneling_camp_config SOURCES test_tunneling_camp_config.cpp) diff --git a/test/unit/configure/process/test_arrhenius_config.cpp b/test/unit/configure/process/test_arrhenius_config.cpp index d2ab313eb..65837f22e 100644 --- a/test/unit/configure/process/test_arrhenius_config.cpp +++ b/test/unit/configure/process/test_arrhenius_config.cpp @@ -110,4 +110,4 @@ TEST(ArrheniusConfig, DetectsNonstandardReactantCoefficient) micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} \ No newline at end of file +} diff --git a/test/unit/configure/process/test_branched_camp_config.cpp b/test/unit/configure/process/test_branched_camp_config.cpp new file mode 100644 index 000000000..225b1a441 --- /dev/null +++ b/test/unit/configure/process/test_branched_camp_config.cpp @@ -0,0 +1,131 @@ +#include + +#include + +TEST(BranchedConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/branched/missing_alkoxy_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/branched/missing_nitrate_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(BranchedConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + EXPECT_EQ(process_vector.size(), 4); + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::BranchedRateConstant* branched_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = branched_rate_constant->parameters_; + EXPECT_EQ(params.X_, 12.3); + EXPECT_EQ(params.Y_, 42.3); + EXPECT_EQ(params.a0_, 1.0e-5); + EXPECT_EQ(params.n_, 3); + EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Alkoxy); + } + { + EXPECT_EQ(process_vector[1].reactants_.size(), 3); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[1].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[1].products_.size(), 1); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "quz"); + EXPECT_EQ(process_vector[1].products_[0].second, 1.0); + micm::BranchedRateConstant* branched_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = branched_rate_constant->parameters_; + EXPECT_EQ(params.X_, 12.3); + EXPECT_EQ(params.Y_, 42.3); + EXPECT_EQ(params.a0_, 1.0e-5); + EXPECT_EQ(params.n_, 3); + EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Nitrate); + } + + // second reaction + { + EXPECT_EQ(process_vector[2].reactants_.size(), 2); + EXPECT_EQ(process_vector[2].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[2].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[2].products_.size(), 1); + EXPECT_EQ(process_vector[2].products_[0].first.name_, "baz"); + EXPECT_EQ(process_vector[2].products_[0].second, 1.0); + micm::BranchedRateConstant* branched_rate_constant = + dynamic_cast(process_vector[2].rate_constant_.get()); + auto& params = branched_rate_constant->parameters_; + EXPECT_EQ(params.X_, 0.32); + EXPECT_EQ(params.Y_, 2.3e8); + EXPECT_EQ(params.a0_, 0.423); + EXPECT_EQ(params.n_, 6); + EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Alkoxy); + } + { + EXPECT_EQ(process_vector[3].reactants_.size(), 2); + EXPECT_EQ(process_vector[3].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[3].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[3].products_.size(), 2); + EXPECT_EQ(process_vector[3].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[3].products_[0].second, 0.5); + EXPECT_EQ(process_vector[3].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[3].products_[1].second, 1.0); + micm::BranchedRateConstant* branched_rate_constant = + dynamic_cast(process_vector[3].rate_constant_.get()); + auto& params = branched_rate_constant->parameters_; + EXPECT_EQ(params.X_, 0.32); + EXPECT_EQ(params.Y_, 2.3e8); + EXPECT_EQ(params.a0_, 0.423); + EXPECT_EQ(params.n_, 6); + EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Nitrate); + } +} + +TEST(BranchedConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(BranchedConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_alkoxy_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); + + status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_nitrate_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(BranchedConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_emission_camp_config.cpp b/test/unit/configure/process/test_emission_camp_config.cpp new file mode 100644 index 000000000..e820f54d0 --- /dev/null +++ b/test/unit/configure/process/test_emission_camp_config.cpp @@ -0,0 +1,58 @@ +#include + +#include + +TEST(EmissionConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/emission/missing_MUSICA_name"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(EmissionConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 0); + EXPECT_EQ(process_vector[0].products_.size(), 1); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "foo"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + micm::UserDefinedRateConstant* emission_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.foo"); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 0); + EXPECT_EQ(process_vector[1].products_.size(), 1); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 1.0); + micm::UserDefinedRateConstant* emission_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.bar"); + } +} + +TEST(EmissionConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_first_order_loss_camp_config.cpp b/test/unit/configure/process/test_first_order_loss_camp_config.cpp new file mode 100644 index 000000000..300b2561b --- /dev/null +++ b/test/unit/configure/process/test_first_order_loss_camp_config.cpp @@ -0,0 +1,57 @@ +#include + +#include + +TEST(FirstOrderLossConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/missing_MUSICA_name"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(FirstOrderLossConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 1); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].products_.size(), 0); + micm::UserDefinedRateConstant* first_order_loss_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.foo"); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 1); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].products_.size(), 0); + micm::UserDefinedRateConstant* first_order_loss_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.bar"); + } +} + +TEST(FirstOrderLossConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/first_order_loss/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_photolysis_camp_config.cpp b/test/unit/configure/process/test_photolysis_camp_config.cpp new file mode 100644 index 000000000..4a303d1e1 --- /dev/null +++ b/test/unit/configure/process/test_photolysis_camp_config.cpp @@ -0,0 +1,82 @@ +#include + +#include + +TEST(PhotolysisConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/photolysis/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/photolysis/missing_MUSICA_name"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(PhotolysisConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 1); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::UserDefinedRateConstant* photo_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jfoo"); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 1); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::UserDefinedRateConstant* photo_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jbar"); + } +} + +TEST(PhotolysisConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(PhotolysisConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(PhotolysisConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp b/test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp new file mode 100644 index 000000000..64ad3ecd2 --- /dev/null +++ b/test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp @@ -0,0 +1,101 @@ +#include + +#include + +TEST(TernaryChemicalActivationConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(TernaryChemicalActivationConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::TernaryChemicalActivationRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.k0_A_, 1.0); + EXPECT_EQ(params.k0_B_, 0.0); + EXPECT_EQ(params.k0_C_, 0.0); + EXPECT_EQ(params.kinf_A_, 1.0); + EXPECT_EQ(params.kinf_B_, 0.0); + EXPECT_EQ(params.kinf_C_, 0.0); + EXPECT_EQ(params.Fc_, 0.6); + EXPECT_EQ(params.N_, 1.0); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::TernaryChemicalActivationRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.k0_A_, 32.1); + EXPECT_EQ(params.k0_B_, -2.3); + EXPECT_EQ(params.k0_C_, 102.3); + EXPECT_EQ(params.kinf_A_, 63.4); + EXPECT_EQ(params.kinf_B_, -1.3); + EXPECT_EQ(params.kinf_C_, 908.5); + EXPECT_EQ(params.Fc_, 1.3); + EXPECT_EQ(params.N_, 32.1); + } +} + +TEST(TernaryChemicalActivationConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TernaryChemicalActivationConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TernaryChemicalActivationConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_troe_camp_config.cpp b/test/unit/configure/process/test_troe_camp_config.cpp new file mode 100644 index 000000000..99934568f --- /dev/null +++ b/test/unit/configure/process/test_troe_camp_config.cpp @@ -0,0 +1,97 @@ +#include + +#include + +TEST(TroeConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(TroeConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::TroeRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.k0_A_, 1.0); + EXPECT_EQ(params.k0_B_, 0.0); + EXPECT_EQ(params.k0_C_, 0.0); + EXPECT_EQ(params.kinf_A_, 1.0); + EXPECT_EQ(params.kinf_B_, 0.0); + EXPECT_EQ(params.kinf_C_, 0.0); + EXPECT_EQ(params.Fc_, 0.6); + EXPECT_EQ(params.N_, 1.0); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::TroeRateConstant* ternary_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = ternary_rate_constant->parameters_; + EXPECT_EQ(params.k0_A_, 32.1); + EXPECT_EQ(params.k0_B_, -2.3); + EXPECT_EQ(params.k0_C_, 102.3); + EXPECT_EQ(params.kinf_A_, 63.4); + EXPECT_EQ(params.kinf_B_, -1.3); + EXPECT_EQ(params.kinf_C_, 908.5); + EXPECT_EQ(params.Fc_, 1.3); + EXPECT_EQ(params.N_, 32.1); + } +} + +TEST(TroeConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TroeConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TroeConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/configure/process/test_tunneling_camp_config.cpp b/test/unit/configure/process/test_tunneling_camp_config.cpp new file mode 100644 index 000000000..726362336 --- /dev/null +++ b/test/unit/configure/process/test_tunneling_camp_config.cpp @@ -0,0 +1,87 @@ +#include + +#include + +TEST(TunnelingConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/tunneling/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(TunnelingConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.0); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[1].second, 3.2); + micm::TunnelingRateConstant* tunneling_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + auto& params = tunneling_rate_constant->parameters_; + EXPECT_EQ(params.A_, 1.0); + EXPECT_EQ(params.B_, 0.0); + EXPECT_EQ(params.C_, 0.0); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); + EXPECT_EQ(process_vector[1].products_.size(), 2); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 0.5); + EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[1].products_[1].second, 1.0); + micm::TunnelingRateConstant* tunneling_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + auto& params = tunneling_rate_constant->parameters_; + EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.B_, -2.3); + EXPECT_EQ(params.C_, 102.3); + } +} + +TEST(TunnelingConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TunnelingConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(TunnelingConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} From 07d872ec5184675d34cf1c99d98b94e38c61d2ed Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 10:34:12 -0500 Subject: [PATCH 196/318] jit tutorial example --- docs/source/user_guide/index.rst | 1 + docs/source/user_guide/jit.rst | 41 ++++ include/micm/configure/solver_config.hpp | 19 +- include/micm/util/matrix.hpp | 5 + include/micm/util/sparse_matrix.hpp | 5 + include/micm/util/vector_matrix.hpp | 5 + test/tutorial/CMakeLists.txt | 4 + .../configs/carbon_bond_5/config.json | 1 + .../configs/carbon_bond_5/reactions.json | 1 + .../configs/carbon_bond_5/species.json | 1 + test/tutorial/test_jit_tutorial.cpp | 188 ++++++++++++++++++ 11 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 docs/source/user_guide/jit.rst create mode 100644 test/tutorial/configs/carbon_bond_5/config.json create mode 100644 test/tutorial/configs/carbon_bond_5/reactions.json create mode 100644 test/tutorial/configs/carbon_bond_5/species.json create mode 100644 test/tutorial/test_jit_tutorial.cpp diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 7896b135a..064955a8b 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -47,3 +47,4 @@ If you would like to include the json examples, you must configure micm to build solver_configurations but_how_fast_is_it openmp + jit diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst new file mode 100644 index 000000000..51a573db7 --- /dev/null +++ b/docs/source/user_guide/jit.rst @@ -0,0 +1,41 @@ +.. _JIT: + +A JIT-compiled solver +===================== + +Solving a chemical mechanism is a computationally intensive task. There are many reasons for this such as +multiple iterations by a solver to achieve an integration, stiff systems requiring internal substepping to +acheive a numerically stable solution, and cache misses, among others. + +This tutorial focuses on alleviating the cache misses. A popular method for handling cache misses is to pre-compute the indices. +This method, which may be referred to as ahead-of-time (AOT) compilation, is used in applications such as KPP :cite:`Damian2002`. +Pre-computed methods require code preprocessors and preclude runtime configurable software, which is a goal of micm. + +MICM uses just-in-time (JIT) compiled functions built with `LLVM JIT `_ libraries +to supply runtime-configurable chemistry to avoid cache misses in important chemistry functions. + +Up until now, a :cpp:class:`micm::RosenbrockSolver` has been used. This is a special class in micm which builds all +of the componenets needed to solve chemistry in memory, including the forcing function and the jacobian. MICM +also provides a :cpp:class:`micm::JitRosenbrockSolver` which builds and compiles several important chemistry functions at runtime. + +What are we JIT-compilng? +------------------------- + +A list of compiled functions is below. How they are compiled and the ellided operations are beyond the scope of this tutorial, but you are free to inspect the source code yourself. + +- :cpp:func:`micm::RosenbrockSolver::AlphaMinusJacobian` is JIT compiled and called with :cpp:func:`micm::JitRosenbrockSolver::AlphaMinusJacobian` +- :cpp:func:`micm::LuDecomposition::Decompose` is JIT compiled and called with :cpp:func:`micm::JitLuDecomposition::Decompose` +- :cpp:func:`micm::LinearSolver::Factor` and :cpp:func:`micm::LinearSolver::Solve` are JIT compiled and called with :cpp:func:`micm::JitLinearSolver::Factor` and :cpp:func:`micm::JitLinearSolver::Solve` +- :cpp:func:`micm::ProcessSet::AddForcingTerms` and :cpp:func:`micm::ProcessSet::AddJacobianTerms` are JIT compiled and called with :cpp:func:`micm::JitProcessSet::AddForcingTerms` and :cpp:func:`micm::JitProcessSet::AddJacobianTerms` + +So what does this gain me? +-------------------------- + +Runtime configuraiton of chemical mechanisms that are *fast*. Let's compare the speed of :cpp:class:`micm::RosenbrockSolver` +and the :cpp:class:`micm::JitRosenbrockSolver` classes applied to the same problem. We'll use this system. + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 15f547eca..5f17f1727 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -434,8 +434,9 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; const std::string MUSICA_NAME = "MUSICA name"; + const std::string SCALING_FACTOR = "scaling_factor"; - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {SCALING_FACTOR}); if (status != ConfigParseStatus::Success) { return status; @@ -453,6 +454,10 @@ namespace micm { return products.first; } + + if (object.contains(SCALING_FACTOR)){ + std::cerr << "Scaling factor supplied to photolysis rate. This is not yet implemented." << std::endl; + } std::string name = "PHOTO." + object[MUSICA_NAME].get(); @@ -767,8 +772,10 @@ namespace micm { const std::string SPECIES = "species"; const std::string MUSICA_NAME = "MUSICA name"; + const std::string PRODUCTS = "products"; + const std::string SCALING_FACTOR = "scaling factor"; - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {SCALING_FACTOR, PRODUCTS}); if (status != ConfigParseStatus::Success) { return status; @@ -788,6 +795,14 @@ namespace micm { return products.first; } + + if (object.contains(PRODUCTS)) { + std::cerr << "Emission contains products, presumably to record the integrated reaction rate. Ignoring for now" << std::endl; + } + + if (object.contains(SCALING_FACTOR)){ + std::cerr << "Scaling factor supplied to emission rate. This is not yet implemented." << std::endl; + } std::string name = "EMIS." + object[MUSICA_NAME].get(); diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index fe98bc3a4..329f386d6 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -191,6 +191,11 @@ namespace micm return Proxy(*this, x * y_dim_, y_dim_); } + Matrix& operator=(T val) { + std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _){ return val;}); + return *this; + } + void ForEach(const std::function f, Matrix &a) { auto a_iter = a.AsVector().begin(); diff --git a/include/micm/util/sparse_matrix.hpp b/include/micm/util/sparse_matrix.hpp index fe4915a70..065c17be2 100644 --- a/include/micm/util/sparse_matrix.hpp +++ b/include/micm/util/sparse_matrix.hpp @@ -229,6 +229,11 @@ namespace micm return ProxyRow(*this, b); } + SparseMatrix& operator=(T val) { + std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _){ return val;}); + return *this; + } + const std::vector& RowStartVector() const { return row_start_; diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index a8507f6f9..7f0bc1fc4 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -212,6 +212,11 @@ namespace micm return Proxy(*this, std::floor(x / L), x % L, y_dim_); } + VectorMatrix& operator=(T val) { + std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _){ return val;}); + return *this; + } + void ForEach(const std::function f, VectorMatrix &a) { auto this_iter = data_.begin(); diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index 163a624a5..cac58994a 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -20,6 +20,10 @@ if(ENABLE_JSON) if(ENABLE_OPENMP) create_standard_test(NAME openmp_tutorial SOURCES test_openmp.cpp) endif() + + if(ENABLE_LLVM) + create_standard_test(NAME jit_tutorial SOURCES test_jit_tutorial.cpp) + endif() endif() ################################################################################ diff --git a/test/tutorial/configs/carbon_bond_5/config.json b/test/tutorial/configs/carbon_bond_5/config.json new file mode 100644 index 000000000..03622f157 --- /dev/null +++ b/test/tutorial/configs/carbon_bond_5/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} \ No newline at end of file diff --git a/test/tutorial/configs/carbon_bond_5/reactions.json b/test/tutorial/configs/carbon_bond_5/reactions.json new file mode 100644 index 000000000..617f98810 --- /dev/null +++ b/test/tutorial/configs/carbon_bond_5/reactions.json @@ -0,0 +1 @@ +{"camp-data": [{"type": "MECHANISM", "name": "music box interactive configuration", "reactions": [{"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NO2", "reactants": {"NO2": {}}, "products": {"NO": {"yield": 1}, "O": {"yield": 1}, "irr__0": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6e-34, "Ea": 0, "B": -2.4, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "O2": {"qty": 1}, "M": {"qty": 1}}, "products": {"O3": {"yield": 1}, "M": {"yield": 1}, "irr__1": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__2": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__3": {"yield": 1}}}, {"type": "TROE", "k0_A": 2.5e-31, "k0_B": -1.8, "k0_C": 0, "kinf_A": 2.2e-11, "kinf_B": -0.7, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"O": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__4": {"yield": 1}}}, {"type": "TROE", "k0_A": 9e-32, "k0_B": -1.5, "k0_C": 0, "kinf_A": 3e-11, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"O": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__5": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.2e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO2": {"qty": 1}, "O3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__6": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "O3->O3P", "reactants": {"O3": {}}, "products": {"O": {"yield": 1}, "irr__7": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "O3->O1D", "reactants": {"O3": {}}, "products": {"O1D": {"yield": 1}, "irr__8": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O1D": {"qty": 1}, "M": {"qty": 1}}, "products": {"O": {"yield": 1}, "M": {"yield": 1}, "irr__9": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O1D": {"qty": 1}, "H2O": {"qty": 1}}, "products": {"OH": {"yield": 2}, "irr__10": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__11": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"OH": {"yield": 1}, "irr__12": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NO3->NO2", "reactants": {"NO3": {}}, "products": {"NO2": {"yield": 1}, "O": {"yield": 1}, "irr__13": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NO3->NO", "reactants": {"NO3": {}}, "products": {"NO": {"yield": 1}, "irr__14": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 2}, "irr__15": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.5e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NO": {"yield": 1}, "NO2": {"yield": 1}, "irr__16": {"yield": 1}}}, {"type": "TROE", "k0_A": 2e-30, "k0_B": -4.4, "k0_C": 0, "kinf_A": 1.4e-12, "kinf_B": -0.7, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"NO3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"N2O5": {"yield": 1}, "irr__17": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.5e-22, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"N2O5": {"qty": 1}, "H2O": {"qty": 1}}, "products": {"HNO3": {"yield": 2}, "irr__18": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-39, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"N2O5": {"qty": 1}, "H2O": {"qty": 2}}, "products": {"HNO3": {"yield": 2}, "irr__19": {"yield": 1}}}, {"type": "TROE", "k0_A": 0.001, "k0_B": -3.5, "k0_C": -11000, "kinf_A": 970000000000000, "kinf_B": 0.1, "kinf_C": -11080, "Fc": 0.45, "N": 1, "reactants": {"N2O5": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "NO2": {"yield": 1}, "irr__20": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.3e-39, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO": {"qty": 2}, "O2": {"qty": 1}}, "products": {"NO2": {"yield": 2}, "irr__21": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5e-40, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO": {"qty": 1}, "NO2": {"qty": 1}, "H2O": {"qty": 1}}, "products": {"HONO": {"yield": 2}, "irr__22": {"yield": 1}}}, {"type": "TROE", "k0_A": 7e-31, "k0_B": -2.6, "k0_C": 0, "kinf_A": 3.6e-11, "kinf_B": -0.1, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"NO": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HONO": {"yield": 1}, "irr__23": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "HONO", "reactants": {"HONO": {}}, "products": {"NO": {"yield": 1}, "OH": {"yield": 1}, "irr__24": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "HONO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__25": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-20, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HONO": {"qty": 2}}, "products": {"NO": {"yield": 1}, "NO2": {"yield": 1}, "irr__26": {"yield": 1}}}, {"type": "TROE", "k0_A": 2e-30, "k0_B": -3, "k0_C": 0, "kinf_A": 2.5e-11, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"NO2": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "irr__27": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.4e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "HNO3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__28": {"yield": 1}}}, {"type": "TROE", "k0_A": 6.5e-34, "k0_B": 0, "k0_C": 1335, "kinf_A": 2.7e-17, "kinf_B": 0, "kinf_C": 2199, "Fc": 1, "N": 1, "reactants": {"OH": {"qty": 1}, "HNO3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__29": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"OH": {"yield": 1}, "NO2": {"yield": 1}, "irr__30": {"yield": 1}}}, {"type": "TROE", "k0_A": 1.8e-31, "k0_B": -3.2, "k0_C": 0, "kinf_A": 4.7e-12, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"HO2": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"PNA": {"yield": 1}, "irr__31": {"yield": 1}}}, {"type": "TROE", "k0_A": 4.1e-05, "k0_B": 0, "k0_C": -10650, "kinf_A": 4800000000000000, "kinf_B": 0, "kinf_C": -11170, "Fc": 0.6, "N": 1, "reactants": {"PNA": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__32": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "PNA": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__33": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}}, "products": {"H2O2": {"yield": 1}, "irr__34": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.7e-33, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}, "M": {"qty": 1}}, "products": {"H2O2": {"yield": 1}, "irr__35": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.22e-34, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}, "H2O": {"qty": 1}}, "products": {"H2O2": {"yield": 1}, "irr__36": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.38e-54, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}, "H2O": {"qty": 1}, "M": {"qty": 1}}, "products": {"H2O2": {"yield": 1}, "irr__37": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "H2O2", "reactants": {"H2O2": {}}, "products": {"OH": {"yield": 2}, "irr__38": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "H2O2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__39": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.1e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O1D": {"qty": 1}, "H2": {"qty": 1}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "irr__40": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "H2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__41": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "O": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__42": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.2e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 2}}, "products": {"O": {"yield": 1}, "irr__43": {"yield": 1}}}, {"type": "TROE", "k0_A": 6.9e-31, "k0_B": -1, "k0_C": 0, "kinf_A": 2.6e-11, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"OH": {"qty": 2}}, "products": {"H2O2": {"yield": 1}, "irr__44": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"irr__45": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 1}, "O": {"qty": 1}}, "products": {"OH": {"yield": 1}, "irr__46": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.4e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"H2O2": {"qty": 1}, "O": {"qty": 1}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "irr__47": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "O": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__48": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__49": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "irr__50": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-17, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "O3": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__51": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 2}}, "products": {"NO2": {"yield": 2}, "irr__52": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PNA", "reactants": {"PNA": {}}, "products": {"HO2": {"yield": 0.61}, "NO2": {"yield": 0.61}, "OH": {"yield": 0.39}, "NO3": {"yield": 0.39}, "irr__53": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "HNO3", "reactants": {"HNO3": {}}, "products": {"OH": {"yield": 1}, "NO2": {"yield": 1}, "irr__54": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "N2O5", "reactants": {"N2O5": {}}, "products": {"NO2": {"yield": 1}, "NO3": {"yield": 1}, "irr__55": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__56": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2N": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NTR": {"yield": 1}, "irr__57": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"ROOH": {"yield": 1}, "irr__58": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2N": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"ROOH": {"yield": 1}, "irr__59": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.8e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 2}}, "products": {"irr__60": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.8e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2N": {"qty": 2}}, "products": {"irr__61": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.8e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 1}, "XO2N": {"qty": 1}}, "products": {"irr__62": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NTR": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "HO2": {"yield": 1}, "FORM": {"yield": 0.33}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.33}, "PAR": {"yield": -0.66}, "irr__63": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NTR", "reactants": {"NTR": {}}, "products": {"NO2": {"yield": 1}, "HO2": {"yield": 1}, "FORM": {"yield": 0.33}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.33}, "PAR": {"yield": -0.66}, "irr__64": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.01e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROOH": {"qty": 1}, "OH": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "ALD2": {"yield": 0.5}, "ALDX": {"yield": 0.5}, "irr__65": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "ROOH", "reactants": {"ROOH": {}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "ALD2": {"yield": 0.5}, "ALDX": {"yield": 0.5}, "irr__66": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.44e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CO": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__67": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.43e-33, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CO": {"qty": 1}, "M": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__68": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.45e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CH4": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "irr__69": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.8e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__70": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.1e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"MEPX": {"yield": 1}, "irr__71": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9.5e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEO2": {"qty": 2}}, "products": {"FORM": {"yield": 1.37}, "HO2": {"yield": 0.74}, "MEOH": {"yield": 0.63}, "irr__72": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.8e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEPX": {"qty": 1}, "OH": {"qty": 1}}, "products": {"MEO2": {"yield": 0.7}, "XO2": {"yield": 0.3}, "HO2": {"yield": 0.3}, "irr__73": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "MEPX", "reactants": {"MEPX": {}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "OH": {"yield": 1}, "irr__74": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEOH": {"qty": 1}, "OH": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "irr__75": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__76": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "FORM->HO2", "reactants": {"FORM": {}}, "products": {"HO2": {"yield": 2}, "CO": {"yield": 1}, "irr__77": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "FORM->CO", "reactants": {"FORM": {}}, "products": {"CO": {"yield": 1}, "irr__78": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.4e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "O": {"qty": 1}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__79": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.8e-16, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__80": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9.7e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HCO3": {"yield": 1}, "irr__81": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2400000000000, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HCO3": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "irr__82": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HCO3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"FACD": {"yield": 1}, "NO2": {"yield": 1}, "HO2": {"yield": 1}, "irr__83": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HCO3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"MEPX": {"yield": 1}, "irr__84": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FACD": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__85": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALD2": {"qty": 1}, "O": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "OH": {"yield": 1}, "irr__86": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALD2": {"qty": 1}, "OH": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "irr__87": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.4e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALD2": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "HNO3": {"yield": 1}, "irr__88": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "ALD2", "reactants": {"ALD2": {}}, "products": {"MEO2": {"yield": 1}, "CO": {"yield": 1}, "HO2": {"yield": 1}, "irr__89": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.1e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__90": {"yield": 1}}}, {"type": "TROE", "k0_A": 2.7e-28, "k0_B": -7.1, "k0_C": 0, "kinf_A": 1.2e-11, "kinf_B": -0.9, "kinf_C": 0, "Fc": 0.3, "N": 1, "reactants": {"C2O3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"PAN": {"yield": 1}, "irr__91": {"yield": 1}}}, {"type": "TROE", "k0_A": 0.0049, "k0_B": 0, "k0_C": -12100, "kinf_A": 54000000000000000, "kinf_B": 0, "kinf_C": -13830, "Fc": 0.3, "N": 1, "reactants": {"PAN": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "NO2": {"yield": 1}, "irr__92": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PAN", "reactants": {"PAN": {}}, "products": {"C2O3": {"yield": 1}, "NO2": {"yield": 1}, "irr__93": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"PACD": {"yield": 0.8}, "AACD": {"yield": 0.2}, "O3": {"yield": 0.2}, "irr__94": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "MEO2": {"qty": 1}}, "products": {"MEO2": {"yield": 0.9}, "HO2": {"yield": 0.9}, "FORM": {"yield": 1}, "AACD": {"yield": 0.1}, "irr__95": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "XO2": {"qty": 1}}, "products": {"MEO2": {"yield": 0.9}, "AACD": {"yield": 0.1}, "irr__96": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 2}}, "products": {"MEO2": {"yield": 2}, "irr__97": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"PACD": {"qty": 1}, "OH": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "irr__98": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PACD", "reactants": {"PACD": {}}, "products": {"MEO2": {"yield": 1}, "OH": {"yield": 1}, "irr__99": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"AACD": {"qty": 1}, "OH": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "irr__100": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALDX": {"qty": 1}, "O": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "OH": {"yield": 1}, "irr__101": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.1e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALDX": {"qty": 1}, "OH": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "irr__102": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.5e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALDX": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "HNO3": {"yield": 1}, "irr__103": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "ALDX", "reactants": {"ALDX": {}}, "products": {"MEO2": {"yield": 1}, "CO": {"yield": 1}, "HO2": {"yield": 1}, "irr__104": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"ALD2": {"yield": 1}, "NO2": {"yield": 1}, "HO2": {"yield": 1}, "XO2": {"yield": 1}, "irr__105": {"yield": 1}}}, {"type": "TROE", "k0_A": 2.7e-28, "k0_B": -7.1, "k0_C": 0, "kinf_A": 1.2e-11, "kinf_B": -0.9, "kinf_C": 0, "Fc": 0.3, "N": 1, "reactants": {"CXO3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"PANX": {"yield": 1}, "irr__106": {"yield": 1}}}, {"type": "TROE", "k0_A": 0.0049, "k0_B": 0, "k0_C": -12100, "kinf_A": 54000000000000000, "kinf_B": 0, "kinf_C": -13830, "Fc": 0.3, "N": 1, "reactants": {"PANX": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "NO2": {"yield": 1}, "irr__107": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PANX", "reactants": {"PANX": {}}, "products": {"CXO3": {"yield": 1}, "NO2": {"yield": 1}, "irr__108": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"PANX": {"qty": 1}, "OH": {"qty": 1}}, "products": {"ALD2": {"yield": 1}, "NO2": {"yield": 1}, "irr__109": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"PACD": {"yield": 0.8}, "AACD": {"yield": 0.2}, "O3": {"yield": 0.2}, "irr__110": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "MEO2": {"qty": 1}}, "products": {"ALD2": {"yield": 0.9}, "XO2": {"yield": 0.9}, "HO2": {"yield": 1}, "AACD": {"yield": 0.1}, "FORM": {"yield": 0.1}, "irr__111": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "XO2": {"qty": 1}}, "products": {"ALD2": {"yield": 0.9}, "AACD": {"yield": 0.1}, "irr__112": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 2}}, "products": {"ALD2": {"yield": 2}, "XO2": {"yield": 2}, "HO2": {"yield": 2}, "irr__113": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "C2O3": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "XO2": {"yield": 1}, "HO2": {"yield": 1}, "ALD2": {"yield": 1}, "irr__114": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.1e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"PAR": {"qty": 1}, "OH": {"qty": 1}}, "products": {"XO2": {"yield": 0.87}, "XO2N": {"yield": 0.13}, "HO2": {"yield": 0.11}, "ALD2": {"yield": 0.06}, "PAR": {"yield": -0.11}, "ROR": {"yield": 0.76}, "ALDX": {"yield": 0.05}, "irr__115": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1000000000000000, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROR": {"qty": 1}}, "products": {"XO2": {"yield": 0.96}, "ALD2": {"yield": 0.6}, "HO2": {"yield": 0.94}, "PAR": {"yield": -2.1}, "XO2N": {"yield": 0.04}, "ROR": {"yield": 0.02}, "ALDX": {"yield": 0.5}, "irr__116": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1600, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROR": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__117": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROR": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NTR": {"yield": 1}, "irr__118": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"ALD2": {"yield": 0.2}, "ALDX": {"yield": 0.3}, "HO2": {"yield": 0.3}, "XO2": {"yield": 0.2}, "CO": {"yield": 0.2}, "FORM": {"yield": 0.2}, "XO2N": {"yield": 0.01}, "PAR": {"yield": 0.2}, "OH": {"yield": 0.1}, "irr__119": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"FORM": {"yield": 0.8}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.62}, "XO2": {"yield": 0.8}, "HO2": {"yield": 0.95}, "PAR": {"yield": -0.7}, "irr__120": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.5e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"ALD2": {"yield": 0.18}, "FORM": {"yield": 0.74}, "ALDX": {"yield": 0.32}, "XO2": {"yield": 0.22}, "OH": {"yield": 0.1}, "CO": {"yield": 0.33}, "HO2": {"yield": 0.44}, "PAR": {"yield": -1}, "irr__121": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "FORM": {"yield": 1}, "XO2": {"yield": 0.91}, "XO2N": {"yield": 0.09}, "ALDX": {"yield": 0.56}, "ALD2": {"yield": 0.35}, "PAR": {"yield": -1}, "irr__122": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.04e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1.7}, "CO": {"yield": 1}, "XO2": {"yield": 0.7}, "OH": {"yield": 0.3}, "irr__123": {"yield": 1}}}, {"type": "TROE", "k0_A": 1e-28, "k0_B": -0.8, "k0_C": 0, "kinf_A": 8.8e-12, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"OH": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "FORM": {"yield": 1.56}, "ALDX": {"yield": 0.22}, "HO2": {"yield": 1}, "irr__124": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.2e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "CO": {"yield": 0.63}, "HO2": {"yield": 0.13}, "OH": {"yield": 0.13}, "FACD": {"yield": 0.37}, "irr__125": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "XO2": {"yield": 1}, "FORM": {"yield": 2}, "irr__126": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "O": {"qty": 1}}, "products": {"ALD2": {"yield": 1.24}, "ALDX": {"yield": 0.66}, "HO2": {"yield": 0.1}, "XO2": {"yield": 0.1}, "CO": {"yield": 0.1}, "PAR": {"yield": 0.1}, "irr__127": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "OH": {"qty": 1}}, "products": {"ALD2": {"yield": 1.3}, "ALDX": {"yield": 0.7}, "HO2": {"yield": 1}, "XO2": {"yield": 1}, "irr__128": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.4e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "O3": {"qty": 1}}, "products": {"ALD2": {"yield": 0.65}, "ALDX": {"yield": 0.35}, "FORM": {"yield": 0.25}, "CO": {"yield": 0.25}, "O": {"yield": 0.5}, "OH": {"yield": 0.5}, "HO2": {"yield": 0.5}, "irr__129": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9.6e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"ALD2": {"yield": 1.18}, "ALDX": {"yield": 0.64}, "HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__130": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TOL": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 0.44}, "XO2": {"yield": 0.08}, "CRES": {"yield": 0.36}, "TO2": {"yield": 0.56}, "TOLRO2": {"yield": 0.765}, "irr__131": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.1e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 0.9}, "HO2": {"yield": 0.9}, "OPEN": {"yield": 0.9}, "NTR": {"yield": 0.1}, "irr__132": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.2, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TO2": {"qty": 1}}, "products": {"CRES": {"yield": 1}, "HO2": {"yield": 1}, "irr__133": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CRES": {"qty": 1}}, "products": {"CRO": {"yield": 0.4}, "XO2": {"yield": 0.6}, "HO2": {"yield": 0.6}, "OPEN": {"yield": 0.3}, "irr__134": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CRES": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"CRO": {"yield": 1}, "HNO3": {"yield": 1}, "irr__135": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.4e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CRO": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NTR": {"yield": 1}, "irr__136": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CRO": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"CRES": {"yield": 1}, "irr__137": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 9, "MUSICA name": "OPEN", "reactants": {"OPEN": {}}, "products": {"C2O3": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__138": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OPEN": {"qty": 1}, "OH": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "CO": {"yield": 2}, "HO2": {"yield": 2}, "C2O3": {"yield": 1}, "FORM": {"yield": 1}, "irr__139": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.4e-17, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OPEN": {"qty": 1}, "O3": {"qty": 1}}, "products": {"ALDX": {"yield": 0.03}, "C2O3": {"yield": 0.62}, "FORM": {"yield": 0.7}, "XO2": {"yield": 0.03}, "CO": {"yield": 0.69}, "OH": {"yield": 0.08}, "HO2": {"yield": 0.76}, "MGLY": {"yield": 0.2}, "irr__140": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.7e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "XYL": {"qty": 1}}, "products": {"HO2": {"yield": 0.7}, "XO2": {"yield": 0.5}, "CRES": {"yield": 0.2}, "MGLY": {"yield": 0.8}, "PAR": {"yield": 1.1}, "TO2": {"yield": 0.3}, "XYLRO2": {"yield": 0.804}, "irr__141": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "MGLY": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "C2O3": {"yield": 1}, "irr__142": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "MGLY", "reactants": {"MGLY": {}}, "products": {"C2O3": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__143": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.6e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.75}, "FORM": {"yield": 0.5}, "XO2": {"yield": 0.25}, "HO2": {"yield": 0.25}, "CXO3": {"yield": 0.25}, "PAR": {"yield": 0.25}, "irr__144": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.54e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.912}, "FORM": {"yield": 0.629}, "XO2": {"yield": 0.991}, "HO2": {"yield": 0.912}, "XO2N": {"yield": 0.088}, "irr__145": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.86e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.65}, "FORM": {"yield": 0.6}, "XO2": {"yield": 0.2}, "HO2": {"yield": 0.066}, "OH": {"yield": 0.266}, "CXO3": {"yield": 0.2}, "ALDX": {"yield": 0.15}, "PAR": {"yield": 0.35}, "CO": {"yield": 0.066}, "irr__146": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.03e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.2}, "NTR": {"yield": 0.8}, "XO2": {"yield": 1}, "HO2": {"yield": 0.8}, "NO2": {"yield": 0.2}, "ALDX": {"yield": 0.8}, "PAR": {"yield": 2.4}, "irr__147": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.36e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ISPD": {"qty": 1}}, "products": {"PAR": {"yield": 1.565}, "FORM": {"yield": 0.167}, "XO2": {"yield": 0.713}, "HO2": {"yield": 0.503}, "CO": {"yield": 0.334}, "MGLY": {"yield": 0.168}, "ALD2": {"yield": 0.252}, "C2O3": {"yield": 0.21}, "CXO3": {"yield": 0.25}, "ALDX": {"yield": 0.12}, "irr__148": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.1e-18, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "ISPD": {"qty": 1}}, "products": {"C2O3": {"yield": 0.114}, "FORM": {"yield": 0.15}, "MGLY": {"yield": 0.85}, "HO2": {"yield": 0.154}, "OH": {"yield": 0.268}, "XO2": {"yield": 0.064}, "ALD2": {"yield": 0.02}, "PAR": {"yield": 0.36}, "CO": {"yield": 0.225}, "irr__149": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "ISPD": {"qty": 1}}, "products": {"ALDX": {"yield": 0.357}, "FORM": {"yield": 0.282}, "PAR": {"yield": 1.282}, "HO2": {"yield": 0.925}, "CO": {"yield": 0.643}, "NTR": {"yield": 0.85}, "CXO3": {"yield": 0.075}, "XO2": {"yield": 0.075}, "HNO3": {"yield": 0.15}, "irr__150": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 0.0036, "MUSICA name": "ISPD", "reactants": {"ISPD": {}}, "products": {"CO": {"yield": 0.333}, "ALD2": {"yield": 0.067}, "FORM": {"yield": 0.9}, "PAR": {"yield": 0.832}, "HO2": {"yield": 1.033}, "XO2": {"yield": 0.7}, "C2O3": {"yield": 0.967}, "irr__151": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.6e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "O": {"qty": 1}}, "products": {"ALDX": {"yield": 0.15}, "PAR": {"yield": 5.12}, "irr__152": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 0.75}, "XO2": {"yield": 1.25}, "XO2N": {"yield": 0.25}, "FORM": {"yield": 0.28}, "PAR": {"yield": 1.66}, "ALDX": {"yield": 0.47}, "irr__153": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.2e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "O3": {"qty": 1}}, "products": {"OH": {"yield": 0.57}, "HO2": {"yield": 0.07}, "XO2": {"yield": 0.76}, "XO2N": {"yield": 0.18}, "FORM": {"yield": 0.24}, "CO": {"yield": 0.001}, "PAR": {"yield": 7}, "ALDX": {"yield": 0.21}, "CXO3": {"yield": 0.39}, "irr__154": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"NO2": {"yield": 0.47}, "HO2": {"yield": 0.28}, "XO2": {"yield": 1.03}, "XO2N": {"yield": 0.25}, "ALDX": {"yield": 0.47}, "NTR": {"yield": 0.53}, "irr__155": {"yield": 1}}}, {"type": "TROE", "k0_A": 3e-31, "k0_B": -3.3, "k0_C": 0, "kinf_A": 1.5e-12, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"SO2": {"qty": 1}, "OH": {"qty": 1}}, "products": {"SULF": {"yield": 1}, "HO2": {"yield": 1}, "irr__156": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ETOH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "ALD2": {"yield": 0.9}, "ALDX": {"yield": 0.05}, "FORM": {"yield": 0.1}, "XO2": {"yield": 0.1}, "irr__157": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ETHA": {"qty": 1}}, "products": {"ALD2": {"yield": 0.991}, "XO2": {"yield": 0.991}, "XO2N": {"yield": 0.009}, "HO2": {"yield": 1}, "irr__158": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-19, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO2": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.2}, "NTR": {"yield": 0.8}, "XO2": {"yield": 1}, "HO2": {"yield": 0.8}, "NO": {"yield": 0.2}, "ALDX": {"yield": 0.8}, "PAR": {"yield": 2.4}, "irr__159": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "CL2", "reactants": {"CL2": {}}, "products": {"CL": {"yield": 2}, "irr__160": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "O3": {"qty": 1}}, "products": {"CLO": {"yield": 1}, "irr__161": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.63e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CLO": {"qty": 2}}, "products": {"CL2": {"yield": 0.3}, "CL": {"yield": 1.4}, "irr__162": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.4e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CLO": {"qty": 1}, "NO": {"qty": 1}}, "products": {"CL": {"yield": 1}, "NO2": {"yield": 1}, "irr__163": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CLO": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HOCL": {"yield": 1}, "irr__164": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "FMCL": {"qty": 1}}, "products": {"CL": {"yield": 1}, "CO": {"yield": 1}, "irr__165": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "CH4": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "MEO2": {"yield": 1}, "irr__166": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "PAR": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "XO2": {"yield": 0.87}, "XO2N": {"yield": 0.13}, "HO2": {"yield": 0.11}, "ALD2": {"yield": 0.06}, "PAR": {"yield": -0.11}, "ROR": {"yield": 0.76}, "ALDX": {"yield": 0.05}, "irr__167": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ETHA": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "ALD2": {"yield": 0.991}, "XO2": {"yield": 0.991}, "XO2N": {"yield": 0.009}, "HO2": {"yield": 1}, "irr__168": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.07e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"FMCL": {"yield": 1}, "XO2": {"yield": 2}, "HO2": {"yield": 1}, "FORM": {"yield": 1}, "irr__169": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.5e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"FMCL": {"yield": 1}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.67}, "XO2": {"yield": 2}, "HO2": {"yield": 1}, "PAR": {"yield": -1}, "irr__170": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.5e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "IOLE": {"qty": 1}}, "products": {"HCL": {"yield": 0.3}, "FMCL": {"yield": 0.7}, "ALD2": {"yield": 0.45}, "ALDX": {"yield": 0.55}, "OLE": {"yield": 0.3}, "PAR": {"yield": 0.3}, "XO2": {"yield": 1.7}, "HO2": {"yield": 1}, "irr__171": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.3e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"HCL": {"yield": 0.15}, "XO2": {"yield": 1}, "HO2": {"yield": 1}, "FMCL": {"yield": 0.85}, "ISPD": {"yield": 1}, "irr__172": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "FORM": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__173": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.9e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ALD2": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "C2O3": {"yield": 1}, "irr__174": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.3e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ALDX": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "CXO3": {"yield": 1}, "irr__175": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "MEOH": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "HO2": {"yield": 1}, "FORM": {"yield": 1}, "irr__176": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ETOH": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "HO2": {"yield": 1}, "ALD2": {"yield": 1}, "irr__177": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.58e-13, "Ea": 0, "B": 1.16, "D": 300, "E": 0, "reactants": {"HCL": {"qty": 1}, "OH": {"qty": 1}}, "products": {"CL": {"yield": 1}, "irr__178": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TOLRO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__179": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TOLRO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__180": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XYLRO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__181": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XYLRO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__182": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.47e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"BENZENE": {"qty": 1}, "OH": {"qty": 1}}, "products": {"OH": {"yield": 1}, "BENZRO2": {"yield": 0.764}, "irr__183": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"BENZRO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__184": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"BENZRO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__185": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.16e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"SESQ": {"qty": 1}, "O3": {"qty": 1}}, "products": {"O3": {"yield": 1}, "irr__186": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.97e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"SESQ": {"qty": 1}, "OH": {"qty": 1}}, "products": {"OH": {"yield": 1}, "irr__187": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"SESQ": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__188": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "O2", "reactants": {"O2": {}}, "products": {"O": {"yield": 2}, "irr__189": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "NO", "species": "NO", "products": {"irr__190": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "NO2", "species": "NO2", "products": {"irr__191": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "CO", "species": "CO", "products": {"irr__192": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "SO2", "species": "SO2", "products": {"irr__193": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "FORM", "species": "FORM", "products": {"irr__194": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "MEOH", "species": "MEOH", "products": {"irr__195": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "ALD2", "species": "ALD2", "products": {"irr__196": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "PAR", "species": "PAR", "products": {"irr__197": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "ETH", "species": "ETH", "products": {"irr__198": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "OLE", "species": "OLE", "products": {"irr__199": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "IOLE", "species": "IOLE", "products": {"irr__200": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "TOL", "species": "TOL", "products": {"irr__201": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "XYL", "species": "XYL", "products": {"irr__202": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "ISOP", "species": "ISOP", "products": {"irr__203": {"yield": 1}}}]}]} \ No newline at end of file diff --git a/test/tutorial/configs/carbon_bond_5/species.json b/test/tutorial/configs/carbon_bond_5/species.json new file mode 100644 index 000000000..8a101b2be --- /dev/null +++ b/test/tutorial/configs/carbon_bond_5/species.json @@ -0,0 +1 @@ +{"camp-data": [{"name": "AACD", "type": "CHEM_SPEC"}, {"name": "ALD2", "type": "CHEM_SPEC"}, {"name": "ALDX", "type": "CHEM_SPEC"}, {"name": "BENZENE", "type": "CHEM_SPEC"}, {"name": "BENZRO2", "type": "CHEM_SPEC"}, {"name": "C2O3", "type": "CHEM_SPEC"}, {"name": "CH4", "type": "CHEM_SPEC"}, {"name": "CL", "type": "CHEM_SPEC"}, {"name": "CL2", "type": "CHEM_SPEC"}, {"name": "CLO", "type": "CHEM_SPEC"}, {"name": "CO", "type": "CHEM_SPEC"}, {"name": "CRES", "type": "CHEM_SPEC"}, {"name": "CRO", "type": "CHEM_SPEC"}, {"name": "CXO3", "type": "CHEM_SPEC"}, {"name": "ETH", "type": "CHEM_SPEC"}, {"name": "ETHA", "type": "CHEM_SPEC"}, {"name": "ETOH", "type": "CHEM_SPEC"}, {"name": "FACD", "type": "CHEM_SPEC"}, {"name": "FMCL", "type": "CHEM_SPEC"}, {"name": "FORM", "type": "CHEM_SPEC"}, {"name": "H2", "type": "CHEM_SPEC"}, {"name": "H2O", "type": "CHEM_SPEC"}, {"name": "H2O2", "type": "CHEM_SPEC"}, {"name": "HCL", "type": "CHEM_SPEC"}, {"name": "HCO3", "type": "CHEM_SPEC"}, {"name": "HNO3", "type": "CHEM_SPEC"}, {"name": "HO2", "type": "CHEM_SPEC"}, {"name": "HOCL", "type": "CHEM_SPEC"}, {"name": "HONO", "type": "CHEM_SPEC"}, {"name": "IOLE", "type": "CHEM_SPEC"}, {"name": "ISOP", "type": "CHEM_SPEC"}, {"name": "ISPD", "type": "CHEM_SPEC"}, {"name": "M", "type": "CHEM_SPEC"}, {"name": "MEO2", "type": "CHEM_SPEC"}, {"name": "MEOH", "type": "CHEM_SPEC"}, {"name": "MEPX", "type": "CHEM_SPEC"}, {"name": "MGLY", "type": "CHEM_SPEC"}, {"name": "N2O5", "type": "CHEM_SPEC"}, {"name": "NO", "type": "CHEM_SPEC"}, {"name": "NO2", "type": "CHEM_SPEC"}, {"name": "NO3", "type": "CHEM_SPEC"}, {"name": "NTR", "type": "CHEM_SPEC"}, {"name": "O", "type": "CHEM_SPEC"}, {"name": "O1D", "type": "CHEM_SPEC"}, {"name": "O3", "type": "CHEM_SPEC"}, {"name": "OH", "type": "CHEM_SPEC"}, {"name": "O2", "type": "CHEM_SPEC"}, {"name": "OLE", "type": "CHEM_SPEC"}, {"name": "OPEN", "type": "CHEM_SPEC"}, {"name": "PACD", "type": "CHEM_SPEC"}, {"name": "PAN", "type": "CHEM_SPEC"}, {"name": "PANX", "type": "CHEM_SPEC"}, {"name": "PAR", "type": "CHEM_SPEC"}, {"name": "PNA", "type": "CHEM_SPEC"}, {"name": "ROOH", "type": "CHEM_SPEC"}, {"name": "ROR", "type": "CHEM_SPEC"}, {"name": "SESQ", "type": "CHEM_SPEC"}, {"name": "SO2", "type": "CHEM_SPEC"}, {"name": "SULF", "type": "CHEM_SPEC"}, {"name": "TERP", "type": "CHEM_SPEC"}, {"name": "TO2", "type": "CHEM_SPEC"}, {"name": "TOL", "type": "CHEM_SPEC"}, {"name": "TOLRO2", "type": "CHEM_SPEC"}, {"name": "XO2", "type": "CHEM_SPEC"}, {"name": "XO2N", "type": "CHEM_SPEC"}, {"name": "XYL", "type": "CHEM_SPEC"}, {"name": "XYLRO2", "type": "CHEM_SPEC"}, {"name": "irr__0", "type": "CHEM_SPEC"}, {"name": "irr__1", "type": "CHEM_SPEC"}, {"name": "irr__2", "type": "CHEM_SPEC"}, {"name": "irr__3", "type": "CHEM_SPEC"}, {"name": "irr__4", "type": "CHEM_SPEC"}, {"name": "irr__5", "type": "CHEM_SPEC"}, {"name": "irr__6", "type": "CHEM_SPEC"}, {"name": "irr__7", "type": "CHEM_SPEC"}, {"name": "irr__8", "type": "CHEM_SPEC"}, {"name": "irr__9", "type": "CHEM_SPEC"}, {"name": "irr__10", "type": "CHEM_SPEC"}, {"name": "irr__11", "type": "CHEM_SPEC"}, {"name": "irr__12", "type": "CHEM_SPEC"}, {"name": "irr__13", "type": "CHEM_SPEC"}, {"name": "irr__14", "type": "CHEM_SPEC"}, {"name": "irr__15", "type": "CHEM_SPEC"}, {"name": "irr__16", "type": "CHEM_SPEC"}, {"name": "irr__17", "type": "CHEM_SPEC"}, {"name": "irr__18", "type": "CHEM_SPEC"}, {"name": "irr__19", "type": "CHEM_SPEC"}, {"name": "irr__20", "type": "CHEM_SPEC"}, {"name": "irr__21", "type": "CHEM_SPEC"}, {"name": "irr__22", "type": "CHEM_SPEC"}, {"name": "irr__23", "type": "CHEM_SPEC"}, {"name": "irr__24", "type": "CHEM_SPEC"}, {"name": "irr__25", "type": "CHEM_SPEC"}, {"name": "irr__26", "type": "CHEM_SPEC"}, {"name": "irr__27", "type": "CHEM_SPEC"}, {"name": "irr__28", "type": "CHEM_SPEC"}, {"name": "irr__29", "type": "CHEM_SPEC"}, {"name": "irr__30", "type": "CHEM_SPEC"}, {"name": "irr__31", "type": "CHEM_SPEC"}, {"name": "irr__32", "type": "CHEM_SPEC"}, {"name": "irr__33", "type": "CHEM_SPEC"}, {"name": "irr__34", "type": "CHEM_SPEC"}, {"name": "irr__35", "type": "CHEM_SPEC"}, {"name": "irr__36", "type": "CHEM_SPEC"}, {"name": "irr__37", "type": "CHEM_SPEC"}, {"name": "irr__38", "type": "CHEM_SPEC"}, {"name": "irr__39", "type": "CHEM_SPEC"}, {"name": "irr__40", "type": "CHEM_SPEC"}, {"name": "irr__41", "type": "CHEM_SPEC"}, {"name": "irr__42", "type": "CHEM_SPEC"}, {"name": "irr__43", "type": "CHEM_SPEC"}, {"name": "irr__44", "type": "CHEM_SPEC"}, {"name": "irr__45", "type": "CHEM_SPEC"}, {"name": "irr__46", "type": "CHEM_SPEC"}, {"name": "irr__47", "type": "CHEM_SPEC"}, {"name": "irr__48", "type": "CHEM_SPEC"}, {"name": "irr__49", "type": "CHEM_SPEC"}, {"name": "irr__50", "type": "CHEM_SPEC"}, {"name": "irr__51", "type": "CHEM_SPEC"}, {"name": "irr__52", "type": "CHEM_SPEC"}, {"name": "irr__53", "type": "CHEM_SPEC"}, {"name": "irr__54", "type": "CHEM_SPEC"}, {"name": "irr__55", "type": "CHEM_SPEC"}, {"name": "irr__56", "type": "CHEM_SPEC"}, {"name": "irr__57", "type": "CHEM_SPEC"}, {"name": "irr__58", "type": "CHEM_SPEC"}, {"name": "irr__59", "type": "CHEM_SPEC"}, {"name": "irr__60", "type": "CHEM_SPEC"}, {"name": "irr__61", "type": "CHEM_SPEC"}, {"name": "irr__62", "type": "CHEM_SPEC"}, {"name": "irr__63", "type": "CHEM_SPEC"}, {"name": "irr__64", "type": "CHEM_SPEC"}, {"name": "irr__65", "type": "CHEM_SPEC"}, {"name": "irr__66", "type": "CHEM_SPEC"}, {"name": "irr__67", "type": "CHEM_SPEC"}, {"name": "irr__68", "type": "CHEM_SPEC"}, {"name": "irr__69", "type": "CHEM_SPEC"}, {"name": "irr__70", "type": "CHEM_SPEC"}, {"name": "irr__71", "type": "CHEM_SPEC"}, {"name": "irr__72", "type": "CHEM_SPEC"}, {"name": "irr__73", "type": "CHEM_SPEC"}, {"name": "irr__74", "type": "CHEM_SPEC"}, {"name": "irr__75", "type": "CHEM_SPEC"}, {"name": "irr__76", "type": "CHEM_SPEC"}, {"name": "irr__77", "type": "CHEM_SPEC"}, {"name": "irr__78", "type": "CHEM_SPEC"}, {"name": "irr__79", "type": "CHEM_SPEC"}, {"name": "irr__80", "type": "CHEM_SPEC"}, {"name": "irr__81", "type": "CHEM_SPEC"}, {"name": "irr__82", "type": "CHEM_SPEC"}, {"name": "irr__83", "type": "CHEM_SPEC"}, {"name": "irr__84", "type": "CHEM_SPEC"}, {"name": "irr__85", "type": "CHEM_SPEC"}, {"name": "irr__86", "type": "CHEM_SPEC"}, {"name": "irr__87", "type": "CHEM_SPEC"}, {"name": "irr__88", "type": "CHEM_SPEC"}, {"name": "irr__89", "type": "CHEM_SPEC"}, {"name": "irr__90", "type": "CHEM_SPEC"}, {"name": "irr__91", "type": "CHEM_SPEC"}, {"name": "irr__92", "type": "CHEM_SPEC"}, {"name": "irr__93", "type": "CHEM_SPEC"}, {"name": "irr__94", "type": "CHEM_SPEC"}, {"name": "irr__95", "type": "CHEM_SPEC"}, {"name": "irr__96", "type": "CHEM_SPEC"}, {"name": "irr__97", "type": "CHEM_SPEC"}, {"name": "irr__98", "type": "CHEM_SPEC"}, {"name": "irr__99", "type": "CHEM_SPEC"}, {"name": "irr__100", "type": "CHEM_SPEC"}, {"name": "irr__101", "type": "CHEM_SPEC"}, {"name": "irr__102", "type": "CHEM_SPEC"}, {"name": "irr__103", "type": "CHEM_SPEC"}, {"name": "irr__104", "type": "CHEM_SPEC"}, {"name": "irr__105", "type": "CHEM_SPEC"}, {"name": "irr__106", "type": "CHEM_SPEC"}, {"name": "irr__107", "type": "CHEM_SPEC"}, {"name": "irr__108", "type": "CHEM_SPEC"}, {"name": "irr__109", "type": "CHEM_SPEC"}, {"name": "irr__110", "type": "CHEM_SPEC"}, {"name": "irr__111", "type": "CHEM_SPEC"}, {"name": "irr__112", "type": "CHEM_SPEC"}, {"name": "irr__113", "type": "CHEM_SPEC"}, {"name": "irr__114", "type": "CHEM_SPEC"}, {"name": "irr__115", "type": "CHEM_SPEC"}, {"name": "irr__116", "type": "CHEM_SPEC"}, {"name": "irr__117", "type": "CHEM_SPEC"}, {"name": "irr__118", "type": "CHEM_SPEC"}, {"name": "irr__119", "type": "CHEM_SPEC"}, {"name": "irr__120", "type": "CHEM_SPEC"}, {"name": "irr__121", "type": "CHEM_SPEC"}, {"name": "irr__122", "type": "CHEM_SPEC"}, {"name": "irr__123", "type": "CHEM_SPEC"}, {"name": "irr__124", "type": "CHEM_SPEC"}, {"name": "irr__125", "type": "CHEM_SPEC"}, {"name": "irr__126", "type": "CHEM_SPEC"}, {"name": "irr__127", "type": "CHEM_SPEC"}, {"name": "irr__128", "type": "CHEM_SPEC"}, {"name": "irr__129", "type": "CHEM_SPEC"}, {"name": "irr__130", "type": "CHEM_SPEC"}, {"name": "irr__131", "type": "CHEM_SPEC"}, {"name": "irr__132", "type": "CHEM_SPEC"}, {"name": "irr__133", "type": "CHEM_SPEC"}, {"name": "irr__134", "type": "CHEM_SPEC"}, {"name": "irr__135", "type": "CHEM_SPEC"}, {"name": "irr__136", "type": "CHEM_SPEC"}, {"name": "irr__137", "type": "CHEM_SPEC"}, {"name": "irr__138", "type": "CHEM_SPEC"}, {"name": "irr__139", "type": "CHEM_SPEC"}, {"name": "irr__140", "type": "CHEM_SPEC"}, {"name": "irr__141", "type": "CHEM_SPEC"}, {"name": "irr__142", "type": "CHEM_SPEC"}, {"name": "irr__143", "type": "CHEM_SPEC"}, {"name": "irr__144", "type": "CHEM_SPEC"}, {"name": "irr__145", "type": "CHEM_SPEC"}, {"name": "irr__146", "type": "CHEM_SPEC"}, {"name": "irr__147", "type": "CHEM_SPEC"}, {"name": "irr__148", "type": "CHEM_SPEC"}, {"name": "irr__149", "type": "CHEM_SPEC"}, {"name": "irr__150", "type": "CHEM_SPEC"}, {"name": "irr__151", "type": "CHEM_SPEC"}, {"name": "irr__152", "type": "CHEM_SPEC"}, {"name": "irr__153", "type": "CHEM_SPEC"}, {"name": "irr__154", "type": "CHEM_SPEC"}, {"name": "irr__155", "type": "CHEM_SPEC"}, {"name": "irr__156", "type": "CHEM_SPEC"}, {"name": "irr__157", "type": "CHEM_SPEC"}, {"name": "irr__158", "type": "CHEM_SPEC"}, {"name": "irr__159", "type": "CHEM_SPEC"}, {"name": "irr__160", "type": "CHEM_SPEC"}, {"name": "irr__161", "type": "CHEM_SPEC"}, {"name": "irr__162", "type": "CHEM_SPEC"}, {"name": "irr__163", "type": "CHEM_SPEC"}, {"name": "irr__164", "type": "CHEM_SPEC"}, {"name": "irr__165", "type": "CHEM_SPEC"}, {"name": "irr__166", "type": "CHEM_SPEC"}, {"name": "irr__167", "type": "CHEM_SPEC"}, {"name": "irr__168", "type": "CHEM_SPEC"}, {"name": "irr__169", "type": "CHEM_SPEC"}, {"name": "irr__170", "type": "CHEM_SPEC"}, {"name": "irr__171", "type": "CHEM_SPEC"}, {"name": "irr__172", "type": "CHEM_SPEC"}, {"name": "irr__173", "type": "CHEM_SPEC"}, {"name": "irr__174", "type": "CHEM_SPEC"}, {"name": "irr__175", "type": "CHEM_SPEC"}, {"name": "irr__176", "type": "CHEM_SPEC"}, {"name": "irr__177", "type": "CHEM_SPEC"}, {"name": "irr__178", "type": "CHEM_SPEC"}, {"name": "irr__179", "type": "CHEM_SPEC"}, {"name": "irr__180", "type": "CHEM_SPEC"}, {"name": "irr__181", "type": "CHEM_SPEC"}, {"name": "irr__182", "type": "CHEM_SPEC"}, {"name": "irr__183", "type": "CHEM_SPEC"}, {"name": "irr__184", "type": "CHEM_SPEC"}, {"name": "irr__185", "type": "CHEM_SPEC"}, {"name": "irr__186", "type": "CHEM_SPEC"}, {"name": "irr__187", "type": "CHEM_SPEC"}, {"name": "irr__188", "type": "CHEM_SPEC"}, {"name": "irr__189", "type": "CHEM_SPEC"}, {"name": "irr__190", "type": "CHEM_SPEC"}, {"name": "irr__191", "type": "CHEM_SPEC"}, {"name": "irr__192", "type": "CHEM_SPEC"}, {"name": "irr__193", "type": "CHEM_SPEC"}, {"name": "irr__194", "type": "CHEM_SPEC"}, {"name": "irr__195", "type": "CHEM_SPEC"}, {"name": "irr__196", "type": "CHEM_SPEC"}, {"name": "irr__197", "type": "CHEM_SPEC"}, {"name": "irr__198", "type": "CHEM_SPEC"}, {"name": "irr__199", "type": "CHEM_SPEC"}, {"name": "irr__200", "type": "CHEM_SPEC"}, {"name": "irr__201", "type": "CHEM_SPEC"}, {"name": "irr__202", "type": "CHEM_SPEC"}, {"name": "irr__203", "type": "CHEM_SPEC"}]} \ No newline at end of file diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp new file mode 100644 index 000000000..c32d858dc --- /dev/null +++ b/test/tutorial/test_jit_tutorial.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include + +// Each rate constant is in its own header file +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; + +template +auto run_solver(T& solver) +{ + typename T::SolverStats total_stats; + State state = solver.GetState(); + + state.variables_ = 1; + + state.conditions_[0].temperature_ = 287.45; // K + state.conditions_[0].pressure_ = 101319.9; // Pa + + state.SetCustomRateParameter("EMIS.NO", 1.44e-10); + state.SetCustomRateParameter("EMIS.NO2", 7.56e-12); + state.SetCustomRateParameter("EMIS.CO", 1.9600000000000003e-09); + state.SetCustomRateParameter("EMIS.SO2", 1.06e-09); + state.SetCustomRateParameter("EMIS.FORM", 1.02e-11); + state.SetCustomRateParameter("EMIS.MEOH", 5.920000000000001e-13); + state.SetCustomRateParameter("EMIS.ALD2", 4.25e-12); + state.SetCustomRateParameter("EMIS.PAR", 4.27e-10); + state.SetCustomRateParameter("EMIS.ETH", 4.62e-11); + state.SetCustomRateParameter("EMIS.OLE", 1.49e-11); + state.SetCustomRateParameter("EMIS.IOLE", 1.49e-11); + state.SetCustomRateParameter("EMIS.TOL", 1.53e-11); + state.SetCustomRateParameter("EMIS.XYL", 1.4e-11); + state.SetCustomRateParameter("EMIS.ISOP", 6.03e-12); + state.SetCustomRateParameter("PHOTO.NO2", 0.00477); + state.SetCustomRateParameter("PHOTO.O3->O1D", 2.26e-06); + state.SetCustomRateParameter("PHOTO.O3->O3P", 0.00025299999999999997); + state.SetCustomRateParameter("PHOTO.NO3->NO2", 0.11699999999999999); + state.SetCustomRateParameter("PHOTO.NO3->NO", 0.0144); + state.SetCustomRateParameter("PHOTO.HONO", 0.000918); + state.SetCustomRateParameter("PHOTO.H2O2", 2.59e-06); + state.SetCustomRateParameter("PHOTO.PNA", 1.89e-06); + state.SetCustomRateParameter("PHOTO.HNO3", 8.61e-08); + state.SetCustomRateParameter("PHOTO.NTR", 4.77e-07); + state.SetCustomRateParameter("PHOTO.ROOH", 1.81e-06); + state.SetCustomRateParameter("PHOTO.MEPX", 1.81e-06); + state.SetCustomRateParameter("PHOTO.FORM->HO2", 7.93e-06); + state.SetCustomRateParameter("PHOTO.FORM->CO", 2.2e-05); + state.SetCustomRateParameter("PHOTO.ALD2", 2.2e-06); + state.SetCustomRateParameter("PHOTO.PACD", 1.81e-06); + state.SetCustomRateParameter("PHOTO.ALDX", 2.2e-06); + state.SetCustomRateParameter("PHOTO.OPEN", 0.0006450000000000001); + state.SetCustomRateParameter("PHOTO.MGLY", 7.64e-05); + state.SetCustomRateParameter("PHOTO.ISPD", 1.98e-09); + + // choose a timestep and print the initial state + double time_step = 500; // s + + // solve for ten iterations + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.template Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + + total_stats.function_calls += result.stats_.function_calls; + total_stats.jacobian_updates += result.stats_.jacobian_updates; + total_stats.number_of_steps += result.stats_.number_of_steps; + total_stats.accepted += result.stats_.accepted; + total_stats.rejected += result.stats_.rejected; + total_stats.decompositions += result.stats_.decompositions; + total_stats.solves += result.stats_.solves; + total_stats.singular += result.stats_.singular; + total_stats.total_forcing_time += result.stats_.total_forcing_time; + total_stats.total_jacobian_time += result.stats_.total_jacobian_time; + total_stats.total_linear_factor_time += result.stats_.total_linear_factor_time; + total_stats.total_linear_solve_time += result.stats_.total_linear_solve_time; + } + } + + return std::make_pair(state, total_stats); +} + +int main(const int argc, const char* argv[]) +{ + SolverConfig solverConfig; + + std::string config_path = "./configs/carbon_bond_5"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + + auto jit{ micm::JitCompiler::create() }; + + auto start = std::chrono::high_resolution_clock::now(); + JitRosenbrockSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + JitLinearSolver<1, Group1SparseVectorMatrix> + > jit_solver( + jit.get(), + chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + ); + auto end = std::chrono::high_resolution_clock::now(); + auto jit_compile_time = std::chrono::duration_cast(end - start); + + std::cout << "Jit compile time: " << jit_compile_time.count() << " nanoseconds" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + auto result_pair = run_solver(solver); + end = std::chrono::high_resolution_clock::now(); + auto result_time = std::chrono::duration_cast(end - start); + + start = std::chrono::high_resolution_clock::now(); + auto jit_result_pair = run_solver(jit_solver); + end = std::chrono::high_resolution_clock::now(); + auto jit_time = std::chrono::duration_cast(end - start); + + std::cout << "Result time: " << result_time.count() << " nanoseconds" << std::endl; + std::cout << "JIT result time: " << jit_time.count() << " nanoseconds" << std::endl; + + auto result_stats = result_pair.second; + std::cout << "Result stats: " << std::endl; + std::cout << "accepted: " << result_stats.accepted << std::endl; + std::cout << "function_calls: " << result_stats.function_calls << std::endl; + std::cout << "jacobian_updates: " << result_stats.jacobian_updates << std::endl; + std::cout << "number_of_steps: " << result_stats.number_of_steps << std::endl; + std::cout << "accepted: " << result_stats.accepted << std::endl; + std::cout << "rejected: " << result_stats.rejected << std::endl; + std::cout << "decompositions: " << result_stats.decompositions << std::endl; + std::cout << "solves: " << result_stats.solves << std::endl; + std::cout << "singular: " << result_stats.singular << std::endl; + std::cout << "total_forcing_time: " << result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "total_jacobian_time: " << result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_factor_time: " << result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; + + auto jit_result_stats = jit_result_pair.second; + std::cout << "JIT result stats: " << std::endl; + std::cout << "accepted: " << jit_result_stats.accepted << std::endl; + std::cout << "function_calls: " << jit_result_stats.function_calls << std::endl; + std::cout << "jacobian_updates: " << jit_result_stats.jacobian_updates << std::endl; + std::cout << "number_of_steps: " << jit_result_stats.number_of_steps << std::endl; + std::cout << "accepted: " << jit_result_stats.accepted << std::endl; + std::cout << "rejected: " << jit_result_stats.rejected << std::endl; + std::cout << "decompositions: " << jit_result_stats.decompositions << std::endl; + std::cout << "solves: " << jit_result_stats.solves << std::endl; + std::cout << "singular: " << jit_result_stats.singular << std::endl; + std::cout << "total_forcing_time: " << jit_result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "total_jacobian_time: " << jit_result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_factor_time: " << jit_result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_solve_time: " << jit_result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; + + auto result = result_pair.first; + auto jit_result = result_pair.first; + + for(auto& species : result.variable_names_) { + if (result.variables_[0][result.variable_map_[species]] != jit_result.variables_[0][jit_result.variable_map_[species]]) { + std::cout << species << " do not match final concentration" << std::endl; + } + } + return 0; +} From 84b4c6d8d78c6d12405c031bc6695e4276009326 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 10:42:04 -0500 Subject: [PATCH 197/318] printing out all stats --- test/tutorial/test_jit_tutorial.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index c32d858dc..942827a77 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -27,6 +27,7 @@ auto run_solver(T& solver) state.conditions_[0].temperature_ = 287.45; // K state.conditions_[0].pressure_ = 101319.9; // Pa + state.conditions_[0].air_density_ = 1e6; // mol m-3 state.SetCustomRateParameter("EMIS.NO", 1.44e-10); state.SetCustomRateParameter("EMIS.NO2", 7.56e-12); From 4432d2117bd54aee203254014badd5c2844bc237 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 13:41:32 -0500 Subject: [PATCH 198/318] added more jit optimizations --- include/micm/jit/jit_compiler.hpp | 14 ++++ test/tutorial/test_jit_tutorial.cpp | 106 ++++++++++++++++------------ 2 files changed, 73 insertions(+), 47 deletions(-) diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 356f0fbf8..a85925f4a 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -24,10 +24,14 @@ #include "llvm/IR/DataLayout.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/PassManager.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Transforms/IPO.h" #include "llvm/Transforms/InstCombine/InstCombine.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Scalar/GVN.h" +#include "llvm/Transforms/Utils.h" +#include "llvm/Transforms/Vectorize.h" namespace micm { @@ -130,10 +134,20 @@ namespace micm auto pass_manager = std::make_unique(&module); // Add some optimizations. + // many from + // https://www.intel.com/content/www/us/en/developer/articles/technical/optimize-llvm-code-data-analytics-vectorization.html#gs.6xkals + // explanation of a few: https://seanforfun.github.io/llvm/2019/08/08/LLVMKaleidoscopeChap3.html + // pass_manager->add(llvm::createFunctionInliningPass()); // causes segfault + pass_manager->add(llvm::createLoopRotatePass()); + pass_manager->add(llvm::createLICMPass()); pass_manager->add(llvm::createInstructionCombiningPass()); pass_manager->add(llvm::createReassociatePass()); + pass_manager->add(llvm::createPromoteMemoryToRegisterPass()); pass_manager->add(llvm::createGVNPass()); pass_manager->add(llvm::createCFGSimplificationPass()); + pass_manager->add(llvm::createLoopVectorizePass()); + pass_manager->add(llvm::createSLPVectorizerPass()); + // pass_manager->add(llvm::createGlobalOptimizerPass()); // causes segfault pass_manager->doInitialization(); // Run the optimizations over all functions in the module being added to diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index 942827a77..7eaa260d4 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -12,10 +12,12 @@ // Use our namespace so that this example is easier to read using namespace micm; +constexpr size_t n_grid_cells = 1; + template -using Group1VectorMatrix = micm::VectorMatrix; +using GroupVectorMatrix = micm::VectorMatrix; template -using Group1SparseVectorMatrix = micm::SparseMatrix>; +using GroupSparseVectorMatrix = micm::SparseMatrix>; template auto run_solver(T& solver) @@ -25,44 +27,47 @@ auto run_solver(T& solver) state.variables_ = 1; - state.conditions_[0].temperature_ = 287.45; // K - state.conditions_[0].pressure_ = 101319.9; // Pa - state.conditions_[0].air_density_ = 1e6; // mol m-3 - - state.SetCustomRateParameter("EMIS.NO", 1.44e-10); - state.SetCustomRateParameter("EMIS.NO2", 7.56e-12); - state.SetCustomRateParameter("EMIS.CO", 1.9600000000000003e-09); - state.SetCustomRateParameter("EMIS.SO2", 1.06e-09); - state.SetCustomRateParameter("EMIS.FORM", 1.02e-11); - state.SetCustomRateParameter("EMIS.MEOH", 5.920000000000001e-13); - state.SetCustomRateParameter("EMIS.ALD2", 4.25e-12); - state.SetCustomRateParameter("EMIS.PAR", 4.27e-10); - state.SetCustomRateParameter("EMIS.ETH", 4.62e-11); - state.SetCustomRateParameter("EMIS.OLE", 1.49e-11); - state.SetCustomRateParameter("EMIS.IOLE", 1.49e-11); - state.SetCustomRateParameter("EMIS.TOL", 1.53e-11); - state.SetCustomRateParameter("EMIS.XYL", 1.4e-11); - state.SetCustomRateParameter("EMIS.ISOP", 6.03e-12); - state.SetCustomRateParameter("PHOTO.NO2", 0.00477); - state.SetCustomRateParameter("PHOTO.O3->O1D", 2.26e-06); - state.SetCustomRateParameter("PHOTO.O3->O3P", 0.00025299999999999997); - state.SetCustomRateParameter("PHOTO.NO3->NO2", 0.11699999999999999); - state.SetCustomRateParameter("PHOTO.NO3->NO", 0.0144); - state.SetCustomRateParameter("PHOTO.HONO", 0.000918); - state.SetCustomRateParameter("PHOTO.H2O2", 2.59e-06); - state.SetCustomRateParameter("PHOTO.PNA", 1.89e-06); - state.SetCustomRateParameter("PHOTO.HNO3", 8.61e-08); - state.SetCustomRateParameter("PHOTO.NTR", 4.77e-07); - state.SetCustomRateParameter("PHOTO.ROOH", 1.81e-06); - state.SetCustomRateParameter("PHOTO.MEPX", 1.81e-06); - state.SetCustomRateParameter("PHOTO.FORM->HO2", 7.93e-06); - state.SetCustomRateParameter("PHOTO.FORM->CO", 2.2e-05); - state.SetCustomRateParameter("PHOTO.ALD2", 2.2e-06); - state.SetCustomRateParameter("PHOTO.PACD", 1.81e-06); - state.SetCustomRateParameter("PHOTO.ALDX", 2.2e-06); - state.SetCustomRateParameter("PHOTO.OPEN", 0.0006450000000000001); - state.SetCustomRateParameter("PHOTO.MGLY", 7.64e-05); - state.SetCustomRateParameter("PHOTO.ISPD", 1.98e-09); + for(int i = 0; i < n_grid_cells; ++i) { + state.conditions_[i].temperature_ = 287.45; // K + state.conditions_[i].pressure_ = 101319.9; // Pa + state.conditions_[i].air_density_ = 1e6; // mol m-3 + } + + + state.SetCustomRateParameter("EMIS.NO", std::vector(solver.parameters_.number_of_grid_cells_, 1.44e-10)); + state.SetCustomRateParameter("EMIS.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.56e-12)); + state.SetCustomRateParameter("EMIS.CO", std::vector(solver.parameters_.number_of_grid_cells_, 1.9600000000000003e-09)); + state.SetCustomRateParameter("EMIS.SO2", std::vector(solver.parameters_.number_of_grid_cells_, 1.06e-09)); + state.SetCustomRateParameter("EMIS.FORM", std::vector(solver.parameters_.number_of_grid_cells_, 1.02e-11)); + state.SetCustomRateParameter("EMIS.MEOH", std::vector(solver.parameters_.number_of_grid_cells_, 5.920000000000001e-13)); + state.SetCustomRateParameter("EMIS.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 4.25e-12)); + state.SetCustomRateParameter("EMIS.PAR", std::vector(solver.parameters_.number_of_grid_cells_, 4.27e-10)); + state.SetCustomRateParameter("EMIS.ETH", std::vector(solver.parameters_.number_of_grid_cells_, 4.62e-11)); + state.SetCustomRateParameter("EMIS.OLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); + state.SetCustomRateParameter("EMIS.IOLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); + state.SetCustomRateParameter("EMIS.TOL", std::vector(solver.parameters_.number_of_grid_cells_, 1.53e-11)); + state.SetCustomRateParameter("EMIS.XYL", std::vector(solver.parameters_.number_of_grid_cells_, 1.4e-11)); + state.SetCustomRateParameter("EMIS.ISOP", std::vector(solver.parameters_.number_of_grid_cells_, 6.03e-12)); + state.SetCustomRateParameter("PHOTO.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.00477)); + state.SetCustomRateParameter("PHOTO.O3->O1D", std::vector(solver.parameters_.number_of_grid_cells_, 2.26e-06)); + state.SetCustomRateParameter("PHOTO.O3->O3P", std::vector(solver.parameters_.number_of_grid_cells_, 0.00025299999999999997)); + state.SetCustomRateParameter("PHOTO.NO3->NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.11699999999999999)); + state.SetCustomRateParameter("PHOTO.NO3->NO", std::vector(solver.parameters_.number_of_grid_cells_, 0.0144)); + state.SetCustomRateParameter("PHOTO.HONO", std::vector(solver.parameters_.number_of_grid_cells_, 0.000918)); + state.SetCustomRateParameter("PHOTO.H2O2", std::vector(solver.parameters_.number_of_grid_cells_, 2.59e-06)); + state.SetCustomRateParameter("PHOTO.PNA", std::vector(solver.parameters_.number_of_grid_cells_, 1.89e-06)); + state.SetCustomRateParameter("PHOTO.HNO3", std::vector(solver.parameters_.number_of_grid_cells_, 8.61e-08)); + state.SetCustomRateParameter("PHOTO.NTR", std::vector(solver.parameters_.number_of_grid_cells_, 4.77e-07)); + state.SetCustomRateParameter("PHOTO.ROOH", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); + state.SetCustomRateParameter("PHOTO.MEPX", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); + state.SetCustomRateParameter("PHOTO.FORM->HO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.93e-06)); + state.SetCustomRateParameter("PHOTO.FORM->CO", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-05)); + state.SetCustomRateParameter("PHOTO.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); + state.SetCustomRateParameter("PHOTO.PACD", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); + state.SetCustomRateParameter("PHOTO.ALDX", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); + state.SetCustomRateParameter("PHOTO.OPEN", std::vector(solver.parameters_.number_of_grid_cells_, 0.0006450000000000001)); + state.SetCustomRateParameter("PHOTO.MGLY", std::vector(solver.parameters_.number_of_grid_cells_, 7.64e-05)); + state.SetCustomRateParameter("PHOTO.ISPD", std::vector(solver.parameters_.number_of_grid_cells_, 1.98e-09)); // choose a timestep and print the initial state double time_step = 500; // s @@ -112,20 +117,25 @@ int main(const int argc, const char* argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + auto solver_parameters = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(n_grid_cells); + + RosenbrockSolver< + GroupVectorMatrix, + GroupSparseVectorMatrix + > solver{ chemical_system, reactions, solver_parameters }; auto jit{ micm::JitCompiler::create() }; auto start = std::chrono::high_resolution_clock::now(); JitRosenbrockSolver< - Group1VectorMatrix, - Group1SparseVectorMatrix, - JitLinearSolver<1, Group1SparseVectorMatrix> + GroupVectorMatrix, + GroupSparseVectorMatrix, + JitLinearSolver > jit_solver( jit.get(), chemical_system, reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + solver_parameters ); auto end = std::chrono::high_resolution_clock::now(); auto jit_compile_time = std::chrono::duration_cast(end - start); @@ -181,8 +191,10 @@ int main(const int argc, const char* argv[]) auto jit_result = result_pair.first; for(auto& species : result.variable_names_) { - if (result.variables_[0][result.variable_map_[species]] != jit_result.variables_[0][jit_result.variable_map_[species]]) { - std::cout << species << " do not match final concentration" << std::endl; + for(int i = 0; i < n_grid_cells; ++i) { + if (result.variables_[i][result.variable_map_[species]] != jit_result.variables_[i][jit_result.variable_map_[species]]) { + std::cout << species << " do not match final concentration" << std::endl; + } } } return 0; From d67ce1bc8acacc28495ed8e9ff5f0a4d1f21f14c Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 14:26:09 -0500 Subject: [PATCH 199/318] jit tutorial --- .../_static/tutorials/carbon_bond_5.zip | Bin 0 -> 6134 bytes docs/source/references.bib | 28 +++++ docs/source/user_guide/jit.rst | 113 +++++++++++++++++- test/tutorial/test_jit_tutorial.cpp | 7 +- 4 files changed, 138 insertions(+), 10 deletions(-) create mode 100644 docs/source/_static/tutorials/carbon_bond_5.zip diff --git a/docs/source/_static/tutorials/carbon_bond_5.zip b/docs/source/_static/tutorials/carbon_bond_5.zip new file mode 100644 index 0000000000000000000000000000000000000000..a9af056229d2f7a18c9de577272ff11d0e4037ae GIT binary patch literal 6134 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 literal 0 HcmV?d00001 diff --git a/docs/source/references.bib b/docs/source/references.bib index 5e848eb60..f1fe941b0 100644 --- a/docs/source/references.bib +++ b/docs/source/references.bib @@ -35,6 +35,24 @@ @article{Damian2002 file = {/Users/kshores/Zotero/storage/2VJ5XQH2/Damian et al. - 2002 - The kinetic preprocessor KPP-a software environmen.pdf;/Users/kshores/Zotero/storage/YQSFJ5YJ/S009813540200128X.html} } +@article{Gery1989, + title = {A Photochemical Kinetics Mechanism for Urban and Regional Scale Computer Modeling}, + author = {Gery, Michael W. and Whitten, Gary Z. and Killus, James P. and Dodge, Marcia C.}, + year = {1989}, + journal = {Journal of Geophysical Research: Atmospheres}, + volume = {94}, + number = {D10}, + pages = {12925--12956}, + issn = {2156-2202}, + doi = {10.1029/JD094iD10p12925}, + urldate = {2023-10-27}, + abstract = {A new chemical kinetics mechanism for simulating urban and regional photochemistry has been developed and evaluated. The mechanism, called the Carbon Bond Mechanism IV (CBM-IV), was derived by condensing a detailed mechanism that included the most recent kinetic, mechanistic, and photolytic information. The CBM-IV contains extensive improvements to earlier carbon bond mechanisms in the chemical representations of aromatics, biogenic hydrocarbons, peroxyacetyl nitrates, and formaldehyde. The performance of the CBM-IV was evaluated against data from 170 experiments conducted in three different smog chambers. These experiments included NOx-air irradiations of individual organic compounds as well as a number of simple and complex organic mixtures. The results of the evaluation indicate substantial improvement in the ability of the CBM-IV to simulate aromatic and isoprene systems with average overcalculation of ozone concentrations of 1\% for the aromatic simulations and 6\% for the isoprene simulations. The mechanism also performed well in simulating organic mixture experiments. Maximum ozone concentrations calculated for 68 of these experiments were approximately 2\% greater than the observed values while formaldehyde values were low by 9\%.}, + copyright = {Copyright 1989 by the American Geophysical Union.}, + langid = {english}, + file = {/Users/kshores/Zotero/storage/4VYTJB9M/Gery et al. - 1989 - A photochemical kinetics mechanism for urban and r.pdf;/Users/kshores/Zotero/storage/QF83P3VZ/JD094iD10p12925.html} +} + + @book{Hairer1996, title = {Solving {{Ordinary Differential Equations II}}: {{Stiff}} and {{Differential-Algebraic Problems}}}, shorttitle = {Solving {{Ordinary Differential Equations II}}}, @@ -152,3 +170,13 @@ @article{Sandu1996 langid = {english}, file = {/Users/kshores/Zotero/storage/XFN28QLI/Sandu et al. - 1996 - Efficient Implementation of Fully Implicit Methods.pdf;/Users/kshores/Zotero/storage/43WPJN5Y/S0021999196902363.html} } + +@techreport{Yarwood2005, + title = {Updates to the {{Carbon Bond Chemical Mechanism}}: {{CB05}}}, + author = {Yarwood, Greg and Rao, Sunja and Way, Rowland and Yocke, Mark and Whitten, Gary Z and Reyes, Smog}, + year = {2005}, + month = dec, + number = {RT-04-00675}, + institution = {{Final Report to the US EPA}}, + langid = {english}, +} \ No newline at end of file diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst index 51a573db7..774661467 100644 --- a/docs/source/user_guide/jit.rst +++ b/docs/source/user_guide/jit.rst @@ -32,10 +32,113 @@ So what does this gain me? -------------------------- Runtime configuraiton of chemical mechanisms that are *fast*. Let's compare the speed of :cpp:class:`micm::RosenbrockSolver` -and the :cpp:class:`micm::JitRosenbrockSolver` classes applied to the same problem. We'll use this system. +and the :cpp:class:`micm::JitRosenbrockSolver` classes applied to the same problem. This time we'll use the carbon-bond 5 +mechanism :cite:`Yarwood2005`, which is an update to the carbon bond mechanism from :cite:`Gery1989`. -.. math:: +If you're looking for a copy and paste, choose +the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. - A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ - 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ - B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ + +.. tab-set:: + + .. tab-item:: OpenAtmos Configuration reading + + .. raw:: html + +

+ + .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + +Line-by-line explanation +------------------------ + +Starting with the header files, we need headers for timing, outpu, reading the configuration, +and of course for both types of solvers. + +.. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + :lines: 1-7 + +Next, we use our namespace, define our number of gridcells (1 for now), and some +partial template specializations. We are using our custom vectorized matrix, which +groups mutliple reactions across grid cells into tiny blocks in a vector, allowing multiple +grid cells to be solved simultaneously. + +.. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + :lines: 14-18 + +Now, all at once, is the function which runs either type of solver. We set all species +concentrations to 1 :math:`\mathrm{mol m^-3}` and set the rate paramters for all of the +photolysis and emissions reactions. Additionally, we are collecting all of the solver +stats across all solving timesteps + +.. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + :lines: 20-96 + +Finally, the main function which reads the configuration and initializes the jit solver. + +.. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + :lines: 101-117 + +The only additionall step here is to make an instance of the :cpp:class:`micm::JitCompiler` +and pass it as a shared pointer to the :cpp:class:`micm::JitRosenbrockSolver`. We also are +using our vectorized matrix for both solvers. The :cpp:class:`micm::JitRosenbrockSolver` only works +with the vectorized matrix whereas the :cpp:class:`micm::RosenbrockSolver` works with a regular matrix. +At construction of the :cpp:class:`micm::JitRosenbrockSolver`, all JIT functions are compiled. We record that +time here. + +.. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + :lines: 119-140 + +Finally, we run both solvers, output their cumulative stats, and compare their results. + + +.. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp + :language: cpp + :lines: 142-198 + + +The output will be similar to this: + +.. code:: bash + + Jit compile time: 207422864917 nanoseconds + Result time: 8593750 nanoseconds + JIT result time: 4904666 nanoseconds + Result stats: + accepted: 162 + function_calls: 334 + jacobian_updates: 162 + number_of_steps: 172 + accepted: 162 + rejected: 0 + decompositions: 172 + solves: 516 + singular: 0 + total_forcing_time: 339551 nanoseconds + total_jacobian_time: 1.60283e+06 nanoseconds + total_linear_factor_time: 4.88587e+06 nanoseconds + total_linear_solve_time: 928501 nanoseconds + JIT result stats: + accepted: 162 + function_calls: 334 + jacobian_updates: 162 + number_of_steps: 172 + accepted: 162 + rejected: 0 + decompositions: 172 + solves: 516 + singular: 0 + total_forcing_time: 317043 nanoseconds + total_jacobian_time: 1.57724e+06 nanoseconds + total_linear_factor_time: 1.56572e+06 nanoseconds + total_linear_solve_time: 630749 nanoseconds \ No newline at end of file diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index 7eaa260d4..1d6b33961 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -1,9 +1,6 @@ #include -#include #include -#include -// Each rate constant is in its own header file #include #include #include @@ -14,6 +11,7 @@ using namespace micm; constexpr size_t n_grid_cells = 1; +// partial template specializations template using GroupVectorMatrix = micm::VectorMatrix; template @@ -33,7 +31,6 @@ auto run_solver(T& solver) state.conditions_[i].air_density_ = 1e6; // mol m-3 } - state.SetCustomRateParameter("EMIS.NO", std::vector(solver.parameters_.number_of_grid_cells_, 1.44e-10)); state.SetCustomRateParameter("EMIS.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.56e-12)); state.SetCustomRateParameter("EMIS.CO", std::vector(solver.parameters_.number_of_grid_cells_, 1.9600000000000003e-09)); @@ -198,4 +195,4 @@ int main(const int argc, const char* argv[]) } } return 0; -} +} \ No newline at end of file From 97e139c645bb3c642608bda4d995b36571387ca1 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 14:35:48 -0500 Subject: [PATCH 200/318] matrix tests --- test/unit/util/test_matrix.cpp | 5 +++++ test/unit/util/test_matrix_policy.hpp | 14 ++++++++++++++ test/unit/util/test_sparse_matrix_policy.hpp | 16 ++++++++++++++++ .../test_sparse_matrix_standard_ordering.cpp | 5 +++++ .../util/test_sparse_matrix_vector_ordering.cpp | 5 +++++ test/unit/util/test_vector_matrix.cpp | 8 ++++++++ 6 files changed, 53 insertions(+) diff --git a/test/unit/util/test_matrix.cpp b/test/unit/util/test_matrix.cpp index eed8abe25..9c0897c8c 100644 --- a/test/unit/util/test_matrix.cpp +++ b/test/unit/util/test_matrix.cpp @@ -96,4 +96,9 @@ TEST(Matrix, AssignmentFromVector) TEST(Matrix, ForEach) { testForEach(); +} + +TEST(Matrix, SetScaler) +{ + testSetScalar(); } \ No newline at end of file diff --git a/test/unit/util/test_matrix_policy.hpp b/test/unit/util/test_matrix_policy.hpp index f9ca8dd5e..23aa5f84b 100644 --- a/test/unit/util/test_matrix_policy.hpp +++ b/test/unit/util/test_matrix_policy.hpp @@ -230,5 +230,19 @@ MatrixPolicy testForEach() matrix.ForEach([&](double a, double b, double c) { result += a + b - c; }, other, other2); EXPECT_NEAR(sum2, result, 1.0e-5); + return matrix; +} + +template class MatrixPolicy> +MatrixPolicy testSetScalar() +{ + MatrixPolicy matrix{ 2, 3, 0.0 }; + + matrix = 2.0; + + for(auto& elem : matrix.AsVector()) { + EXPECT_EQ(elem, 2.0); + } + return matrix; } \ No newline at end of file diff --git a/test/unit/util/test_sparse_matrix_policy.hpp b/test/unit/util/test_sparse_matrix_policy.hpp index 6dfe30660..74b44a36e 100644 --- a/test/unit/util/test_sparse_matrix_policy.hpp +++ b/test/unit/util/test_sparse_matrix_policy.hpp @@ -456,3 +456,19 @@ micm::SparseMatrix testMultiBlockMatrix() std::invalid_argument); return matrix; } + +template +micm::SparseMatrix testSetScalar() +{ + auto builder = micm::SparseMatrix::create(3); + + micm::SparseMatrix matrix{ builder }; + + matrix = 2.0; + + for(auto& elem : matrix.AsVector()) { + EXPECT_EQ(elem, 2.0); + } + + return matrix; +} \ No newline at end of file diff --git a/test/unit/util/test_sparse_matrix_standard_ordering.cpp b/test/unit/util/test_sparse_matrix_standard_ordering.cpp index cdd2d0e35..c7e7b6dc4 100644 --- a/test/unit/util/test_sparse_matrix_standard_ordering.cpp +++ b/test/unit/util/test_sparse_matrix_standard_ordering.cpp @@ -17,6 +17,11 @@ TEST(SparseMatrix, ConstZeroMatrix) testConstZeroMatrix(); } +TEST(SparseMatrix, SetScalar) +{ + testSetScalar(); +} + TEST(SparseMatrix, SingleBlockMatrix) { auto matrix = testSingleBlockMatrix(); diff --git a/test/unit/util/test_sparse_matrix_vector_ordering.cpp b/test/unit/util/test_sparse_matrix_vector_ordering.cpp index 1ee29c89b..28191e85b 100644 --- a/test/unit/util/test_sparse_matrix_vector_ordering.cpp +++ b/test/unit/util/test_sparse_matrix_vector_ordering.cpp @@ -15,6 +15,11 @@ TEST(SparseVectorMatrix, ConstZeroMatrix) testConstZeroMatrix>(); } +TEST(SparseVectorMatrix, SetScalar) +{ + testSetScalar>(); +} + TEST(SparseVectorMatrix, SingleBlockMatrix) { auto matrix = testSingleBlockMatrix>(); diff --git a/test/unit/util/test_vector_matrix.cpp b/test/unit/util/test_vector_matrix.cpp index 1c8583a80..20d53fd67 100644 --- a/test/unit/util/test_vector_matrix.cpp +++ b/test/unit/util/test_vector_matrix.cpp @@ -89,4 +89,12 @@ TEST(VectorMatrix, ForEach) testForEach(); testForEach(); testForEach(); +} + +TEST(VectorMatrix, SetScaler) +{ + testSetScalar(); + testSetScalar(); + testSetScalar(); + testSetScalar(); } \ No newline at end of file From 173fbf97b112f4911899dfc7d3bb8a4ce997e14e Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 14:58:45 -0500 Subject: [PATCH 201/318] saving progress --- test/unit/solver/test_jit_rosenbrock.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index 52b99ded3..4d88827c7 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -132,4 +132,9 @@ TEST(JitRosenbrockSolver, AlphaMinusJacobian) testAlphaMinusJacobian<2, Group2VectorMatrix, Group2SparseVectorMatrix>(jit.get()); testAlphaMinusJacobian<3, Group3VectorMatrix, Group3SparseVectorMatrix>(jit.get()); testAlphaMinusJacobian<4, Group4VectorMatrix, Group4SparseVectorMatrix>(jit.get()); +} + +TEST(JitRosenbrockSolver, MultipleInstances) +{ + auto jit{ micm::JitCompiler::create() }; } \ No newline at end of file From 0132c67cbb9acda586fa667304167ed41297c99c Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 15:00:08 -0500 Subject: [PATCH 202/318] algorithm --- include/micm/util/matrix.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index 329f386d6..f39ece820 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include #include From 379b6a38cdf3a3270bcff53d182d011338ed98fd Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 15:24:16 -0500 Subject: [PATCH 203/318] adding random string to end of jit function names --- include/micm/process/jit_process_set.hpp | 7 +- include/micm/solver/jit_linear_solver.hpp | 1 + include/micm/solver/jit_linear_solver.inl | 3 +- include/micm/solver/jit_lu_decomposition.hpp | 1 + include/micm/solver/jit_lu_decomposition.inl | 3 +- include/micm/solver/jit_rosenbrock.hpp | 14 +--- include/micm/util/random_string.hpp | 30 ++++++++ test/unit/solver/test_jit_rosenbrock.cpp | 76 ++++++++++++++++++++ 8 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 include/micm/util/random_string.hpp diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index ef3926beb..227df3eef 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -123,8 +124,9 @@ namespace micm template void JitProcessSet::GenerateForcingFunction() { + std::string function_name = "add_forcing_terms_" + generate_random_string(); JitFunction func = JitFunction::create(compiler_) - .name("add_forcing_terms") + .name(function_name) .arguments({ { "rate constants", JitType::DoublePtr }, { "state variables", JitType::DoublePtr }, { "forcing", JitType::DoublePtr } }) @@ -224,8 +226,9 @@ namespace micm template void JitProcessSet::GenerateJacobianFunction(const SparseMatrix> &matrix) { + std::string function_name = "add_jacobian_terms_" + generate_random_string(); JitFunction func = JitFunction::create(compiler_) - .name("add_jacobian_terms") + .name(function_name) .arguments({ { "rate constants", JitType::DoublePtr }, { "state variables", JitType::DoublePtr }, { "jacobian", JitType::DoublePtr } }) diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index 11a6d2fe7..a3354e84e 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index 195ec27a1..894d21347 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -86,8 +86,9 @@ namespace micm template class SparseMatrixPolicy, class LuDecompositionPolicy> inline void JitLinearSolver::GenerateSolveFunction() { + std::string function_name = "linear_solve_" + generate_random_string(); JitFunction func = JitFunction::create(compiler_) - .name("linear_solve") + .name(function_name) .arguments({ { "b", JitType::DoublePtr }, { "x", JitType::DoublePtr }, { "L", JitType::DoublePtr }, diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index 5d44d26f4..e5b2959ab 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include namespace micm diff --git a/include/micm/solver/jit_lu_decomposition.inl b/include/micm/solver/jit_lu_decomposition.inl index a1a3cbb28..71677ac28 100644 --- a/include/micm/solver/jit_lu_decomposition.inl +++ b/include/micm/solver/jit_lu_decomposition.inl @@ -53,8 +53,9 @@ namespace micm template void JitLuDecomposition::GenerateDecomposeFunction() { + std::string function_name = "lu_decompose_" + generate_random_string(); JitFunction func = JitFunction::create(compiler_) - .name("lu_decompose") + .name(function_name) .arguments({ { "A matrix", JitType::DoublePtr }, { "lower matrix", JitType::DoublePtr }, { "upper matrix", JitType::DoublePtr } }) diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 3380ed713..8a8fefadb 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -119,20 +120,11 @@ namespace micm std::size_t n_cells = this->jacobian_.GroupVectorSize(); std::size_t number_of_nonzero_jacobian_elements = this->jacobian_.AsVector().size(); - // Get the current timestamp using std::chrono::high_resolution_clock - std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); - std::chrono::nanoseconds ns = std::chrono::duration_cast(now.time_since_epoch()); - long long timestamp = ns.count(); - - // Convert the timestamp to a string - std::string timestampStr = std::to_string(timestamp); - - // Create the function name with the timestamp - std::string functionName = "alpha_minus_jacobian_" + timestampStr; + std::string function_name = "alpha_minus_jacobian_" + generate_random_string(); // Create the JitFunction with the modified name JitFunction func = JitFunction::create(compiler_) - .name("alpha_minus_jacobian") + .name(function_name) .arguments({ { "jacobian", JitType::DoublePtr }, { "alpha", JitType::Double } }) .return_type(JitType::Void); diff --git a/include/micm/util/random_string.hpp b/include/micm/util/random_string.hpp new file mode 100644 index 000000000..c48e7a1aa --- /dev/null +++ b/include/micm/util/random_string.hpp @@ -0,0 +1,30 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +namespace micm +{ + std::string generate_random_string() + { + auto now = std::chrono::system_clock::now(); + auto epoch = now.time_since_epoch(); + auto timestamp = std::chrono::duration_cast(epoch).count(); + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dist(0, 9999); + int randomNum = dist(gen); + + std::string randomNumStr = std::to_string(randomNum); + std::string timestampStr = std::to_string(timestamp); + + std::string result = timestampStr + randomNumStr; + + return result; + } +} // namespace micm \ No newline at end of file diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index 4d88827c7..8abcd755a 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -120,6 +121,31 @@ using Group3SparseVectorMatrix = micm::SparseMatrix using Group4SparseVectorMatrix = micm::SparseMatrix>; +void run_solver(auto& solver) +{ + auto state = solver.GetState(); + + state.variables_ = { 1, 0, 0 }; + + state.conditions_[0].temperature_ = 287.45; // K + state.conditions_[0].pressure_ = 101319.9; // Pa + state.conditions_[0].air_density_ = 1e6; // mol m-3 + + double time_step = 500; // s + + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.template Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + } +} + TEST(JitRosenbrockSolver, AlphaMinusJacobian) { auto jit{ micm::JitCompiler::create() }; @@ -137,4 +163,54 @@ TEST(JitRosenbrockSolver, AlphaMinusJacobian) TEST(JitRosenbrockSolver, MultipleInstances) { auto jit{ micm::JitCompiler::create() }; + + micm::SolverConfig solverConfig; + std::string config_path = "./unit_configs/robertson"; + micm::ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + auto solver_parameters = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(); + + micm::JitRosenbrockSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix> + > solver1( + jit.get(), + chemical_system, + reactions, + solver_parameters + ); + micm::JitRosenbrockSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix> + > solver2( + jit.get(), + chemical_system, + reactions, + solver_parameters + ); + micm::JitRosenbrockSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix> + > solver3( + jit.get(), + chemical_system, + reactions, + solver_parameters + ); + + run_solver(solver1); + run_solver(solver2); + run_solver(solver3); } \ No newline at end of file From d7be5426d2a147928b277a8cb5bf51e5e05ef2fc Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 15:52:46 -0500 Subject: [PATCH 204/318] adding multiple instance of a jit test --- test/unit/solver/test_jit_rosenbrock.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index 8abcd755a..939100fdf 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -125,7 +125,7 @@ void run_solver(auto& solver) { auto state = solver.GetState(); - state.variables_ = { 1, 0, 0 }; + state.variables_[0] = { 1, 0, 0 }; state.conditions_[0].temperature_ = 287.45; // K state.conditions_[0].pressure_ = 101319.9; // Pa @@ -139,7 +139,7 @@ void run_solver(auto& solver) while (elapsed_solve_time < time_step) { - auto result = solver.template Solve(time_step - elapsed_solve_time, state); + auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; state.variables_ = result.result_; } @@ -213,4 +213,4 @@ TEST(JitRosenbrockSolver, MultipleInstances) run_solver(solver1); run_solver(solver2); run_solver(solver3); -} \ No newline at end of file +} From 0f8c579fa2d383c7559d6e87b795369d649c545f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 27 Oct 2023 16:53:11 -0500 Subject: [PATCH 205/318] 279 make solve function for ode solvers thread safe (#322) * decoupling solver stats * put the jacobian on the state * moving L and U to state * tests pass * added openmp test * stuff * adding a test for the jit solver for thread safety * removing data member * removing N * ordering state parameters * more ordering... * ordering * ordering * fixing some things? * thing * fixing cuda stuff? * correcting analytical tests * removing duplicate linkage of LLVM with the tests * removing system from rosenbrock solver * moving solver parameters into its own file * removing invalid state constructor * removing state from jit process set * removing assert * setting variable names * removing parallel build to see if clang builds * verbose build on clang * attempting to force libc++ for clang to make test pass * only linking libc++ if on linux... * matching linux properly * comment --- .github/workflows/ubuntu.yml | 2 +- CMakeLists.txt | 6 + cmake/test_util.cmake | 8 - docker/Dockerfile | 2 +- docs/source/conf.py | 2 + .../user_defined_rate_constant_tutorial.rst | 4 +- .../micm/process/arrhenius_rate_constant.hpp | 10 + .../micm/process/branched_rate_constant.hpp | 10 + include/micm/process/cuda_process_set.hpp | 18 +- include/micm/process/jit_process_set.hpp | 12 +- include/micm/process/process.hpp | 16 +- include/micm/process/process_set.hpp | 12 +- include/micm/process/rate_constant.hpp | 9 + .../micm/process/surface_rate_constant.hpp | 11 + ...nary_chemical_activation_rate_constant.hpp | 11 + include/micm/process/troe_rate_constant.hpp | 10 + .../micm/process/tunneling_rate_constant.hpp | 10 + .../process/user_defined_rate_constant.hpp | 10 + include/micm/solver/cuda_lu_decomposition.hpp | 9 +- include/micm/solver/jit_linear_solver.hpp | 17 +- include/micm/solver/jit_linear_solver.inl | 21 +- include/micm/solver/jit_rosenbrock.hpp | 7 +- include/micm/solver/linear_solver.hpp | 20 +- include/micm/solver/linear_solver.inl | 54 +- include/micm/solver/rosenbrock.hpp | 172 ++---- include/micm/solver/rosenbrock.inl | 522 +++--------------- .../solver/rosenbrock_solver_parameters.hpp | 462 ++++++++++++++++ include/micm/solver/state.hpp | 34 +- include/micm/solver/state.inl | 79 ++- include/micm/util/cuda_param.hpp | 10 +- include/micm/util/exit_codes.hpp | 12 - include/micm/util/jacobian.hpp | 24 + include/micm/util/matrix.hpp | 9 +- include/micm/util/vector_matrix.hpp | 7 +- src/CMakeLists.txt | 8 +- test/integration/analytical_rosenbrock.cpp | 10 +- test/integration/chapman.cpp | 2 +- test/integration/e5.hpp | 50 +- test/integration/hires.hpp | 50 +- test/integration/oregonator.hpp | 50 +- test/integration/terminator.hpp | 2 +- test/kpp/test_kpp_to_micm.cpp | 4 +- .../RosenbrockChapman/chapman_ode_solver.hpp | 17 +- .../regression_test_dforce_dy_policy.hpp | 2 +- test/tutorial/test_but_how_fast_is_it.cpp | 2 +- test/tutorial/test_multiple_grid_cells.cpp | 6 +- test/tutorial/test_openmp.cpp | 15 +- ...st_rate_constants_user_defined_by_hand.cpp | 4 +- ...ate_constants_user_defined_with_config.cpp | 4 +- test/tutorial/test_solver_configuration.cpp | 10 +- test/unit/openmp/CMakeLists.txt | 5 +- test/unit/openmp/run_solver.hpp | 40 ++ test/unit/openmp/test_openmp.cpp | 69 +-- test/unit/openmp/test_openmp_jit_solver.cpp | 64 +++ .../process/test_arrhenius_rate_constant.cpp | 34 +- .../process/test_branched_rate_constant.cpp | 22 +- test/unit/process/test_cuda_process_set.cpp | 26 +- test/unit/process/test_jit_process_set.cpp | 20 +- test/unit/process/test_process.cpp | 8 +- test/unit/process/test_process_set.cpp | 38 +- test/unit/process/test_process_set_policy.hpp | 20 +- .../process/test_surface_rate_constant.cpp | 19 +- ...nary_chemical_activation_rate_constant.cpp | 22 +- test/unit/process/test_troe_rate_constant.cpp | 44 +- .../process/test_tunneling_rate_constant.cpp | 16 +- .../test_user_defined_rate_constant.cpp | 29 +- test/unit/solver/test_jit_rosenbrock.cpp | 2 +- .../unit/solver/test_linear_solver_policy.hpp | 22 +- test/unit/solver/test_rosenbrock.cpp | 2 +- test/unit/solver/test_state.cpp | 100 ++-- test/unit/util/test_matrix_policy.hpp | 4 +- 71 files changed, 1415 insertions(+), 1049 deletions(-) create mode 100644 include/micm/solver/rosenbrock_solver_parameters.hpp delete mode 100644 include/micm/util/exit_codes.hpp create mode 100644 include/micm/util/jacobian.hpp create mode 100644 test/unit/openmp/run_solver.hpp create mode 100644 test/unit/openmp/test_openmp_jit_solver.cpp diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3cd9aad7c..64b81798f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -48,7 +48,7 @@ jobs: run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} - name: Build - run: cmake --build build --parallel 10 + run: cmake --build build --verbose - name: Run tests run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ea8bc748..9d40148da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,12 @@ endif() add_compile_definitions(DEFAULT_VECTOR_SIZE=${DEFAULT_VECTOR_MATRIX_SIZE}) +# on ubuntu with clang, an incorrect version of the c++ standard library was being linked +if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux" AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + # If the compiler is Clang, use libc++ + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") +endif() + ################################################################################ # Dependencies diff --git a/cmake/test_util.cmake b/cmake/test_util.cmake index eb843efc6..c369165d1 100644 --- a/cmake/test_util.cmake +++ b/cmake/test_util.cmake @@ -25,14 +25,6 @@ function(create_standard_test) target_link_libraries(test_${TEST_NAME} PUBLIC ${library}) endforeach() - if(ENABLE_JSON) - target_link_libraries(test_${TEST_NAME} PRIVATE nlohmann_json::nlohmann_json) - endif() - - if(ENABLE_LLVM) - target_link_libraries(test_${TEST_NAME} PRIVATE ${llvm_libs}) - endif() - if(NOT DEFINED TEST_WORKING_DIRECTORY) set(TEST_WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") endif() diff --git a/docker/Dockerfile b/docker/Dockerfile index 1954e7222..89d7c89ea 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,7 +20,7 @@ RUN mkdir /build \ -D CMAKE_BUILD_TYPE=debug \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ ../micm \ - && make install -j 8 + && make install -j # now test if we can use the installed files RUN cd /micm/test/integration/cmake/find_package \ diff --git a/docs/source/conf.py b/docs/source/conf.py index 15a34ab77..68a05905a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -66,6 +66,8 @@ "json_url": "https://ncar.github.io/micm/_static/switcher.json", "version_match": release, }, + "pygment_light_style": "tango", + "pygment_dark_style": "monokai" } html_css_files = [ diff --git a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst index a36d7246d..46f1b6028 100644 --- a/docs/source/user_guide/user_defined_rate_constant_tutorial.rst +++ b/docs/source/user_guide/user_defined_rate_constant_tutorial.rst @@ -194,7 +194,7 @@ Finally, set and upate the rate constants as needed: // so we need to track how much time the solver was able to integrate for and continue // solving until we finish double elapsed_solve_time = 0; - + state.SetCustomRateParameter("PHOTO.my photolysis rate", photo_rate); + +state.SetCustomRateParameter("PHOTO.my photolysis rate", photo_rate); while (elapsed_solve_time < time_step) { @@ -205,7 +205,7 @@ Finally, set and upate the rate constants as needed: } print_state(time_step * (i + 1), state); - + photo_rate *= 1.5; + +photo_rate *= 1.5; } And this is final output. Notice that the concentration of G ends up much higher than in diff --git a/include/micm/process/arrhenius_rate_constant.hpp b/include/micm/process/arrhenius_rate_constant.hpp index ac4f04bc3..e680903c1 100644 --- a/include/micm/process/arrhenius_rate_constant.hpp +++ b/include/micm/process/arrhenius_rate_constant.hpp @@ -47,6 +47,11 @@ namespace micm /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; + double calculate(const double& temperature, const double& pressure) const; }; @@ -72,6 +77,11 @@ namespace micm return calculate(conditions.temperature_, conditions.pressure_); } + inline double ArrheniusRateConstant::calculate(const Conditions& conditions) const + { + return calculate(conditions.temperature_, conditions.pressure_); + } + inline double ArrheniusRateConstant::calculate(const double& temperature, const double& pressure) const { return parameters_.A_ * std::exp(parameters_.C_ / temperature) * std::pow(temperature / parameters_.D_, parameters_.B_) * diff --git a/include/micm/process/branched_rate_constant.hpp b/include/micm/process/branched_rate_constant.hpp index 58dd177ff..8291bd7dc 100644 --- a/include/micm/process/branched_rate_constant.hpp +++ b/include/micm/process/branched_rate_constant.hpp @@ -54,6 +54,11 @@ namespace micm /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; + /// @brief Calculate the rate constant /// @param temperature Temperature in [K] /// @param air_number_density Number density in [mol m-3] @@ -92,6 +97,11 @@ namespace micm return calculate(conditions.temperature_, conditions.air_density_); } + inline double BranchedRateConstant::calculate(const Conditions& conditions) const + { + return calculate(conditions.temperature_, conditions.air_density_); + } + inline double BranchedRateConstant::calculate(const double& temperature, const double& air_number_density) const { double pre = parameters_.X_ * std::exp(-parameters_.Y_ / temperature); diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index e3f352fcc..31b3d580a 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -5,12 +5,8 @@ #include #include +#include -#ifdef USE_CUDA -# include -#endif - -#ifdef USE_CUDA namespace micm { /// @brief A GPU-based implementation of ProcessSet @@ -20,8 +16,7 @@ namespace micm /// @brief Create a process set calculator for a given set of processes /// @param processes Processes to create calculator for /// @param state Solver state - template class MatrixPolicy> - CudaProcessSet(const std::vector& processes, const State& state); + CudaProcessSet(const std::vector& processes, const std::map& variable_map); template typename MatrixPolicy> requires VectorizableDense> std::chrono::nanoseconds AddForcingTerms( @@ -39,9 +34,8 @@ namespace micm const; }; - template class MatrixPolicy> - inline CudaProcessSet::CudaProcessSet(const std::vector& processes, const State& state) - : ProcessSet(processes, state) + inline CudaProcessSet::CudaProcessSet(const std::vector& processes, const std::map& variable_map) + : ProcessSet(processes, variable_map) { } @@ -73,6 +67,7 @@ namespace micm std::chrono::nanoseconds kernel_duration = micm::cuda::AddForcingTermsKernelDriver(matrix, processSet); return kernel_duration; // time performance of kernel function } + template class MatrixPolicy, template class SparseMatrixPolicy> requires VectorizableDense> && VectorizableSparse> inline std::chrono::nanoseconds CudaProcessSet::AddJacobianTerms( @@ -104,5 +99,4 @@ namespace micm std::chrono::nanoseconds kernel_duration = micm::cuda::AddJacobianTermsKernelDriver(matrix, sparseMatrix, processSet); return kernel_duration; // time performance of kernel function } -} // namespace micm -#endif \ No newline at end of file +} // namespace micm \ No newline at end of file diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index ef3926beb..077de39df 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -33,11 +33,10 @@ namespace micm /// @param compiler JIT compiler /// @param processes Processes to create calculator for /// @param state Solver state - template class MatrixPolicy> JitProcessSet( std::shared_ptr compiler, const std::vector &processes, - const State &state); + const std::map& variable_map); ~JitProcessSet(); @@ -103,20 +102,15 @@ namespace micm } template - template class MatrixPolicy> inline JitProcessSet::JitProcessSet( std::shared_ptr compiler, const std::vector &processes, - const State &state) - : ProcessSet(processes, state), + const std::map& variable_map) + : ProcessSet(processes, variable_map), compiler_(compiler) { forcing_function_ = NULL; jacobian_function_ = NULL; - if (state.variables_.size() != L || state.variables_.GroupVectorSize() != L) - { - throw std::runtime_error("Invalid state for JitProcessSet. Check the the VectorMatrix template parameters."); - } this->GenerateForcingFunction(); } diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index f19386c9b..cf86a4692 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -42,14 +42,14 @@ namespace micm /// @brief Update the solver state rate constants /// @param processes The set of processes being solved /// @param state The solver state to update - template class MatrixPolicy> + template class MatrixPolicy, template class SparseMatrixPolicy> requires(!VectorizableDense>) static void UpdateState( const std::vector& processes, - State& state); - template class MatrixPolicy> + State& state); + template class MatrixPolicy, template class SparseMatrixPolicy> requires(VectorizableDense>) static void UpdateState( const std::vector& processes, - State& state); + State& state); friend class ProcessBuilder; static ProcessBuilder create(); @@ -105,10 +105,10 @@ namespace micm ProcessBuilder& phase(const Phase& phase); }; - template class MatrixPolicy> + template class MatrixPolicy, template class SparseMatrixPolicy> requires(!VectorizableDense>) void Process::UpdateState( const std::vector& processes, - State& state) + State& state) { for (std::size_t i{}; i < state.custom_rate_parameters_.size(); ++i) { @@ -128,10 +128,10 @@ namespace micm } } - template class MatrixPolicy> + template class MatrixPolicy, template class SparseMatrixPolicy> requires(VectorizableDense>) void Process::UpdateState( const std::vector& processes, - State& state) + State& state) { const auto& v_custom_parameters = state.custom_rate_parameters_.AsVector(); auto& v_rate_constants = state.rate_constants_.AsVector(); diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 5942157cb..f7836f7c6 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -29,9 +29,8 @@ namespace micm /// @brief Create a process set calculator for a given set of processes /// @param processes Processes to create calculator for - /// @param state Solver state - template class MatrixPolicy> - ProcessSet(const std::vector& processes, const State& state); + /// @param StateParameters Solver state + ProcessSet(const std::vector& processes, const std::map& variable_map); /// @brief Return the full set of non-zero Jacobian elements for the set of processes /// @return Jacobian elements as a set of index pairs @@ -74,8 +73,7 @@ namespace micm SparseMatrixPolicy& jacobian) const; }; - template class MatrixPolicy> - inline ProcessSet::ProcessSet(const std::vector& processes, const State& state) + inline ProcessSet::ProcessSet(const std::vector& processes, const std::map& variable_map) : number_of_reactants_(), reactant_ids_(), number_of_products_(), @@ -90,14 +88,14 @@ namespace micm { if (reactant.IsParameterized()) continue; // Skip reactants that are parameterizations - reactant_ids_.push_back(state.variable_map_.at(reactant.name_)); + reactant_ids_.push_back(variable_map.at(reactant.name_)); ++number_of_reactants; } for (auto& product : process.products_) { if (product.first.IsParameterized()) continue; // Skip products that are parameterizations - product_ids_.push_back(state.variable_map_.at(product.first.name_)); + product_ids_.push_back(variable_map.at(product.first.name_)); yields_.push_back(product.second); ++number_of_products; } diff --git a/include/micm/process/rate_constant.hpp b/include/micm/process/rate_constant.hpp index 036571190..2dd82e9eb 100644 --- a/include/micm/process/rate_constant.hpp +++ b/include/micm/process/rate_constant.hpp @@ -36,6 +36,15 @@ namespace micm return 0; } + /// @brief Calculate the rate constant for a set of conditions + /// @param conditions The current environmental conditions of the chemical system + /// @param custom_parameters User defined rate constant parameters + /// @return The reaction rate constant + virtual double calculate(const Conditions& conditions) const + { + return 0; + } + /// @brief Calculate the rate constant for a set of conditions /// @param conditions The current environmental conditions of the chemical system /// @param custom_parameters User defined rate constant parameters diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index 1781f51f3..e72cb477e 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -56,6 +56,11 @@ namespace micm /// @param custom_parameters User-defined rate constant parameters /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; }; inline SurfaceRateConstant::SurfaceRateConstant(const SurfaceRateConstantParameters& parameters) @@ -70,6 +75,12 @@ namespace micm return std::unique_ptr{ new SurfaceRateConstant{ *this } }; } + inline double SurfaceRateConstant::calculate( + const Conditions& conditions) const + { + throw std::runtime_error("Surface rate constants must be supplied with a radius and number density using the alternative calculate function"); + } + inline double SurfaceRateConstant::calculate( const Conditions& conditions, std::vector::const_iterator custom_parameters) const diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index 715fce2d2..afd80844f 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -52,6 +52,11 @@ namespace micm /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; + /// @brief Calculate the rate constant /// @param temperature Temperature in [K] /// @param air_number_density Number density in [mol m-3] @@ -75,6 +80,12 @@ namespace micm return std::unique_ptr{ new TernaryChemicalActivationRateConstant{ *this } }; } + inline double TernaryChemicalActivationRateConstant::calculate(const Conditions& conditions) const + { + double val = calculate(conditions.temperature_, conditions.air_density_); + return val; + } + inline double TernaryChemicalActivationRateConstant::calculate( const Conditions& conditions, std::vector::const_iterator custom_parameters) const diff --git a/include/micm/process/troe_rate_constant.hpp b/include/micm/process/troe_rate_constant.hpp index 9c6fa85c0..f721720f6 100644 --- a/include/micm/process/troe_rate_constant.hpp +++ b/include/micm/process/troe_rate_constant.hpp @@ -52,6 +52,11 @@ namespace micm /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; + /// @brief Calculate the rate constant /// @param temperature Temperature in [K] /// @param air_number_density Number density in [mol m-3] @@ -74,6 +79,11 @@ namespace micm return std::unique_ptr{ new TroeRateConstant{ *this } }; } + inline double TroeRateConstant::calculate(const Conditions& conditions) const + { + return calculate(conditions.temperature_, conditions.air_density_); + } + inline double TroeRateConstant::calculate( const Conditions& conditions, std::vector::const_iterator custom_parameters) const diff --git a/include/micm/process/tunneling_rate_constant.hpp b/include/micm/process/tunneling_rate_constant.hpp index 26b366b83..deab180c8 100644 --- a/include/micm/process/tunneling_rate_constant.hpp +++ b/include/micm/process/tunneling_rate_constant.hpp @@ -42,6 +42,11 @@ namespace micm /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; + /// @brief Calculate the rate constant /// @param temperature Temperature in [K] /// @return the calculated rate constant @@ -63,6 +68,11 @@ namespace micm return std::unique_ptr{ new TunnelingRateConstant{ *this } }; } + inline double TunnelingRateConstant::calculate(const Conditions& conditions) const + { + return calculate(conditions.temperature_); + } + inline double TunnelingRateConstant::calculate( const Conditions& conditions, std::vector::const_iterator custom_parameters) const diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index 1b9428991..68f48cd38 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -43,6 +43,11 @@ namespace micm /// @param custom_parameters User-defined rate constant parameters /// @return A rate constant based off of the conditions in the system double calculate(const Conditions& conditions, std::vector::const_iterator custom_parameters) const override; + + /// @brief Calculate the rate constant + /// @param conditions The current environmental conditions of the chemical system + /// @return A rate constant based off of the conditions in the system + double calculate(const Conditions& conditions) const override; }; inline UserDefinedRateConstant::UserDefinedRateConstant() @@ -60,6 +65,11 @@ namespace micm return std::unique_ptr{ new UserDefinedRateConstant{ *this } }; } + inline double UserDefinedRateConstant::calculate(const Conditions& conditions) const + { + throw std::runtime_error("User defined rate constants must be supplied with custom rate parameters using the alternative calculate function"); + } + inline double UserDefinedRateConstant::calculate( const Conditions& conditions, std::vector::const_iterator custom_parameters) const diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index 7f2d33e17..85aca2a75 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -2,15 +2,13 @@ // // SPDX-License-Identifier: Apache-2.0 #pragma once + #include #include #include #include -#ifdef USE_CUDA -# include -#endif +#include -#ifdef USE_CUDA namespace micm { class CudaLuDecomposition : public LuDecomposition @@ -64,5 +62,4 @@ namespace micm // calling kernelSetup function return micm::cuda::DecomposeKernelDriver(sparseMatrix, solver); } -} // namespace micm -#endif \ No newline at end of file +} // namespace micm \ No newline at end of file diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index 11a6d2fe7..7896f517d 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -46,15 +46,26 @@ namespace micm /// @brief Decompose the matrix into upper and lower triangular matrices and general JIT functions /// @param matrix Matrix that will be factored into lower and upper triangular matrices /// @param is_singular Flag that will be set to true if matrix is singular; false otherwise - void Factor(SparseMatrix>& matrix, bool& is_singular); + void Factor( + SparseMatrix>& matrix, + SparseMatrix>& lower_matrix, + SparseMatrix>& upper_matrix, + bool& is_singular); /// @brief Decompose the matrix into upper and lower triangular matrices and general JIT functions /// @param matrix Matrix that will be factored into lower and upper triangular matrices - void Factor(SparseMatrix>& matrix); + void Factor( + SparseMatrix>& matrix, + SparseMatrix>& lower_matrix, + SparseMatrix>& upper_matrix); /// @brief Solve for x in Ax = b template class MatrixPolicy> - void Solve(const MatrixPolicy& b, MatrixPolicy& x); + void Solve( + const MatrixPolicy& b, + MatrixPolicy& x, + SparseMatrixPolicy& lower_matrix, + SparseMatrixPolicy& upper_matrix); private: /// @brief Generates the JIT-ed Solve function diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index 195ec27a1..b578cfd75 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -58,29 +58,36 @@ namespace micm template class SparseMatrixPolicy, class LuDecompositionPolicy> inline void JitLinearSolver::Factor( - SparseMatrix> &matrix, bool& is_singular) + SparseMatrix>& matrix, + SparseMatrix>& lower_matrix, + SparseMatrix>& upper_matrix, + bool& is_singular) { - LinearSolver::Factor(matrix, is_singular); + LinearSolver::Factor(matrix, lower_matrix, upper_matrix, is_singular); } template class SparseMatrixPolicy, class LuDecompositionPolicy> inline void JitLinearSolver::Factor( - SparseMatrix> &matrix) + SparseMatrix>& matrix, + SparseMatrix>& lower_matrix, + SparseMatrix>& upper_matrix) { - LinearSolver::Factor(matrix); + LinearSolver::Factor(matrix, lower_matrix, upper_matrix); } template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> inline void JitLinearSolver::Solve( const MatrixPolicy &b, - MatrixPolicy &x) + MatrixPolicy &x, + SparseMatrixPolicy &lower_matrix, + SparseMatrixPolicy &upper_matrix) { solve_function_( b.AsVector().data(), x.AsVector().data(), - LinearSolver::lower_matrix_.AsVector().data(), - LinearSolver::upper_matrix_.AsVector().data()); + lower_matrix.AsVector().data(), + upper_matrix.AsVector().data()); } template class SparseMatrixPolicy, class LuDecompositionPolicy> diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 3380ed713..61acf0845 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -115,9 +115,10 @@ namespace micm private: void GenerateAlphaMinusJacobian() { + auto jacobian = this->GetState().jacobian_; // save sizes needed throughout the function - std::size_t n_cells = this->jacobian_.GroupVectorSize(); - std::size_t number_of_nonzero_jacobian_elements = this->jacobian_.AsVector().size(); + std::size_t n_cells = jacobian.GroupVectorSize(); + std::size_t number_of_nonzero_jacobian_elements = jacobian.AsVector().size(); // Get the current timestamp using std::chrono::high_resolution_clock std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now(); @@ -147,7 +148,7 @@ namespace micm // iterative over the blocks of the jacobian and add the alpha value // jacobian_vector[i_elem + i_cell] += alpha; - for (const auto& i_elem : this->jacobian_diagonal_elements_) + for (const auto& i_elem : this->state_parameters_.jacobian_diagonal_elements_) { llvm::Value* ptr_index[1]; diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index c292f098e..44569dc2a 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -49,8 +49,6 @@ namespace micm std::vector> Uij_xj_; LuDecompositionPolicy lu_decomp_; - SparseMatrixPolicy lower_matrix_; - SparseMatrixPolicy upper_matrix_; public: /// @brief default constructor @@ -71,24 +69,20 @@ namespace micm const std::function&)> create_lu_decomp); /// @brief Decompose the matrix into upper and lower triangular matrices - /// @param matrix Matrix to decompose into lower and upper triangular matrices - /// @param is_singular Flag that is set to true if matrix is singular; false otherwise - void Factor(const SparseMatrixPolicy& matrix); - + void Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); + /// @brief Decompose the matrix into upper and lower triangular matrices /// @param matrix Matrix to decompose into lower and upper triangular matrices /// @param is_singular Flag that is set to true if matrix is singular; false otherwise - void Factor(const SparseMatrixPolicy& matrix, bool& is_singular); + void Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix, bool& is_singular); /// @brief Solve for x in Ax = b template class MatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) void Solve( - const MatrixPolicy& b, - MatrixPolicy& x); + requires(!VectorizableDense> || !VectorizableSparse>) + void Solve(const MatrixPolicy& b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); template class MatrixPolicy> - requires(VectorizableDense>&& VectorizableSparse>) void Solve( - const MatrixPolicy& b, - MatrixPolicy& x); + requires(VectorizableDense> && VectorizableSparse>) + void Solve(const MatrixPolicy& b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); }; } // namespace micm diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index 0e1957aab..741958ff6 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -72,61 +72,62 @@ namespace micm lu_decomp_(create_lu_decomp(matrix)) { auto lu = lu_decomp_.GetLUMatrices(matrix, initial_value); - lower_matrix_ = std::move(lu.first); - upper_matrix_ = std::move(lu.second); - for (std::size_t i = 0; i < lower_matrix_[0].size(); ++i) + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + for (std::size_t i = 0; i < lower_matrix[0].size(); ++i) { std::size_t nLij = 0; - for (std::size_t j_id = lower_matrix_.RowStartVector()[i]; j_id < lower_matrix_.RowStartVector()[i + 1]; ++j_id) + for (std::size_t j_id = lower_matrix.RowStartVector()[i]; j_id < lower_matrix.RowStartVector()[i + 1]; ++j_id) { - std::size_t j = lower_matrix_.RowIdsVector()[j_id]; + std::size_t j = lower_matrix.RowIdsVector()[j_id]; if (j >= i) break; - Lij_yj_.push_back(std::make_pair(lower_matrix_.VectorIndex(0, i, j), j)); + Lij_yj_.push_back(std::make_pair(lower_matrix.VectorIndex(0, i, j), j)); ++nLij; } // There must always be a non-zero element on the diagonal - nLij_Lii_.push_back(std::make_pair(nLij, lower_matrix_.VectorIndex(0, i, i))); + nLij_Lii_.push_back(std::make_pair(nLij, lower_matrix.VectorIndex(0, i, i))); } - for (std::size_t i = upper_matrix_[0].size() - 1; i != static_cast(-1); --i) + for (std::size_t i = upper_matrix[0].size() - 1; i != static_cast(-1); --i) { std::size_t nUij = 0; - for (std::size_t j_id = upper_matrix_.RowStartVector()[i]; j_id < upper_matrix_.RowStartVector()[i + 1]; ++j_id) + for (std::size_t j_id = upper_matrix.RowStartVector()[i]; j_id < upper_matrix.RowStartVector()[i + 1]; ++j_id) { - std::size_t j = upper_matrix_.RowIdsVector()[j_id]; + std::size_t j = upper_matrix.RowIdsVector()[j_id]; if (j <= i) continue; - Uij_xj_.push_back(std::make_pair(upper_matrix_.VectorIndex(0, i, j), j)); + Uij_xj_.push_back(std::make_pair(upper_matrix.VectorIndex(0, i, j), j)); ++nUij; } // There must always be a non-zero element on the diagonal - nUij_Uii_.push_back(std::make_pair(nUij, upper_matrix_.VectorIndex(0, i, i))); + nUij_Uii_.push_back(std::make_pair(nUij, upper_matrix.VectorIndex(0, i, i))); } }; + template class SparseMatrixPolicy, class LuDecompositionPolicy> - inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix) + inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix) { - lu_decomp_.template Decompose(matrix, lower_matrix_, upper_matrix_); + lu_decomp_.template Decompose(matrix, lower_matrix, upper_matrix); } template class SparseMatrixPolicy, class LuDecompositionPolicy> - inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix, bool& is_singular) + inline void LinearSolver::Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix, bool& is_singular) { - lu_decomp_.template Decompose(matrix, lower_matrix_, upper_matrix_, is_singular); + lu_decomp_.template Decompose(matrix, lower_matrix, upper_matrix, is_singular); } template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> requires(!VectorizableDense> || !VectorizableSparse>) - inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x) + inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix) { for (std::size_t i_cell = 0; i_cell < b.size(); ++i_cell) { auto b_cell = b[i_cell]; auto x_cell = x[i_cell]; - const std::size_t lower_grid_offset = i_cell * lower_matrix_.FlatBlockSize(); - const std::size_t upper_grid_offset = i_cell * upper_matrix_.FlatBlockSize(); + const std::size_t lower_grid_offset = i_cell * lower_matrix.FlatBlockSize(); + const std::size_t upper_grid_offset = i_cell * upper_matrix.FlatBlockSize(); auto& y_cell = x_cell; // Alias x for consistency with equations, but to reuse memory { auto b_elem = b_cell.begin(); @@ -137,10 +138,10 @@ namespace micm *y_elem = *(b_elem++); for (std::size_t i = 0; i < nLij_Lii.first; ++i) { - *y_elem -= lower_matrix_.AsVector()[lower_grid_offset + (*Lij_yj).first] * y_cell[(*Lij_yj).second]; + *y_elem -= lower_matrix.AsVector()[lower_grid_offset + (*Lij_yj).first] * y_cell[(*Lij_yj).second]; ++Lij_yj; } - *(y_elem++) /= lower_matrix_.AsVector()[lower_grid_offset + nLij_Lii.second]; + *(y_elem++) /= lower_matrix.AsVector()[lower_grid_offset + nLij_Lii.second]; } } { @@ -151,12 +152,12 @@ namespace micm // x_elem starts out as y_elem from the previous loop for (std::size_t i = 0; i < nUij_Uii.first; ++i) { - *x_elem -= upper_matrix_.AsVector()[upper_grid_offset + (*Uij_xj).first] * x_cell[(*Uij_xj).second]; + *x_elem -= upper_matrix.AsVector()[upper_grid_offset + (*Uij_xj).first] * x_cell[(*Uij_xj).second]; ++Uij_xj; } // don't iterate before the beginning of the vector - *(x_elem) /= upper_matrix_.AsVector()[upper_grid_offset + nUij_Uii.second]; + *(x_elem) /= upper_matrix.AsVector()[upper_grid_offset + nUij_Uii.second]; if (x_elem != x_cell.begin()) { --x_elem; @@ -169,7 +170,7 @@ namespace micm template class SparseMatrixPolicy, class LuDecompositionPolicy> template class MatrixPolicy> requires(VectorizableDense> && VectorizableSparse>) - inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x) + inline void LinearSolver::Solve(const MatrixPolicy& b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix) { const std::size_t n_cells = b.GroupVectorSize(); // Loop over groups of blocks @@ -178,9 +179,9 @@ namespace micm auto b_group = std::next(b.AsVector().begin(), i_group * b.GroupSize()); auto x_group = std::next(x.AsVector().begin(), i_group * x.GroupSize()); auto L_group = - std::next(lower_matrix_.AsVector().begin(), i_group * lower_matrix_.GroupSize(lower_matrix_.FlatBlockSize())); + std::next(lower_matrix.AsVector().begin(), i_group * lower_matrix.GroupSize(lower_matrix.FlatBlockSize())); auto U_group = - std::next(upper_matrix_.AsVector().begin(), i_group * upper_matrix_.GroupSize(upper_matrix_.FlatBlockSize())); + std::next(upper_matrix.AsVector().begin(), i_group * upper_matrix.GroupSize(upper_matrix.FlatBlockSize())); auto y_group = x_group; // Alias x for consistency with equations, but to reuse memory { auto b_elem = b_group; @@ -224,5 +225,4 @@ namespace micm } } } - } // namespace micm \ No newline at end of file diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 9dda76770..e78ed639e 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -16,7 +16,6 @@ #pragma once #include -#include #include #include #include @@ -28,7 +27,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -37,98 +38,6 @@ namespace micm { - /// @brief Rosenbrock solver parameters - struct RosenbrockSolverParameters - { - size_t stages_{}; - size_t upper_limit_tolerance_{}; - size_t max_number_of_steps_{ 1000 }; - - double round_off_{ std::numeric_limits::epsilon() }; // Unit roundoff (1+round_off)>1 - double factor_min_{ 0.2 }; // solver step size minimum boundary - double factor_max_{ 6 }; // solver step size maximum boundary - double rejection_factor_decrease_{ 0.1 }; // used to decrease the step after 2 successive rejections - double safety_factor_{ 0.9 }; // safety factor in new step size computation - - double h_min_{ 0.0 }; // step size min [s] - double h_max_{ - 0.0 - }; // step size max [s] (if zero or greater than the solver time-step, the time-step passed to the solver will be used) - double h_start_{ 0.0 }; // step size start [s] (if zero, 1.0e-6 will be used, if greater than h_max, h_max will be used) - - // Does the stage i require a new function evaluation (ros_NewF(i)=TRUE) - // or does it re-use the function evaluation from stage i-1 (ros_NewF(i)=FALSE) - std::array - new_function_evaluation_{}; // which steps reuse the previous iterations evaluation or do a new evaluation - - double estimator_of_local_order_{}; // the minumum between the main and the embedded scheme orders plus one - - // The coefficient matrices A and C are strictly lower triangular. - // The lower triangular (subdiagonal) elements are stored in row-wise order: - // A(2,1) = ros_A(1), A(3,1)=ros_A(2), A(3,2)=ros_A(3), etc. - // The general mapping formula is: - // A(i,j) = ros_A( (i-1)*(i-2)/2 + j ) - // C(i,j) = ros_C( (i-1)*(i-2)/2 + j ) - std::array a_{}; // coefficient matrix a - std::array c_{}; // coefficient matrix c - std::array m_{}; // coefficients for new step evaluation - std::array e_{}; // error estimation coefficients - - // Y_stage_i ~ Y( T + H*Alpha_i ) - std::array alpha_{}; - // Gamma_i = \sum_j gamma_{i,j} - std::array gamma_{}; - - double absolute_tolerance_{ 1e-3 }; - double relative_tolerance_{ 1e-4 }; - - size_t number_of_grid_cells_{ 1 }; // Number of grid cells to solve simultaneously - bool reorder_state_{ true }; // Reorder state during solver construction to minimize LU fill-in - bool check_singularity_{ false }; // Check for singular A matrix in linear solve of A x = b - - // Print RosenbrockSolverParameters to console - void print() const; - - /// @brief an L-stable method, 2 stages, order 2 - /// @param number_of_grid_cells - /// @param reorder_state - /// @return - static RosenbrockSolverParameters two_stage_rosenbrock_parameters( - size_t number_of_grid_cells = 1, - bool reorder_state = true); - /// @brief an L-stable method, 3 stages, order 3, 2 function evaluations - /// @param number_of_grid_cells - /// @param reorder_state - /// @return - static RosenbrockSolverParameters three_stage_rosenbrock_parameters( - size_t number_of_grid_cells = 1, - bool reorder_state = true); - /// @brief L-stable rosenbrock method of order 4, with 4 stages - /// @param number_of_grid_cells - /// @param reorder_state - /// @return - static RosenbrockSolverParameters four_stage_rosenbrock_parameters( - size_t number_of_grid_cells = 1, - bool reorder_state = true); - /// @brief A stiffly-stable method, 4 stages, order 3 - /// @param number_of_grid_cells - /// @param reorder_state - /// @return - static RosenbrockSolverParameters four_stage_differential_algebraic_rosenbrock_parameters( - size_t number_of_grid_cells = 1, - bool reorder_state = true); - /// @brief stiffly-stable rosenbrock method of order 4, with 6 stages - /// @param number_of_grid_cells - /// @param reorder_state - /// @return - static RosenbrockSolverParameters six_stage_differential_algebraic_rosenbrock_parameters( - size_t number_of_grid_cells = 1, - bool reorder_state = true); - - private: - RosenbrockSolverParameters() = default; - }; - /// @brief The final state the solver was in after the Solve function finishes enum class SolverState { @@ -150,6 +59,37 @@ namespace micm std::string StateToString(const SolverState& state); + struct SolverStats + { + /// @brief The number of forcing function calls + uint64_t function_calls{}; + /// @brief The number of jacobian function calls + uint64_t jacobian_updates{}; + /// @brief The total number of internal time steps taken + uint64_t number_of_steps{}; + /// @brief The number of accepted integrations + uint64_t accepted{}; + /// @brief The number of rejected integrations + uint64_t rejected{}; + /// @brief The number of LU decompositions + uint64_t decompositions{}; + /// @brief The number of linear solves + uint64_t solves{}; + /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix is never singular + uint64_t singular{}; + /// @brief The cumulative amount of time spent calculating the forcing function + std::chrono::duration total_forcing_time{}; + /// @brief The cumulative amount of time spent calculating the jacobian + std::chrono::duration total_jacobian_time{}; + /// @brief The cumulative amount of time spent calculating the linear factorization + std::chrono::duration total_linear_factor_time{}; + /// @brief The cumulative amount of time spent calculating the linear solve + std::chrono::duration total_linear_solve_time{}; + + /// @brief Set all member variables to zero + void Reset(); + }; + /// @brief An implementation of the Rosenbrock ODE solver /// /// The template parameter is the type of matrix to use @@ -160,37 +100,6 @@ namespace micm class RosenbrockSolver { public: - struct SolverStats - { - /// @brief The number of forcing function calls - uint64_t function_calls{}; - /// @brief The number of jacobian function calls - uint64_t jacobian_updates{}; - /// @brief The total number of internal time steps taken - uint64_t number_of_steps{}; - /// @brief The number of accepted integrations - uint64_t accepted{}; - /// @brief The number of rejected integrations - uint64_t rejected{}; - /// @brief The number of LU decompositions - uint64_t decompositions{}; - /// @brief The number of linear solvers - uint64_t solves{}; - /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix - /// is never singular - uint64_t singular{}; - /// @brief The cumulative amount of time spent calculating the forcing function - std::chrono::duration total_forcing_time{}; - /// @brief The cumulative amount of time spent calculating the jacobian - std::chrono::duration total_jacobian_time{}; - /// @brief The cumulative amount of time spent calculating the linear factorization - std::chrono::duration total_linear_factor_time{}; - /// @brief The cumulative amount of time spent calculating the linear solve - std::chrono::duration total_linear_solve_time{}; - - /// @brief Set all member variables to zero - void Reset(); - }; struct [[nodiscard]] SolverResult { @@ -204,16 +113,11 @@ namespace micm double final_time_{}; }; - System system_; std::vector processes_; RosenbrockSolverParameters parameters_; - std::function& variables, const std::size_t i)> state_reordering_; + StateParameters state_parameters_; ProcessSet process_set_; - SolverStats stats_; - SparseMatrixPolicy jacobian_; LinearSolverPolicy linear_solver_; - std::vector jacobian_diagonal_elements_; - size_t N_{}; static constexpr double delta_min_ = 1.0e-6; @@ -245,13 +149,13 @@ namespace micm /// @brief Returns a state object for use with the solver /// @return A object that can hold the full state of the chemical system - State GetState() const; + virtual State GetState() const; /// @brief Advances the given step over the specified time step /// @param time_step Time [s] to advance the state by /// @return A struct containing results and a status code template - SolverResult Solve(double time_step, State& state) noexcept; + SolverResult Solve(double time_step, State& state) noexcept; /// @brief Calculate a chemical forcing /// @param rate_constants List of rate constants for each needed species @@ -272,7 +176,7 @@ namespace micm /// @brief Update the rate constants for the environment state /// @param state The current state of the chemical system - void UpdateState(State& state); + void UpdateState(State& state); /// @brief Compute the derivative of the forcing w.r.t. each chemical, the jacobian /// @param rate_constants List of rate constants for each needed species @@ -289,7 +193,7 @@ namespace micm /// @param singular indicates if the matrix is singular /// @param number_densities constituent concentration (molec/cm^3) /// @param rate_constants Rate constants for each process (molecule/cm3)^(n-1) s-1 - void LinearFactor(double& H, const double gamma, bool& singular, const MatrixPolicy& number_densities); + void LinearFactor(double& H, const double gamma, bool& singular, const MatrixPolicy& number_densities, SolverStats& stats, State& state); protected: /// @brief Computes the scaled norm of the vector errors diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index 4f3f1b7eb..ed9cb9342 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -17,372 +17,11 @@ } namespace micm -{ - // - // RosenbrockSolverParameters - // - inline void RosenbrockSolverParameters::print() const - { - std::cout << "stages_: " << stages_ << std::endl; - std::cout << "upper_limit_tolerance_: " << upper_limit_tolerance_ << std::endl; - std::cout << "max_number_of_steps_: " << max_number_of_steps_ << std::endl; - std::cout << "round_off_: " << round_off_ << std::endl; - std::cout << "factor_min_: " << factor_min_ << std::endl; - std::cout << "factor_max_: " << factor_max_ << std::endl; - std::cout << "rejection_factor_decrease_: " << rejection_factor_decrease_ << std::endl; - std::cout << "safety_factor_: " << safety_factor_ << std::endl; - std::cout << "h_min_: " << h_min_ << std::endl; - std::cout << "h_max_: " << h_max_ << std::endl; - std::cout << "h_start_: " << h_start_ << std::endl; - std::cout << "new_function_evaluation_: "; - for (bool val : new_function_evaluation_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "estimator_of_local_order_: " << estimator_of_local_order_ << std::endl; - std::cout << "a_: "; - for (double val : a_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "c_: "; - for (double val : c_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "m_: "; - for (double val : m_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "e_: "; - for (double val : e_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "alpha_: "; - for (double val : alpha_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "gamma_: "; - for (double val : gamma_) - std::cout << val << " "; - std::cout << std::endl; - std::cout << "absolute_tolerance_: " << absolute_tolerance_ << std::endl; - std::cout << "relative_tolerance_: " << relative_tolerance_ << std::endl; - std::cout << "number_of_grid_cells_: " << number_of_grid_cells_ << std::endl; - } - - inline RosenbrockSolverParameters RosenbrockSolverParameters::two_stage_rosenbrock_parameters( - size_t number_of_grid_cells, - bool reorder_state) - { - // an L-stable method, 2 stages, order 2 - - RosenbrockSolverParameters parameters; - double g = 1.0 + 1.0 / std::sqrt(2.0); - - parameters.stages_ = 2; - - parameters.a_.fill(0); - parameters.a_[0] = 1.0 / g; - - parameters.c_.fill(0); - parameters.c_[0] = -2.0 / g; - - // Both stages require a new function evaluation - parameters.new_function_evaluation_.fill(true); - - parameters.m_.fill(0); - parameters.m_[0] = (3.0) / (2.0 * g); - parameters.m_[1] = (1.0) / (2.0 * g); - - parameters.e_.fill(0); - parameters.e_[0] = 1.0 / (2.0 * g); - parameters.e_[1] = 1.0 / (2.0 * g); - - parameters.estimator_of_local_order_ = 2.0; - - parameters.alpha_.fill(0); - parameters.alpha_[0] = 0.0; - parameters.alpha_[1] = 1.0; - - parameters.gamma_.fill(0); - parameters.gamma_[0] = g; - parameters.gamma_[1] = -g; - - parameters.number_of_grid_cells_ = number_of_grid_cells; - parameters.reorder_state_ = reorder_state; - - return parameters; - } - - inline RosenbrockSolverParameters RosenbrockSolverParameters::three_stage_rosenbrock_parameters( - size_t number_of_grid_cells, - bool reorder_state) - { - // an L-stable method, 3 stages, order 3, 2 function evaluations - // - // original formaulation for three stages: - // Sandu, A., Verwer, J.G., Blom, J.G., Spee, E.J., Carmichael, G.R., Potra, F.A., 1997. - // Benchmarking stiff ode solvers for atmospheric chemistry problems II: Rosenbrock solvers. - // Atmospheric Environment 31, 3459–3472. https://doi.org/10.1016/S1352-2310(97)83212-8 - RosenbrockSolverParameters parameters; - - parameters.stages_ = 3; - - parameters.a_.fill(0); - parameters.a_[0] = 1; - parameters.a_[1] = 1; - parameters.a_[2] = 0; - - parameters.c_.fill(0); - parameters.c_[0] = -0.10156171083877702091975600115545e+01; - parameters.c_[1] = 0.40759956452537699824805835358067e+01; - parameters.c_[2] = 0.92076794298330791242156818474003e+01; - - parameters.new_function_evaluation_.fill(false); - parameters.new_function_evaluation_[0] = true; - parameters.new_function_evaluation_[1] = true; - parameters.new_function_evaluation_[2] = false; - parameters.m_.fill(0); - parameters.m_[0] = 0.1e+01; - parameters.m_[1] = 0.61697947043828245592553615689730e+01; - parameters.m_[2] = -0.42772256543218573326238373806514; - - parameters.e_.fill(0); - parameters.e_[0] = 0.5; - parameters.e_[1] = -0.29079558716805469821718236208017e+01; - parameters.e_[2] = 0.22354069897811569627360909276199; - - parameters.estimator_of_local_order_ = 3; - - parameters.alpha_.fill(0); - parameters.alpha_[0] = 0; - parameters.alpha_[1] = 0.43586652150845899941601945119356; - parameters.alpha_[2] = 0.43586652150845899941601945119356; - - parameters.gamma_.fill(0); - parameters.gamma_[0] = 0.43586652150845899941601945119356; - parameters.gamma_[1] = 0.24291996454816804366592249683314; - parameters.gamma_[2] = 0.21851380027664058511513169485832e+01; - - parameters.number_of_grid_cells_ = number_of_grid_cells; - parameters.reorder_state_ = reorder_state; - - return parameters; - } - - inline RosenbrockSolverParameters RosenbrockSolverParameters::four_stage_rosenbrock_parameters( - size_t number_of_grid_cells, - bool reorder_state) - { - // L-STABLE ROSENBROCK METHOD OF ORDER 4, WITH 4 STAGES - // L-STABLE EMBEDDED ROSENBROCK METHOD OF ORDER 3 - // - // E. HAIRER AND G. WANNER, SOLVING ORDINARY DIFFERENTIAL - // EQUATIONS II. STIFF AND DIFFERENTIAL-ALGEBRAIC PROBLEMS. - // SPRINGER SERIES IN COMPUTATIONAL MATHEMATICS, - // SPRINGER-VERLAG (1990) - RosenbrockSolverParameters parameters; - - parameters.stages_ = 4; - - parameters.a_.fill(0); - parameters.a_[0] = 0.2000000000000000E+01; - parameters.a_[1] = 0.1867943637803922E+01; - parameters.a_[2] = 0.2344449711399156; - parameters.a_[3] = parameters.a_[1]; - parameters.a_[4] = parameters.a_[2]; - parameters.a_[5] = 0.0; - - parameters.c_.fill(0); - parameters.c_[0] = -0.7137615036412310E+01; - parameters.c_[1] = 0.2580708087951457E+01; - parameters.c_[2] = 0.6515950076447975; - parameters.c_[3] = -0.2137148994382534E+01; - parameters.c_[4] = -0.3214669691237626; - parameters.c_[5] = -0.6949742501781779; - - parameters.new_function_evaluation_.fill(false); - parameters.new_function_evaluation_[0] = true; - parameters.new_function_evaluation_[1] = true; - parameters.new_function_evaluation_[2] = true; - parameters.new_function_evaluation_[3] = false; - - parameters.m_.fill(0); - parameters.m_[0] = 0.2255570073418735E+01; - parameters.m_[1] = 0.2870493262186792; - parameters.m_[2] = 0.4353179431840180; - parameters.m_[3] = 0.1093502252409163E+01; - - parameters.e_.fill(0); - parameters.e_[0] = -0.2815431932141155; - parameters.e_[1] = -0.7276199124938920E-01; - parameters.e_[2] = -0.1082196201495311; - parameters.e_[3] = -0.1093502252409163E+01; - - parameters.estimator_of_local_order_ = 4.0; - - parameters.alpha_.fill(0); - parameters.alpha_[0] = 0.0; - parameters.alpha_[1] = 0.1145640000000000E+01; - parameters.alpha_[2] = 0.6552168638155900; - parameters.alpha_[3] = parameters.alpha_[2]; - - parameters.gamma_.fill(0); - parameters.gamma_[0] = 0.5728200000000000; - parameters.gamma_[1] = -0.1769193891319233E+01; - parameters.gamma_[2] = 0.7592633437920482; - parameters.gamma_[3] = -0.1049021087100450; - - parameters.number_of_grid_cells_ = number_of_grid_cells; - parameters.reorder_state_ = reorder_state; - - return parameters; - } - - inline RosenbrockSolverParameters RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters( - size_t number_of_grid_cells, - bool reorder_state) - { - // A STIFFLY-STABLE METHOD, 4 stages, order 3 - RosenbrockSolverParameters parameters; - - // Set the number of stages - parameters.stages_ = 4; - - parameters.a_.fill(0.0); - parameters.a_[0] = 0.0; - parameters.a_[1] = 2.0; - parameters.a_[2] = 0.0; - parameters.a_[3] = 2.0; - parameters.a_[4] = 0.0; - parameters.a_[5] = 1.0; - - parameters.c_.fill(0.0); - parameters.c_[0] = 4.0; - parameters.c_[1] = 1.0; - parameters.c_[2] = -1.0; - parameters.c_[3] = 1.0; - parameters.c_[4] = -1.0; - parameters.c_[5] = -(8.0 / 3.0); - - parameters.new_function_evaluation_.fill(false); - parameters.new_function_evaluation_[0] = true; - parameters.new_function_evaluation_[2] = true; - parameters.new_function_evaluation_[3] = true; - - parameters.m_.fill(0.0); - parameters.m_[0] = 2.0; - parameters.m_[2] = 1.0; - parameters.m_[3] = 1.0; - - parameters.e_.fill(0.0); - parameters.e_[3] = 1.0; - - parameters.estimator_of_local_order_ = 3.0; - - parameters.alpha_.fill(0.0); - parameters.alpha_[2] = 1.0; - parameters.alpha_[3] = 1.0; - - parameters.gamma_.fill(0.0); - parameters.gamma_[0] = 0.5; - parameters.gamma_[1] = 1.5; - - parameters.number_of_grid_cells_ = number_of_grid_cells; - parameters.reorder_state_ = reorder_state; - - return parameters; - } - - inline RosenbrockSolverParameters RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters( - size_t number_of_grid_cells, - bool reorder_state) - { - // STIFFLY-STABLE ROSENBROCK METHOD OF ORDER 4, WITH 6 STAGES - // - // E. HAIRER AND G. WANNER, SOLVING ORDINARY DIFFERENTIAL - // EQUATIONS II. STIFF AND DIFFERENTIAL-ALGEBRAIC PROBLEMS. - // SPRINGER SERIES IN COMPUTATIONAL MATHEMATICS, - // SPRINGER-VERLAG (1996) - - RosenbrockSolverParameters parameters; - - parameters.stages_ = 6; - - parameters.alpha_.fill(0.0); - parameters.alpha_[0] = 0.000; - parameters.alpha_[1] = 0.386; - parameters.alpha_[2] = 0.210; - parameters.alpha_[3] = 0.630; - parameters.alpha_[4] = 1.000; - parameters.alpha_[5] = 1.000; - - parameters.gamma_.fill(0.0); - parameters.gamma_[0] = 0.2500000000000000; - parameters.gamma_[1] = -0.1043000000000000; - parameters.gamma_[2] = 0.1035000000000000; - parameters.gamma_[3] = -0.3620000000000023E-01; - parameters.gamma_[4] = 0.0; - parameters.gamma_[5] = 0.0; - - parameters.a_.fill(0.0); - parameters.a_[0] = 0.1544000000000000E+01; - parameters.a_[1] = 0.9466785280815826; - parameters.a_[2] = 0.2557011698983284; - parameters.a_[3] = 0.3314825187068521E+01; - parameters.a_[4] = 0.2896124015972201E+01; - parameters.a_[5] = 0.9986419139977817; - parameters.a_[6] = 0.1221224509226641E+01; - parameters.a_[7] = 0.6019134481288629E+01; - parameters.a_[8] = 0.1253708332932087E+02; - parameters.a_[9] = -0.6878860361058950; - parameters.a_[10] = parameters.a_[6]; - parameters.a_[11] = parameters.a_[7]; - parameters.a_[12] = parameters.a_[8]; - parameters.a_[13] = parameters.a_[9]; - parameters.a_[14] = 1.0; - - parameters.c_.fill(0.0); - parameters.c_[0] = -0.5668800000000000E+01; - parameters.c_[1] = -0.2430093356833875E+01; - parameters.c_[2] = -0.2063599157091915; - parameters.c_[3] = -0.1073529058151375; - parameters.c_[4] = -0.9594562251023355E+01; - parameters.c_[5] = -0.2047028614809616E+02; - parameters.c_[6] = 0.7496443313967647E+01; - parameters.c_[7] = -0.1024680431464352E+02; - parameters.c_[8] = -0.3399990352819905E+02; - parameters.c_[9] = 0.1170890893206160E+02; - parameters.c_[10] = 0.8083246795921522E+01; - parameters.c_[11] = -0.7981132988064893E+01; - parameters.c_[12] = -0.3152159432874371E+02; - parameters.c_[13] = 0.1631930543123136E+02; - parameters.c_[14] = -0.6058818238834054E+01; - - parameters.m_.fill(0.0); - parameters.m_[0] = parameters.a_[6]; - parameters.m_[1] = parameters.a_[7]; - parameters.m_[2] = parameters.a_[8]; - parameters.m_[3] = parameters.a_[9]; - parameters.m_[4] = 1.0; - parameters.m_[5] = 1.0; - - parameters.e_.fill(0.0); - parameters.e_[5] = 1.0; - - parameters.new_function_evaluation_.fill(true); - - parameters.estimator_of_local_order_ = 4.0; - - parameters.number_of_grid_cells_ = number_of_grid_cells; - parameters.reorder_state_ = reorder_state; - - return parameters; - } - +{ // // RosenbrockSolver // - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::SolverStats::Reset() + inline void SolverStats::Reset() { function_calls = 0; jacobian_updates = 0; @@ -411,21 +50,15 @@ namespace micm case SolverState::NaNDetected: return "NaNDetected"; default: return "Unknown"; } - return ""; } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> inline RosenbrockSolver::RosenbrockSolver() - : system_(), - processes_(), + : processes_(), parameters_(RosenbrockSolverParameters::three_stage_rosenbrock_parameters()), - state_reordering_(), + state_parameters_(), process_set_(), - stats_(), - jacobian_(), - linear_solver_(), - jacobian_diagonal_elements_(), - N_(system_.StateSize() * parameters_.number_of_grid_cells_) + linear_solver_() { } @@ -450,59 +83,74 @@ namespace micm const std::vector& processes, const RosenbrockSolverParameters& parameters, const std::function, double)> create_linear_solver) - : system_(system), - processes_(processes), + : processes_(processes), parameters_(parameters), - state_reordering_(), + state_parameters_(), process_set_(), - stats_(), - jacobian_(), - linear_solver_(), - jacobian_diagonal_elements_(), - N_(system_.StateSize() * parameters_.number_of_grid_cells_) + linear_solver_() { + std::map variable_map; + std::function& variables, const std::size_t i)> state_reordering; + + std::size_t index = 0; + for (auto& name : system.UniqueNames()) + variable_map[name] = index++; + // generate a state-vector reordering function to reduce fill-in in linear solver if (parameters_.reorder_state_) { // get unsorted Jacobian non-zero elements - auto unsorted_process_set = ProcessSet(processes, GetState()); + auto unsorted_process_set = ProcessSet(processes, variable_map); auto unsorted_jac_elements = unsorted_process_set.NonZeroJacobianElements(); - MatrixPolicy unsorted_jac_non_zeros(system_.StateSize(), system_.StateSize(), 0); + MatrixPolicy unsorted_jac_non_zeros(system.StateSize(), system.StateSize(), 0); for (auto& elem : unsorted_jac_elements) unsorted_jac_non_zeros[elem.first][elem.second] = 1; auto reorder_map = DiagonalMarkowitzReorder(unsorted_jac_non_zeros); - state_reordering_ = [=](const std::vector& variables, const std::size_t i) + state_reordering = [=](const std::vector& variables, const std::size_t i) { return variables[reorder_map[i]]; }; - } - process_set_ = ProcessSet(processes, GetState()); - auto builder = - SparseMatrixPolicy::create(system_.StateSize()).number_of_blocks(parameters_.number_of_grid_cells_); - auto jac_elements = process_set_.NonZeroJacobianElements(); - for (auto& elem : jac_elements) - builder = builder.with_element(elem.first, elem.second); - // Always include diagonal elements - for (std::size_t i = 0; i < system_.StateSize(); ++i) - builder = builder.with_element(i, i); - - jacobian_ = builder; - linear_solver_ = std::move(create_linear_solver(jacobian_, 1.0e-30)); - process_set_.SetJacobianFlatIds(jacobian_); - for (std::size_t i = 0; i < jacobian_[0].size(); ++i) - jacobian_diagonal_elements_.push_back(jacobian_.VectorIndex(0, i, i)); - } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline State RosenbrockSolver::GetState() const - { + variable_map.clear(); + std::size_t index = 0; + for (auto& name : system.UniqueNames(state_reordering)) + variable_map[name] = index++; + } + + // setup the state_parameters std::vector param_labels{}; for (const auto& process : processes_) if (process.rate_constant_) for (auto& label : process.rate_constant_->CustomParameters()) param_labels.push_back(label); - return State{ micm::StateParameters{ .state_variable_names_ = system_.UniqueNames(state_reordering_), - .custom_rate_parameter_labels_ = param_labels, - .number_of_grid_cells_ = parameters_.number_of_grid_cells_, - .number_of_rate_constants_ = processes_.size() } }; + + process_set_ = ProcessSet(processes, variable_map); + + auto jacobian = build_jacobian( + process_set_.NonZeroJacobianElements(), + parameters_.number_of_grid_cells_, + system.StateSize() + ); + + std::vector jacobian_diagonal_elements; + for (std::size_t i = 0; i < jacobian[0].size(); ++i) + jacobian_diagonal_elements.push_back(jacobian.VectorIndex(0, i, i)); + + state_parameters_ = { + .number_of_grid_cells_ = parameters_.number_of_grid_cells_, + .number_of_rate_constants_ = processes_.size(), + .variable_names_ = system.UniqueNames(state_reordering), + .custom_rate_parameter_labels_ = param_labels, + .jacobian_diagonal_elements_ = jacobian_diagonal_elements, + .nonzero_jacobian_elements_ = process_set_.NonZeroJacobianElements() + }; + + process_set_.SetJacobianFlatIds(jacobian); + linear_solver_ = std::move(create_linear_solver(jacobian, 1.0e-30)); + } + + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + inline State RosenbrockSolver::GetState() const + { + return State{ state_parameters_ }; } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -510,7 +158,7 @@ namespace micm inline typename RosenbrockSolver::SolverResult RosenbrockSolver::Solve( double time_step, - State& state) noexcept + State& state) noexcept { typename RosenbrockSolver::SolverResult result{}; result.state_ = SolverState::Running; @@ -524,7 +172,7 @@ namespace micm const double h_start = parameters_.h_start_ == 0.0 ? std::max(parameters_.h_min_, delta_min_) : std::min(h_max, parameters_.h_start_); - stats_.Reset(); + SolverStats stats; UpdateState(state); for (std::size_t i = 0; i < parameters_.stages_; ++i) @@ -544,7 +192,7 @@ namespace micm while ((present_time - time_step + parameters_.round_off_) <= 0 && (result.state_ == SolverState::Running)) { - if (stats_.number_of_steps > parameters_.max_number_of_steps_) + if (stats.number_of_steps > parameters_.max_number_of_steps_) { result.state_ = SolverState::ConvergenceExceededMaxSteps; break; @@ -560,10 +208,12 @@ namespace micm H = std::min(H, std::abs(time_step - present_time)); // compute the forcing at the beginning of the current time - TIMED_METHOD(stats_.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Y, initial_forcing); + TIMED_METHOD(stats.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Y, initial_forcing); + stats.function_calls += 1; // compute the jacobian at the beginning of the current time - TIMED_METHOD(stats_.total_jacobian_time, time_it, CalculateJacobian, state.rate_constants_, Y, jacobian_); + TIMED_METHOD(stats.total_jacobian_time, time_it, CalculateJacobian, state.rate_constants_, Y, state.jacobian_); + stats.jacobian_updates += 1; bool accepted = false; // Repeat step calculation until current step accepted @@ -571,7 +221,7 @@ namespace micm { bool is_singular{ false }; // Form and factor the rosenbrock ode jacobian - TIMED_METHOD(stats_.total_linear_factor_time, time_it, LinearFactor, H, parameters_.gamma_[0], is_singular, Y); + TIMED_METHOD(stats.total_linear_factor_time, time_it, LinearFactor, H, parameters_.gamma_[0], is_singular, Y, stats, state); if (is_singular) { result.state_ = SolverState::RepeatedlySingularMatrix; @@ -596,7 +246,8 @@ namespace micm auto a = parameters_.a_[stage_combinations + j]; Ynew.ForEach([&](double& iYnew, double& iKj) { iYnew += a * iKj; }, K[j]); } - TIMED_METHOD(stats_.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Ynew, forcing); + TIMED_METHOD(stats.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Ynew, forcing); + stats.function_calls += 1; } } K[stage].AsVector().assign(forcing.AsVector().begin(), forcing.AsVector().end()); @@ -606,8 +257,8 @@ namespace micm K[stage].ForEach([&](double& iKstage, double& iKj) { iKstage += HC * iKj; }, K[j]); } temp.AsVector().assign(K[stage].AsVector().begin(), K[stage].AsVector().end()); - TIMED_METHOD(stats_.total_linear_solve_time, time_it, linear_solver_.template Solve, temp, K[stage]); - stats_.solves += 1; + TIMED_METHOD(stats.total_linear_solve_time, time_it, linear_solver_.template Solve, temp, K[stage], state.lower_matrix_, state.upper_matrix_); + stats.solves += 1; } // Compute the new solution @@ -630,7 +281,7 @@ namespace micm parameters_.safety_factor_ / std::pow(error, 1 / parameters_.estimator_of_local_order_))); double Hnew = H * fac; - stats_.number_of_steps += 1; + stats.number_of_steps += 1; // Check the error magnitude and adjust step size if (std::isnan(error)) @@ -641,7 +292,7 @@ namespace micm } else if ((error < 1) || (H < parameters_.h_min_)) { - stats_.accepted += 1; + stats.accepted += 1; present_time = present_time + H; Y.AsVector().assign(Ynew.AsVector().begin(), Ynew.AsVector().end()); Hnew = std::max(parameters_.h_min_, std::min(Hnew, h_max)); @@ -665,9 +316,9 @@ namespace micm reject_more_h = reject_last_h; reject_last_h = true; H = Hnew; - if (stats_.accepted >= 1) + if (stats.accepted >= 1) { - stats_.rejected += 1; + stats.rejected += 1; } } } @@ -679,7 +330,7 @@ namespace micm } result.final_time_ = present_time; - result.stats_ = stats_; + result.stats_ = stats; result.result_ = Y; return result; } @@ -692,7 +343,6 @@ namespace micm { std::fill(forcing.AsVector().begin(), forcing.AsVector().end(), 0.0); process_set_.AddForcingTerms(rate_constants, number_densities, forcing); - stats_.function_calls += 1; } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -706,7 +356,7 @@ namespace micm for (std::size_t i_block = 0; i_block < jacobian.size(); ++i_block) { auto jacobian_vector = std::next(jacobian.AsVector().begin(), i_block * jacobian.FlatBlockSize()); - for (const auto& i_elem : jacobian_diagonal_elements_) + for (const auto& i_elem : state_parameters_.jacobian_diagonal_elements_) jacobian_vector[i_elem] += alpha; } } @@ -723,7 +373,7 @@ namespace micm for (std::size_t i_group = 0; i_group < jacobian.NumberOfGroups(jacobian.size()); ++i_group) { auto jacobian_vector = std::next(jacobian.AsVector().begin(), i_group * jacobian.GroupSize(jacobian.FlatBlockSize())); - for (const auto& i_elem : jacobian_diagonal_elements_) + for (const auto& i_elem : state_parameters_.jacobian_diagonal_elements_) for (std::size_t i_cell = 0; i_cell < n_cells; ++i_cell) jacobian_vector[i_elem + i_cell] += alpha; } @@ -737,11 +387,10 @@ namespace micm { std::fill(jacobian.AsVector().begin(), jacobian.AsVector().end(), 0.0); process_set_.AddJacobianTerms(rate_constants, number_densities, jacobian); - stats_.jacobian_updates += 1; } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::UpdateState(State& state) + inline void RosenbrockSolver::UpdateState(State& state) { Process::UpdateState(processes_, state); } @@ -751,11 +400,11 @@ namespace micm double& H, const double gamma, bool& singular, - const MatrixPolicy& number_densities) + const MatrixPolicy& number_densities, + SolverStats& stats, + State& state) { - // TODO: invesitage this function. The fortran equivalent appears to have a bug. - // From my understanding the fortran do loop would only ever do one iteration and is equivalent to what's below - SparseMatrixPolicy jacobian = jacobian_; + auto jacobian = state.jacobian_; uint64_t n_consecutive = 0; singular = false; while (true) @@ -764,16 +413,16 @@ namespace micm AlphaMinusJacobian(jacobian, alpha); if (parameters_.check_singularity_) { - linear_solver_.Factor(jacobian, singular); + linear_solver_.Factor(jacobian, state.lower_matrix_, state.upper_matrix_, singular); } else { singular = false; - linear_solver_.Factor(jacobian); + linear_solver_.Factor(jacobian, state.lower_matrix_, state.upper_matrix_); } singular = false; // TODO This should be evaluated in some way - stats_.decompositions += 1; + stats.decompositions += 1; if (!singular) break; - stats_.singular += 1; + stats.singular += 1; if (++n_consecutive > 5) break; H /= 2; @@ -792,10 +441,11 @@ namespace micm auto _y = Y.AsVector(); auto _ynew = Ynew.AsVector(); auto _errors = errors.AsVector(); + size_t N = Y.AsVector().size(); double error = 0; - for (size_t i = 0; i < N_; ++i) + for (size_t i = 0; i < N; ++i) { double ymax = std::max(std::abs(_y[i]), std::abs(_ynew[i])); double scale = parameters_.absolute_tolerance_ + parameters_.relative_tolerance_ * ymax; @@ -803,7 +453,7 @@ namespace micm } double error_min_ = 1.0e-10; - return std::max(std::sqrt(error / N_), error_min_); + return std::max(std::sqrt(error / N), error_min_); } } // namespace micm diff --git a/include/micm/solver/rosenbrock_solver_parameters.hpp b/include/micm/solver/rosenbrock_solver_parameters.hpp new file mode 100644 index 000000000..bfb4276fe --- /dev/null +++ b/include/micm/solver/rosenbrock_solver_parameters.hpp @@ -0,0 +1,462 @@ +// Copyright (C) 2023 National Center for Atmospheric Research +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include + +namespace micm +{ + + /// @brief Rosenbrock solver parameters + struct RosenbrockSolverParameters + { + size_t stages_{}; + size_t upper_limit_tolerance_{}; + size_t max_number_of_steps_{ 1000 }; + + double round_off_{ std::numeric_limits::epsilon() }; // Unit roundoff (1+round_off)>1 + double factor_min_{ 0.2 }; // solver step size minimum boundary + double factor_max_{ 6 }; // solver step size maximum boundary + double rejection_factor_decrease_{ 0.1 }; // used to decrease the step after 2 successive rejections + double safety_factor_{ 0.9 }; // safety factor in new step size computation + + double h_min_{ 0.0 }; // step size min [s] + double h_max_{ + 0.0 + }; // step size max [s] (if zero or greater than the solver time-step, the time-step passed to the solver will be used) + double h_start_{ 0.0 }; // step size start [s] (if zero, 1.0e-6 will be used, if greater than h_max, h_max will be used) + + // Does the stage i require a new function evaluation (ros_NewF(i)=TRUE) + // or does it re-use the function evaluation from stage i-1 (ros_NewF(i)=FALSE) + std::array + new_function_evaluation_{}; // which steps reuse the previous iterations evaluation or do a new evaluation + + double estimator_of_local_order_{}; // the minumum between the main and the embedded scheme orders plus one + + // The coefficient matrices A and C are strictly lower triangular. + // The lower triangular (subdiagonal) elements are stored in row-wise order: + // A(2,1) = ros_A(1), A(3,1)=ros_A(2), A(3,2)=ros_A(3), etc. + // The general mapping formula is: + // A(i,j) = ros_A( (i-1)*(i-2)/2 + j ) + // C(i,j) = ros_C( (i-1)*(i-2)/2 + j ) + std::array a_{}; // coefficient matrix a + std::array c_{}; // coefficient matrix c + std::array m_{}; // coefficients for new step evaluation + std::array e_{}; // error estimation coefficients + + // Y_stage_i ~ Y( T + H*Alpha_i ) + std::array alpha_{}; + // Gamma_i = \sum_j gamma_{i,j} + std::array gamma_{}; + + double absolute_tolerance_{ 1e-3 }; + double relative_tolerance_{ 1e-4 }; + + size_t number_of_grid_cells_{ 1 }; // Number of grid cells to solve simultaneously + bool reorder_state_{ true }; // Reorder state during solver construction to minimize LU fill-in + bool check_singularity_{ false }; // Check for singular A matrix in linear solve of A x = b + + // Print RosenbrockSolverParameters to console + void print() const; + + /// @brief an L-stable method, 2 stages, order 2 + /// @param number_of_grid_cells + /// @param reorder_state + /// @return + static RosenbrockSolverParameters two_stage_rosenbrock_parameters( + size_t number_of_grid_cells = 1, + bool reorder_state = true); + /// @brief an L-stable method, 3 stages, order 3, 2 function evaluations + /// @param number_of_grid_cells + /// @param reorder_state + /// @return + static RosenbrockSolverParameters three_stage_rosenbrock_parameters( + size_t number_of_grid_cells = 1, + bool reorder_state = true); + /// @brief L-stable rosenbrock method of order 4, with 4 stages + /// @param number_of_grid_cells + /// @param reorder_state + /// @return + static RosenbrockSolverParameters four_stage_rosenbrock_parameters( + size_t number_of_grid_cells = 1, + bool reorder_state = true); + /// @brief A stiffly-stable method, 4 stages, order 3 + /// @param number_of_grid_cells + /// @param reorder_state + /// @return + static RosenbrockSolverParameters four_stage_differential_algebraic_rosenbrock_parameters( + size_t number_of_grid_cells = 1, + bool reorder_state = true); + /// @brief stiffly-stable rosenbrock method of order 4, with 6 stages + /// @param number_of_grid_cells + /// @param reorder_state + /// @return + static RosenbrockSolverParameters six_stage_differential_algebraic_rosenbrock_parameters( + size_t number_of_grid_cells = 1, + bool reorder_state = true); + + private: + RosenbrockSolverParameters() = default; + }; + + inline void RosenbrockSolverParameters::print() const + { + std::cout << "stages_: " << stages_ << std::endl; + std::cout << "upper_limit_tolerance_: " << upper_limit_tolerance_ << std::endl; + std::cout << "max_number_of_steps_: " << max_number_of_steps_ << std::endl; + std::cout << "round_off_: " << round_off_ << std::endl; + std::cout << "factor_min_: " << factor_min_ << std::endl; + std::cout << "factor_max_: " << factor_max_ << std::endl; + std::cout << "rejection_factor_decrease_: " << rejection_factor_decrease_ << std::endl; + std::cout << "safety_factor_: " << safety_factor_ << std::endl; + std::cout << "h_min_: " << h_min_ << std::endl; + std::cout << "h_max_: " << h_max_ << std::endl; + std::cout << "h_start_: " << h_start_ << std::endl; + std::cout << "new_function_evaluation_: "; + for (bool val : new_function_evaluation_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "estimator_of_local_order_: " << estimator_of_local_order_ << std::endl; + std::cout << "a_: "; + for (double val : a_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "c_: "; + for (double val : c_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "m_: "; + for (double val : m_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "e_: "; + for (double val : e_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "alpha_: "; + for (double val : alpha_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "gamma_: "; + for (double val : gamma_) + std::cout << val << " "; + std::cout << std::endl; + std::cout << "absolute_tolerance_: " << absolute_tolerance_ << std::endl; + std::cout << "relative_tolerance_: " << relative_tolerance_ << std::endl; + std::cout << "number_of_grid_cells_: " << number_of_grid_cells_ << std::endl; + } + + inline RosenbrockSolverParameters RosenbrockSolverParameters::two_stage_rosenbrock_parameters( + size_t number_of_grid_cells, + bool reorder_state) + { + // an L-stable method, 2 stages, order 2 + + RosenbrockSolverParameters parameters; + double g = 1.0 + 1.0 / std::sqrt(2.0); + + parameters.stages_ = 2; + + parameters.a_.fill(0); + parameters.a_[0] = 1.0 / g; + + parameters.c_.fill(0); + parameters.c_[0] = -2.0 / g; + + // Both stages require a new function evaluation + parameters.new_function_evaluation_.fill(true); + + parameters.m_.fill(0); + parameters.m_[0] = (3.0) / (2.0 * g); + parameters.m_[1] = (1.0) / (2.0 * g); + + parameters.e_.fill(0); + parameters.e_[0] = 1.0 / (2.0 * g); + parameters.e_[1] = 1.0 / (2.0 * g); + + parameters.estimator_of_local_order_ = 2.0; + + parameters.alpha_.fill(0); + parameters.alpha_[0] = 0.0; + parameters.alpha_[1] = 1.0; + + parameters.gamma_.fill(0); + parameters.gamma_[0] = g; + parameters.gamma_[1] = -g; + + parameters.number_of_grid_cells_ = number_of_grid_cells; + parameters.reorder_state_ = reorder_state; + + return parameters; + } + + inline RosenbrockSolverParameters RosenbrockSolverParameters::three_stage_rosenbrock_parameters( + size_t number_of_grid_cells, + bool reorder_state) + { + // an L-stable method, 3 stages, order 3, 2 function evaluations + // + // original formaulation for three stages: + // Sandu, A., Verwer, J.G., Blom, J.G., Spee, E.J., Carmichael, G.R., Potra, F.A., 1997. + // Benchmarking stiff ode solvers for atmospheric chemistry problems II: Rosenbrock solvers. + // Atmospheric Environment 31, 3459–3472. https://doi.org/10.1016/S1352-2310(97)83212-8 + RosenbrockSolverParameters parameters; + + parameters.stages_ = 3; + + parameters.a_.fill(0); + parameters.a_[0] = 1; + parameters.a_[1] = 1; + parameters.a_[2] = 0; + + parameters.c_.fill(0); + parameters.c_[0] = -0.10156171083877702091975600115545e+01; + parameters.c_[1] = 0.40759956452537699824805835358067e+01; + parameters.c_[2] = 0.92076794298330791242156818474003e+01; + + parameters.new_function_evaluation_.fill(false); + parameters.new_function_evaluation_[0] = true; + parameters.new_function_evaluation_[1] = true; + parameters.new_function_evaluation_[2] = false; + parameters.m_.fill(0); + parameters.m_[0] = 0.1e+01; + parameters.m_[1] = 0.61697947043828245592553615689730e+01; + parameters.m_[2] = -0.42772256543218573326238373806514; + + parameters.e_.fill(0); + parameters.e_[0] = 0.5; + parameters.e_[1] = -0.29079558716805469821718236208017e+01; + parameters.e_[2] = 0.22354069897811569627360909276199; + + parameters.estimator_of_local_order_ = 3; + + parameters.alpha_.fill(0); + parameters.alpha_[0] = 0; + parameters.alpha_[1] = 0.43586652150845899941601945119356; + parameters.alpha_[2] = 0.43586652150845899941601945119356; + + parameters.gamma_.fill(0); + parameters.gamma_[0] = 0.43586652150845899941601945119356; + parameters.gamma_[1] = 0.24291996454816804366592249683314; + parameters.gamma_[2] = 0.21851380027664058511513169485832e+01; + + parameters.number_of_grid_cells_ = number_of_grid_cells; + parameters.reorder_state_ = reorder_state; + + return parameters; + } + + inline RosenbrockSolverParameters RosenbrockSolverParameters::four_stage_rosenbrock_parameters( + size_t number_of_grid_cells, + bool reorder_state) + { + // L-STABLE ROSENBROCK METHOD OF ORDER 4, WITH 4 STAGES + // L-STABLE EMBEDDED ROSENBROCK METHOD OF ORDER 3 + // + // E. HAIRER AND G. WANNER, SOLVING ORDINARY DIFFERENTIAL + // EQUATIONS II. STIFF AND DIFFERENTIAL-ALGEBRAIC PROBLEMS. + // SPRINGER SERIES IN COMPUTATIONAL MATHEMATICS, + // SPRINGER-VERLAG (1990) + RosenbrockSolverParameters parameters; + + parameters.stages_ = 4; + + parameters.a_.fill(0); + parameters.a_[0] = 0.2000000000000000E+01; + parameters.a_[1] = 0.1867943637803922E+01; + parameters.a_[2] = 0.2344449711399156; + parameters.a_[3] = parameters.a_[1]; + parameters.a_[4] = parameters.a_[2]; + parameters.a_[5] = 0.0; + + parameters.c_.fill(0); + parameters.c_[0] = -0.7137615036412310E+01; + parameters.c_[1] = 0.2580708087951457E+01; + parameters.c_[2] = 0.6515950076447975; + parameters.c_[3] = -0.2137148994382534E+01; + parameters.c_[4] = -0.3214669691237626; + parameters.c_[5] = -0.6949742501781779; + + parameters.new_function_evaluation_.fill(false); + parameters.new_function_evaluation_[0] = true; + parameters.new_function_evaluation_[1] = true; + parameters.new_function_evaluation_[2] = true; + parameters.new_function_evaluation_[3] = false; + + parameters.m_.fill(0); + parameters.m_[0] = 0.2255570073418735E+01; + parameters.m_[1] = 0.2870493262186792; + parameters.m_[2] = 0.4353179431840180; + parameters.m_[3] = 0.1093502252409163E+01; + + parameters.e_.fill(0); + parameters.e_[0] = -0.2815431932141155; + parameters.e_[1] = -0.7276199124938920E-01; + parameters.e_[2] = -0.1082196201495311; + parameters.e_[3] = -0.1093502252409163E+01; + + parameters.estimator_of_local_order_ = 4.0; + + parameters.alpha_.fill(0); + parameters.alpha_[0] = 0.0; + parameters.alpha_[1] = 0.1145640000000000E+01; + parameters.alpha_[2] = 0.6552168638155900; + parameters.alpha_[3] = parameters.alpha_[2]; + + parameters.gamma_.fill(0); + parameters.gamma_[0] = 0.5728200000000000; + parameters.gamma_[1] = -0.1769193891319233E+01; + parameters.gamma_[2] = 0.7592633437920482; + parameters.gamma_[3] = -0.1049021087100450; + + parameters.number_of_grid_cells_ = number_of_grid_cells; + parameters.reorder_state_ = reorder_state; + + return parameters; + } + + inline RosenbrockSolverParameters RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters( + size_t number_of_grid_cells, + bool reorder_state) + { + // A STIFFLY-STABLE METHOD, 4 stages, order 3 + RosenbrockSolverParameters parameters; + + // Set the number of stages + parameters.stages_ = 4; + + parameters.a_.fill(0.0); + parameters.a_[0] = 0.0; + parameters.a_[1] = 2.0; + parameters.a_[2] = 0.0; + parameters.a_[3] = 2.0; + parameters.a_[4] = 0.0; + parameters.a_[5] = 1.0; + + parameters.c_.fill(0.0); + parameters.c_[0] = 4.0; + parameters.c_[1] = 1.0; + parameters.c_[2] = -1.0; + parameters.c_[3] = 1.0; + parameters.c_[4] = -1.0; + parameters.c_[5] = -(8.0 / 3.0); + + parameters.new_function_evaluation_.fill(false); + parameters.new_function_evaluation_[0] = true; + parameters.new_function_evaluation_[2] = true; + parameters.new_function_evaluation_[3] = true; + + parameters.m_.fill(0.0); + parameters.m_[0] = 2.0; + parameters.m_[2] = 1.0; + parameters.m_[3] = 1.0; + + parameters.e_.fill(0.0); + parameters.e_[3] = 1.0; + + parameters.estimator_of_local_order_ = 3.0; + + parameters.alpha_.fill(0.0); + parameters.alpha_[2] = 1.0; + parameters.alpha_[3] = 1.0; + + parameters.gamma_.fill(0.0); + parameters.gamma_[0] = 0.5; + parameters.gamma_[1] = 1.5; + + parameters.number_of_grid_cells_ = number_of_grid_cells; + parameters.reorder_state_ = reorder_state; + + return parameters; + } + + inline RosenbrockSolverParameters RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters( + size_t number_of_grid_cells, + bool reorder_state) + { + // STIFFLY-STABLE ROSENBROCK METHOD OF ORDER 4, WITH 6 STAGES + // + // E. HAIRER AND G. WANNER, SOLVING ORDINARY DIFFERENTIAL + // EQUATIONS II. STIFF AND DIFFERENTIAL-ALGEBRAIC PROBLEMS. + // SPRINGER SERIES IN COMPUTATIONAL MATHEMATICS, + // SPRINGER-VERLAG (1996) + + RosenbrockSolverParameters parameters; + + parameters.stages_ = 6; + + parameters.alpha_.fill(0.0); + parameters.alpha_[0] = 0.000; + parameters.alpha_[1] = 0.386; + parameters.alpha_[2] = 0.210; + parameters.alpha_[3] = 0.630; + parameters.alpha_[4] = 1.000; + parameters.alpha_[5] = 1.000; + + parameters.gamma_.fill(0.0); + parameters.gamma_[0] = 0.2500000000000000; + parameters.gamma_[1] = -0.1043000000000000; + parameters.gamma_[2] = 0.1035000000000000; + parameters.gamma_[3] = -0.3620000000000023E-01; + parameters.gamma_[4] = 0.0; + parameters.gamma_[5] = 0.0; + + parameters.a_.fill(0.0); + parameters.a_[0] = 0.1544000000000000E+01; + parameters.a_[1] = 0.9466785280815826; + parameters.a_[2] = 0.2557011698983284; + parameters.a_[3] = 0.3314825187068521E+01; + parameters.a_[4] = 0.2896124015972201E+01; + parameters.a_[5] = 0.9986419139977817; + parameters.a_[6] = 0.1221224509226641E+01; + parameters.a_[7] = 0.6019134481288629E+01; + parameters.a_[8] = 0.1253708332932087E+02; + parameters.a_[9] = -0.6878860361058950; + parameters.a_[10] = parameters.a_[6]; + parameters.a_[11] = parameters.a_[7]; + parameters.a_[12] = parameters.a_[8]; + parameters.a_[13] = parameters.a_[9]; + parameters.a_[14] = 1.0; + + parameters.c_.fill(0.0); + parameters.c_[0] = -0.5668800000000000E+01; + parameters.c_[1] = -0.2430093356833875E+01; + parameters.c_[2] = -0.2063599157091915; + parameters.c_[3] = -0.1073529058151375; + parameters.c_[4] = -0.9594562251023355E+01; + parameters.c_[5] = -0.2047028614809616E+02; + parameters.c_[6] = 0.7496443313967647E+01; + parameters.c_[7] = -0.1024680431464352E+02; + parameters.c_[8] = -0.3399990352819905E+02; + parameters.c_[9] = 0.1170890893206160E+02; + parameters.c_[10] = 0.8083246795921522E+01; + parameters.c_[11] = -0.7981132988064893E+01; + parameters.c_[12] = -0.3152159432874371E+02; + parameters.c_[13] = 0.1631930543123136E+02; + parameters.c_[14] = -0.6058818238834054E+01; + + parameters.m_.fill(0.0); + parameters.m_[0] = parameters.a_[6]; + parameters.m_[1] = parameters.a_[7]; + parameters.m_[2] = parameters.a_[8]; + parameters.m_[3] = parameters.a_[9]; + parameters.m_[4] = 1.0; + parameters.m_[5] = 1.0; + + parameters.e_.fill(0.0); + parameters.e_[5] = 1.0; + + parameters.new_function_evaluation_.fill(true); + + parameters.estimator_of_local_order_ = 4.0; + + parameters.number_of_grid_cells_ = number_of_grid_cells; + parameters.reorder_state_ = reorder_state; + + return parameters; + } + + +} \ No newline at end of file diff --git a/include/micm/solver/state.hpp b/include/micm/solver/state.hpp index ac2674bcb..5b7750439 100644 --- a/include/micm/solver/state.hpp +++ b/include/micm/solver/state.hpp @@ -3,9 +3,12 @@ #include #include #include +#include #include #include +#include #include +#include #include #include #include @@ -14,34 +17,41 @@ namespace micm { + /// @brief Invariants that can be used to construct a state struct StateParameters { - std::vector state_variable_names_{}; - std::vector custom_rate_parameter_labels_{}; std::size_t number_of_grid_cells_{ 1 }; std::size_t number_of_rate_constants_{ 0 }; + std::vector variable_names_{}; + std::vector custom_rate_parameter_labels_{}; + std::vector jacobian_diagonal_elements_{}; + std::set> nonzero_jacobian_elements_{}; }; - template class MatrixPolicy = Matrix> + template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = StandardSparseMatrix> struct State { + /// @brief The concentration of chemicals, varies through time + MatrixPolicy variables_; + /// @brief Rate paramters particular to user-defined rate constants, may vary in time + MatrixPolicy custom_rate_parameters_; + /// @brief The reaction rates, may vary in time + MatrixPolicy rate_constants_; + /// @brief Atmospheric conditions, varies in time std::vector conditions_; + /// @brief The jacobian structure, varies for each solve + SparseMatrixPolicy jacobian_; + /// @brief Immutable data required for the state std::map variable_map_; std::map custom_rate_parameter_map_; std::vector variable_names_{}; - MatrixPolicy variables_; - MatrixPolicy custom_rate_parameters_; - MatrixPolicy rate_constants_; + SparseMatrixPolicy lower_matrix_; + SparseMatrixPolicy upper_matrix_; + size_t state_size_; /// @brief State(); - /// @brief - /// @param state_size The number of System state variables - /// @param custom_parameters_size The number of custom rate parameters - /// @param process_size The number of processes to store rate constants for - State(const std::size_t state_size, const std::size_t custom_parameters_size, const std::size_t process_size); - /// @brief /// @param parameters State dimension information State(const StateParameters& parameters); diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index dbd2c2321..b6e71c72e 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -4,53 +4,52 @@ namespace micm { - template class MatrixPolicy> - inline State::State() + template class MatrixPolicy, template class SparseMatrixPolicy> + inline State::State() : conditions_(), - variable_map_(), - custom_rate_parameter_map_(), - variable_names_(), variables_(), custom_rate_parameters_(), - rate_constants_() - { - } - - template class MatrixPolicy> - inline State::State( - const std::size_t state_size, - const std::size_t custom_parameters_size, - const std::size_t process_size) - : conditions_(1), - variable_map_(), - custom_rate_parameter_map_(), - variable_names_(), - variables_(1, state_size, 0.0), - custom_rate_parameters_(1, custom_parameters_size, 0.0), - rate_constants_(1, process_size, 0.0) + rate_constants_(), + jacobian_() { } - template class MatrixPolicy> - inline State::State(const StateParameters& parameters) + template class MatrixPolicy, template class SparseMatrixPolicy> + inline State::State(const StateParameters& parameters) : conditions_(parameters.number_of_grid_cells_), + variables_(parameters.number_of_grid_cells_, parameters.variable_names_.size(), 0.0), + custom_rate_parameters_(parameters.number_of_grid_cells_, parameters.custom_rate_parameter_labels_.size(), 0.0), + rate_constants_(parameters.number_of_grid_cells_, parameters.number_of_rate_constants_, 0.0), variable_map_(), custom_rate_parameter_map_(), - variable_names_(parameters.state_variable_names_), - variables_(parameters.number_of_grid_cells_, parameters.state_variable_names_.size(), 0.0), - custom_rate_parameters_(parameters.number_of_grid_cells_, parameters.custom_rate_parameter_labels_.size(), 0.0), - rate_constants_(parameters.number_of_grid_cells_, parameters.number_of_rate_constants_, 0.0) + variable_names_(parameters.variable_names_), + jacobian_(), + lower_matrix_(), + upper_matrix_(), + state_size_(parameters.variable_names_.size()) { std::size_t index = 0; - for (auto& name : parameters.state_variable_names_) + for (auto& name : variable_names_) variable_map_[name] = index++; index = 0; for (auto& label : parameters.custom_rate_parameter_labels_) custom_rate_parameter_map_[label] = index++; + + jacobian_ = build_jacobian( + parameters.nonzero_jacobian_elements_, + parameters.number_of_grid_cells_, + state_size_ + ); + + auto lu = LuDecomposition::GetLUMatrices(jacobian_, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + lower_matrix_ = lower_matrix; + upper_matrix_ = upper_matrix; } - template class MatrixPolicy> - inline void State::SetConcentrations( + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::SetConcentrations( const std::unordered_map>& species_to_concentration) { const int num_grid_cells = conditions_.size(); @@ -58,8 +57,8 @@ namespace micm SetConcentration({ pair.first }, pair.second); } - template class MatrixPolicy> - inline void State::SetConcentration(const Species& species, double concentration) + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::SetConcentration(const Species& species, double concentration) { auto var = variable_map_.find(species.name_); if (var == variable_map_.end()) @@ -69,8 +68,8 @@ namespace micm variables_[0][variable_map_[species.name_]] = concentration; } - template class MatrixPolicy> - inline void State::SetConcentration(const Species& species, const std::vector& concentration) + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::SetConcentration(const Species& species, const std::vector& concentration) { auto var = variable_map_.find(species.name_); if (var == variable_map_.end()) @@ -82,16 +81,16 @@ namespace micm variables_[i][i_species] = concentration[i]; } - template class MatrixPolicy> - inline void State::SetCustomRateParameters( + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::SetCustomRateParameters( const std::unordered_map>& parameters) { for (auto& pair : parameters) SetCustomRateParameter(pair.first, pair.second); } - template class MatrixPolicy> - inline void State::SetCustomRateParameter(const std::string& label, double value) + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::SetCustomRateParameter(const std::string& label, double value) { auto param = custom_rate_parameter_map_.find(label); if (param == custom_rate_parameter_map_.end()) @@ -101,8 +100,8 @@ namespace micm custom_rate_parameters_[0][param->second] = value; } - template class MatrixPolicy> - inline void State::SetCustomRateParameter(const std::string& label, const std::vector& values) + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::SetCustomRateParameter(const std::string& label, const std::vector& values) { auto param = custom_rate_parameter_map_.find(label); if (param == custom_rate_parameter_map_.end()) diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index 2d95a1ec3..759e2bf4b 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -1,7 +1,10 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once + #include -#ifndef CUDA_PARAM_HPP -# define CUDA_PARAM_HPP // member data of class CudaProcessSet grouped in struct passing to kernel driver function const size_t BLOCK_SIZE = 320; struct CudaProcessSetParam @@ -63,5 +66,4 @@ struct CudaSparseMatrixParam double* U_; size_t U_size_; size_t n_grids_; -}; -#endif \ No newline at end of file +}; \ No newline at end of file diff --git a/include/micm/util/exit_codes.hpp b/include/micm/util/exit_codes.hpp deleted file mode 100644 index b5027fb81..000000000 --- a/include/micm/util/exit_codes.hpp +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, -// -// SPDX-License-Identifier: Apache-2.0 -#pragma once - -namespace micm -{ - enum ExitCodes - { - InvalidMatrixDimension = 1, - }; -} \ No newline at end of file diff --git a/include/micm/util/jacobian.hpp b/include/micm/util/jacobian.hpp new file mode 100644 index 000000000..ad18dc3d4 --- /dev/null +++ b/include/micm/util/jacobian.hpp @@ -0,0 +1,24 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +namespace micm +{ + // annonymous namespace to hide jacobian builder + template class SparseMatrixPolicy> + SparseMatrixPolicy build_jacobian( + std::set> nonzero_jacobian_elements, + size_t number_of_grid_cells, + size_t state_size) + { + auto builder = SparseMatrixPolicy::create(state_size).number_of_blocks(number_of_grid_cells); + for (auto& elem : nonzero_jacobian_elements) + builder = builder.with_element(elem.first, elem.second); + // Always include diagonal elements + for (std::size_t i = 0; i < state_size; ++i) + builder = builder.with_element(i, i); + + return SparseMatrixPolicy(builder); + } +} // namespace micm \ No newline at end of file diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index fe98bc3a4..9eb4284c7 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -5,7 +5,6 @@ #include #include -#include #include namespace micm @@ -49,8 +48,7 @@ namespace micm // check that this row matches the expected rectangular matrix dimensions if (other.size() < y_dim_) { - std::cerr << "Matrix row size mismatch in assignment from vector"; - std::exit(micm::ExitCodes::InvalidMatrixDimension); + throw std::runtime_error("Matrix row size mismatch in assignment from vector"); } auto other_elem = other.begin(); for (auto &elem : *this) @@ -163,8 +161,7 @@ namespace micm // check that this row matches the expected rectangular matrix dimensions if (other[x].size() != y_dim) { - std::cerr << "Invalid vector for matrix assignment\n"; - std::exit(micm::ExitCodes::InvalidMatrixDimension); + throw std::runtime_error("Invalid vector for matrix assignment"); } for (std::size_t y{}; y < y_dim; ++y) { @@ -217,4 +214,4 @@ namespace micm } }; -} // namespace micm \ No newline at end of file +} // namespace micm diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index a8507f6f9..465aadd85 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include #ifndef DEFAULT_VECTOR_SIZE @@ -52,8 +51,7 @@ namespace micm { if (other.size() < y_dim_) { - std::cerr << "Matrix row size mismatch in assignment from vector"; - std::exit(micm::ExitCodes::InvalidMatrixDimension); + throw std::runtime_error("Matrix row size mismatch in assignment from vector."); } auto iter = std::next(matrix_.data_.begin(), group_index_ * y_dim_ * L + row_index_); std::for_each( @@ -165,8 +163,7 @@ namespace micm { if (other_row.size() != y_dim) { - std::cerr << "Invalid vector for matrix assignment\n"; - std::exit(micm::ExitCodes::InvalidMatrixDimension); + throw std::runtime_error("Invalid vector for matrix assignment"); } auto iter = std::next(data.begin(), std::floor(i_row / (double)L) * y_dim * L + i_row % L); for (auto &elem : other_row) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1766b5172..bf5414137 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,10 @@ if(ENABLE_MPI) target_link_libraries(micm INTERFACE MPI::MPI_CXX) endif() +if(ENABLE_LLVM) + target_link_libraries(micm INTERFACE ${llvm_libs}) +endif() + if(ENABLE_OPENACC OR ENABLE_CUDA) if(NOT GPU_TYPE) message(FATAL_ERROR "GPU_TYPE is not set or is empty. Please provide a GPU type.") @@ -80,10 +84,6 @@ if(ENABLE_CUDA) PUBLIC cudart ) - target_compile_definitions(micm_cuda - PUBLIC - USE_CUDA - ) endif() add_subdirectory(process) diff --git a/test/integration/analytical_rosenbrock.cpp b/test/integration/analytical_rosenbrock.cpp index 83dae3188..6d72a32ce 100644 --- a/test/integration/analytical_rosenbrock.cpp +++ b/test/integration/analytical_rosenbrock.cpp @@ -238,7 +238,7 @@ TEST(AnalyticalExamples, Oregonator) { 0.1000814870318523E+01, 0.1228178521549889E+04, 0.1320554942846513E+03 }, }; - micm::State state = solver.GetState(); + auto state = solver.GetState(); state.variables_[0] = model_concentrations[0]; @@ -372,7 +372,7 @@ TEST(AnalyticalExamples, Oregonator2) { 0.1000814870318523E+01, 0.1228178521549889E+04, 0.1320554942846513E+03 }, }; - micm::State state = solver.GetState(); + auto state = solver.GetState(); double k1 = 77.27; double k2 = 0.161; @@ -525,7 +525,7 @@ TEST(AnalyticalExamples, HIRES) 0.004685507242281520 }, }; - micm::State state = solver.GetState(); + auto state = solver.GetState(); state.variables_[0] = model_concentrations[0]; @@ -624,7 +624,7 @@ TEST(AnalyticalExamples, E5) { 0.0000000000000000000e-000, 8.8612334976263783420e-023, 8.8612334976263783421e-023, 0.0000000000000000000e-000 } }; - micm::State state = solver.GetState(); + auto state = solver.GetState(); state.variables_[0] = model_concentrations[0]; @@ -661,4 +661,4 @@ TEST(AnalyticalExamples, E5) << "difference at (" << i << ", " << j << ")"; } } -} \ No newline at end of file +} diff --git a/test/integration/chapman.cpp b/test/integration/chapman.cpp index 01da9b6e7..a0c422b9c 100644 --- a/test/integration/chapman.cpp +++ b/test/integration/chapman.cpp @@ -132,7 +132,7 @@ TEST(ChapmanIntegration, CanBuildChapmanSystem) micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - micm::State state = solver.GetState(); + auto state = solver.GetState(); std::vector concentrations{ 0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.3, 0.3, 0.3 }; state.variables_[0] = concentrations; diff --git a/test/integration/e5.hpp b/test/integration/e5.hpp index 9eb7d4cb8..f5763df1e 100644 --- a/test/integration/e5.hpp +++ b/test/integration/e5.hpp @@ -8,6 +8,8 @@ template< class LinearSolverPolicy = micm::LinearSolver> class E5 : public micm::RosenbrockSolver { + std::set> nonzero_jacobian_elements_; + public: /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters /// @param system The chemical system to create the solver for @@ -17,28 +19,62 @@ class E5 : public micm::RosenbrockSolver() { - this->system_ = system; this->processes_ = processes; this->parameters_ = parameters; - this->N_ = this->system_.StateSize() * this->parameters_.number_of_grid_cells_; + auto builder = SparseMatrixPolicy::create(4).number_of_blocks(1).initial_value(0.0); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 4; ++j) { builder = builder.with_element(i, j); + nonzero_jacobian_elements_.insert(std::make_pair(i, j)); } } - this->jacobian_ = builder; - for (std::size_t i = 0; i < this->jacobian_[0].size(); ++i) - this->jacobian_diagonal_elements_.push_back(this->jacobian_.VectorIndex(0, i, i)); - this->linear_solver_ = LinearSolverPolicy(this->jacobian_, 1.0e-30); + SparseMatrixPolicy jacobian = SparseMatrixPolicy(builder); + + std::vector jacobian_diagonal_elements; + for (std::size_t i = 0; i < jacobian[0].size(); ++i) + jacobian_diagonal_elements.push_back(jacobian.VectorIndex(0, i, i)); + + std::vector param_labels{}; + for (const auto& process : processes) + if (process.rate_constant_) + for (auto& label : process.rate_constant_->CustomParameters()) + param_labels.push_back(label); + + std::function& variables, const std::size_t i)> state_reordering; + this->state_parameters_ = { + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = processes.size(), + .variable_names_ = system.UniqueNames(state_reordering), + .custom_rate_parameter_labels_ = param_labels, + .jacobian_diagonal_elements_ = jacobian_diagonal_elements, + }; + + this->linear_solver_ = LinearSolverPolicy(jacobian, 1.0e-30); } ~E5() { } + micm::State GetState() const override + { + auto state = micm::State{ this->state_parameters_ }; + + state.jacobian_ = micm::build_jacobian( + nonzero_jacobian_elements_, this->state_parameters_.number_of_grid_cells_, this->state_parameters_.variable_names_.size()); + + auto lu = micm::LuDecomposition::GetLUMatrices(state.jacobian_, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + state.lower_matrix_ = lower_matrix; + state.upper_matrix_ = upper_matrix; + + return state; + } + /// @brief Calculate a chemical forcing /// @param rate_constants List of rate constants for each needed species /// @param number_densities The number density of each species @@ -49,7 +85,6 @@ class E5 : public micm::RosenbrockSolver& forcing) override { std::fill(forcing.AsVector().begin(), forcing.AsVector().end(), 0.0); - this->stats_.function_calls += 1; auto data = number_densities.AsVector(); @@ -74,7 +109,6 @@ class E5 : public micm::RosenbrockSolverstats_.jacobian_updates += 1; double A = 7.89e-10; double B = 1.1e7; diff --git a/test/integration/hires.hpp b/test/integration/hires.hpp index 9fb638c26..0c39a896f 100644 --- a/test/integration/hires.hpp +++ b/test/integration/hires.hpp @@ -8,6 +8,8 @@ template< class LinearSolverPolicy = micm::LinearSolver> class HIRES : public micm::RosenbrockSolver { + std::set> nonzero_jacobian_elements_; + public: /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters /// @param system The chemical system to create the solver for @@ -18,28 +20,62 @@ class HIRES : public micm::RosenbrockSolver() { - this->system_ = system; this->processes_ = processes; this->parameters_ = parameters; - this->N_ = this->system_.StateSize() * this->parameters_.number_of_grid_cells_; + auto builder = SparseMatrixPolicy::create(8).number_of_blocks(1).initial_value(0.0); for (int i = 0; i < 8; ++i) { for (int j = 0; j < 8; ++j) { builder = builder.with_element(i, j); + nonzero_jacobian_elements_.insert(std::make_pair(i, j)); } } - this->jacobian_ = builder; - for (std::size_t i = 0; i < this->jacobian_[0].size(); ++i) - this->jacobian_diagonal_elements_.push_back(this->jacobian_.VectorIndex(0, i, i)); - this->linear_solver_ = LinearSolverPolicy(this->jacobian_, 1.0e-30); + SparseMatrixPolicy jacobian = SparseMatrixPolicy(builder); + + std::vector jacobian_diagonal_elements; + for (std::size_t i = 0; i < jacobian[0].size(); ++i) + jacobian_diagonal_elements.push_back(jacobian.VectorIndex(0, i, i)); + + std::vector param_labels{}; + for (const auto& process : processes) + if (process.rate_constant_) + for (auto& label : process.rate_constant_->CustomParameters()) + param_labels.push_back(label); + + std::function& variables, const std::size_t i)> state_reordering; + this->state_parameters_ = { + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = processes.size(), + .variable_names_ = system.UniqueNames(state_reordering), + .custom_rate_parameter_labels_ = param_labels, + .jacobian_diagonal_elements_ = jacobian_diagonal_elements, + }; + + this->linear_solver_ = LinearSolverPolicy(jacobian, 1.0e-30); } ~HIRES() { } + micm::State GetState() const override + { + auto state = micm::State{ this->state_parameters_ }; + + state.jacobian_ = micm::build_jacobian( + nonzero_jacobian_elements_, this->state_parameters_.number_of_grid_cells_, this->state_parameters_.variable_names_.size()); + + auto lu = micm::LuDecomposition::GetLUMatrices(state.jacobian_, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + state.lower_matrix_ = lower_matrix; + state.upper_matrix_ = upper_matrix; + + return state; + } + /// @brief Calculate a chemical forcing /// @param rate_constants List of rate constants for each needed species /// @param number_densities The number density of each species @@ -50,7 +86,6 @@ class HIRES : public micm::RosenbrockSolver& forcing) override { std::fill(forcing.AsVector().begin(), forcing.AsVector().end(), 0.0); - this->stats_.function_calls += 1; auto data = number_densities.AsVector(); @@ -75,7 +110,6 @@ class HIRES : public micm::RosenbrockSolverstats_.jacobian_updates += 1; jacobian[0][0][0] = -1.71; jacobian[0][0][1] = 0.43; diff --git a/test/integration/oregonator.hpp b/test/integration/oregonator.hpp index 42a1e0449..6c457c282 100644 --- a/test/integration/oregonator.hpp +++ b/test/integration/oregonator.hpp @@ -8,6 +8,8 @@ template< class LinearSolverPolicy = micm::LinearSolver> class Oregonator : public micm::RosenbrockSolver { + std::set> nonzero_jacobian_elements_; + public: /// @brief Builds a Rosenbrock solver for the given system, processes, and solver parameters /// @param system The chemical system to create the solver for @@ -18,28 +20,62 @@ class Oregonator : public micm::RosenbrockSolver() { - this->system_ = system; this->processes_ = processes; this->parameters_ = parameters; - this->N_ = this->system_.StateSize() * this->parameters_.number_of_grid_cells_; + auto builder = SparseMatrixPolicy::create(3).number_of_blocks(1).initial_value(0.0); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { builder = builder.with_element(i, j); + nonzero_jacobian_elements_.insert(std::make_pair(i, j)); } } - this->jacobian_ = builder; - for (std::size_t i = 0; i < this->jacobian_[0].size(); ++i) - this->jacobian_diagonal_elements_.push_back(this->jacobian_.VectorIndex(0, i, i)); - this->linear_solver_ = LinearSolverPolicy(this->jacobian_, 1.0e-30); + SparseMatrixPolicy jacobian = SparseMatrixPolicy(builder); + + std::vector jacobian_diagonal_elements; + for (std::size_t i = 0; i < jacobian[0].size(); ++i) + jacobian_diagonal_elements.push_back(jacobian.VectorIndex(0, i, i)); + + std::vector param_labels{}; + for (const auto& process : processes) + if (process.rate_constant_) + for (auto& label : process.rate_constant_->CustomParameters()) + param_labels.push_back(label); + + std::function& variables, const std::size_t i)> state_reordering; + this->state_parameters_ = { + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = processes.size(), + .variable_names_ = system.UniqueNames(state_reordering), + .custom_rate_parameter_labels_ = param_labels, + .jacobian_diagonal_elements_ = jacobian_diagonal_elements, + }; + + this->linear_solver_ = LinearSolverPolicy(jacobian, 1.0e-30); } ~Oregonator() { } + micm::State GetState() const override + { + auto state = micm::State{ this->state_parameters_ }; + + state.jacobian_ = micm::build_jacobian( + nonzero_jacobian_elements_, this->state_parameters_.number_of_grid_cells_, this->state_parameters_.variable_names_.size()); + + auto lu = micm::LuDecomposition::GetLUMatrices(state.jacobian_, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + state.lower_matrix_ = lower_matrix; + state.upper_matrix_ = upper_matrix; + + return state; + } + /// @brief Calculate a chemical forcing /// @param rate_constants List of rate constants for each needed species /// @param number_densities The number density of each species @@ -50,7 +86,6 @@ class Oregonator : public micm::RosenbrockSolver& forcing) override { std::fill(forcing.AsVector().begin(), forcing.AsVector().end(), 0.0); - this->stats_.function_calls += 1; auto data = number_densities.AsVector(); @@ -69,7 +104,6 @@ class Oregonator : public micm::RosenbrockSolver& jacobian) override { auto data = number_densities.AsVector(); - this->stats_.jacobian_updates += 1; jacobian[0][0][0] = 77.27 * (1. - 2. * 8.375e-6 * data[0] - data[1]); jacobian[0][0][1] = 77.27 * (1. - data[0]); diff --git a/test/integration/terminator.hpp b/test/integration/terminator.hpp index 3d300c58e..f4cd848c8 100644 --- a/test/integration/terminator.hpp +++ b/test/integration/terminator.hpp @@ -44,7 +44,7 @@ void TestTerminator( auto solver = create_solver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ toy_r1, toy_r2 }); - micm::State state = solver.GetState(); + auto state = solver.GetState(); auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); std::unordered_map> concentrations{ { "Cl2", {} }, { "Cl", {} } }; diff --git a/test/kpp/test_kpp_to_micm.cpp b/test/kpp/test_kpp_to_micm.cpp index 05ad70117..a491bc73a 100644 --- a/test/kpp/test_kpp_to_micm.cpp +++ b/test/kpp/test_kpp_to_micm.cpp @@ -16,8 +16,8 @@ void print_header() << "," << std::setw(11) << "O1D" << std::endl; } -template class T> -void print_state(double time, micm::State& state) +template class T, template class D> +void print_state(double time, micm::State& state) { std::ios oldState(nullptr); oldState.copyfmt(std::cout); diff --git a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp index b38477363..917710fc8 100644 --- a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp +++ b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp @@ -124,11 +124,11 @@ namespace micm /// @brief Returns a list of species that participate in photolysis /// @return vector of strings - std::vector photolysis_names(); + std::vector photolysis_names() const; /// @brief Returns a list of species names /// @return vector of strings - std::vector species_names(); + std::vector species_names() const; /// @brief Calculate a chemical forcing /// @param rate_constants List of rate constants for each needed species @@ -305,7 +305,14 @@ namespace micm inline State<> ChapmanODESolver::GetState() const { - return State{ 9, 3, 7 }; + auto state_parameters = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 7, + .variable_names_ = species_names(), + .custom_rate_parameter_labels_ = photolysis_names(), + }; + + return micm::State{ state_parameters }; } inline ChapmanODESolver::SolverResult ChapmanODESolver::Solve(double time_start, double time_end, State<>& state) noexcept @@ -488,7 +495,7 @@ namespace micm return std::vector{ "O2_1", "O3_1", "O3_2", "N2_O1D_1", "O1D_O2_1", "O_O3_1", "M_O_O2_1" }; } - inline std::vector ChapmanODESolver::photolysis_names() + inline std::vector ChapmanODESolver::photolysis_names() const { return std::vector{ "O2_1", @@ -497,7 +504,7 @@ namespace micm }; } - inline std::vector ChapmanODESolver::species_names() + inline std::vector ChapmanODESolver::species_names() const { return std::vector{ "M", "Ar", "CO2", "H2O", "N2", "O1D", "O", "O2", "O3", diff --git a/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp b/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp index f95bb15a0..a86c2599f 100644 --- a/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp +++ b/test/regression/RosenbrockChapman/regression_test_dforce_dy_policy.hpp @@ -25,7 +25,7 @@ void testJacobian(OdeSolverPolicy& solver) auto& rate_const_vec = state.rate_constants_.AsVector(); std::generate(state_vec.begin(), state_vec.end(), [&]() { return dist(engine); }); - auto& jacobian = solver.jacobian_; + auto& jacobian = state.jacobian_; solver.CalculateJacobian(state.rate_constants_, state.variables_, jacobian); for (std::size_t i{}; i < 3; ++i) diff --git a/test/tutorial/test_but_how_fast_is_it.cpp b/test/tutorial/test_but_how_fast_is_it.cpp index 209296878..f36949ee6 100644 --- a/test/tutorial/test_but_how_fast_is_it.cpp +++ b/test/tutorial/test_but_how_fast_is_it.cpp @@ -37,7 +37,7 @@ int main() std::vector{ r1, r2, r3 }, RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) }; - State state = solver.GetState(); + auto state = solver.GetState(); // mol m-3 state.SetConcentration(a, std::vector{ 1, 2, 0.5 }); diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index 988d814a1..a922e4caa 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -15,8 +15,8 @@ void print_header() << "," << std::setw(10) << "C" << std::endl; } -template class T> -void print_state(double time, State& state) +template class T, template class D> +void print_state(double time, State& state) { std::ios oldState(nullptr); oldState.copyfmt(std::cout); @@ -74,7 +74,7 @@ int main() std::vector{ r1, r2, r3 }, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) }; - State state = solver.GetState(); + auto state = solver.GetState(); // mol m-3 state.SetConcentration(a, std::vector{ 1, 2, 0.5 }); diff --git a/test/tutorial/test_openmp.cpp b/test/tutorial/test_openmp.cpp index 16a935cab..003827c6c 100644 --- a/test/tutorial/test_openmp.cpp +++ b/test/tutorial/test_openmp.cpp @@ -23,12 +23,9 @@ void print_results(std::vector results) std::cout.copyfmt(oldState); } -std::vector test_solver_on_thread(System chemical_system, std::vector reactions) +std::vector run_solver_on_thread_with_own_state(auto& solver, auto& state) { - RosenbrockSolver<> solver{ chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters(1, false) }; - State state = solver.GetState(); + std::cout << "Running solver on thread " << omp_get_thread_num() << std::endl; // mol m-3 state.variables_[0] = { 1, 0, 0 }; @@ -83,11 +80,15 @@ int main() auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - std::vector> results(3); + std::vector> results(n_threads); + + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; #pragma omp parallel num_threads(n_threads) { - std::vector result = test_solver_on_thread(chemical_system, reactions); + // each thread should use its own state + auto state = solver.GetState(); + std::vector result = run_solver_on_thread_with_own_state(solver, state); results[omp_get_thread_num()] = result; #pragma omp barrier } diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index 5ca59be84..ec06f6638 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -27,8 +27,8 @@ void print_header() << "," << std::setw(10) << "G" << std::endl; } -template class T> -void print_state(double time, State& state) +template class T, template class D> +void print_state(double time, State& state) { std::ios oldState(nullptr); oldState.copyfmt(std::cout); diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index 59ddbb5d8..fb013fa5d 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -27,8 +27,8 @@ void print_header() << "," << std::setw(10) << "G" << std::endl; } -template class T> -void print_state(double time, State& state) +template class T, template class D> +void print_state(double time, State& state) { std::ios oldState(nullptr); oldState.copyfmt(std::cout); diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp index e52b5d76b..16616c400 100644 --- a/test/tutorial/test_solver_configuration.cpp +++ b/test/tutorial/test_solver_configuration.cpp @@ -15,8 +15,8 @@ void print_header() << "," << std::setw(10) << "C" << std::endl; } -template class T> -void print_state(double time, State& state) +template class T, template class D> +void print_state(double time, State& state) { std::ios oldState(nullptr); oldState.copyfmt(std::cout); @@ -32,7 +32,7 @@ void print_state(double time, State& state) template void test_solver_type(T solver) { - State state = solver.GetState(); + auto state = solver.GetState(); // mol m-3 state.variables_[0] = { 1, 0, 0 }; @@ -58,9 +58,9 @@ void test_solver_type(T solver) print_header(); print_state(0, state); - typename T::SolverStats total_stats; + SolverStats total_stats; std::chrono::duration total_solve_time = std::chrono::nanoseconds::zero(); - ; + // solve for ten iterations for (int i = 0; i < 10; ++i) diff --git a/test/unit/openmp/CMakeLists.txt b/test/unit/openmp/CMakeLists.txt index 7db63577c..b744f9807 100644 --- a/test/unit/openmp/CMakeLists.txt +++ b/test/unit/openmp/CMakeLists.txt @@ -6,4 +6,7 @@ include(test_util) ################################################################################ # Tests -create_standard_test(NAME openmp SOURCES test_openmp.cpp) \ No newline at end of file +create_standard_test(NAME openmp SOURCES test_openmp.cpp) +if(ENABLE_LLVM) + create_standard_test(NAME openmp_jit_solver SOURCES test_openmp_jit_solver.cpp) +endif() \ No newline at end of file diff --git a/test/unit/openmp/run_solver.hpp b/test/unit/openmp/run_solver.hpp new file mode 100644 index 000000000..d1e01020e --- /dev/null +++ b/test/unit/openmp/run_solver.hpp @@ -0,0 +1,40 @@ +#pragma once + +std::vector run_solver_on_thread_with_own_state(auto& solver, auto& state) +{ + std::cout << "Running solver on thread " << omp_get_thread_num() << std::endl; + + // mol m-3 + state.variables_[0] = { 1, 0, 0 }; + + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("PHOTO.r1", k1); + state.SetCustomRateParameter("PHOTO.r2", k2); + state.SetCustomRateParameter("PHOTO.r3", k3); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + double time_step = 200; // s + + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + } + + return state.variables_.AsVector(); +} \ No newline at end of file diff --git a/test/unit/openmp/test_openmp.cpp b/test/unit/openmp/test_openmp.cpp index 94707145f..575c12a24 100644 --- a/test/unit/openmp/test_openmp.cpp +++ b/test/unit/openmp/test_openmp.cpp @@ -4,55 +4,13 @@ #include #include -using namespace micm; - -template -using SparseMatrixPolicy = SparseMatrix; - -std::vector test_solver_on_thread(System chemical_system, std::vector reactions) -{ - std::cout << "Running solver on thread " << omp_get_thread_num() << std::endl; - RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - State state = solver.GetState(); - - // mol m-3 - state.variables_[0] = { 1, 0, 0 }; - - double k1 = 0.04; - double k2 = 3e7; - double k3 = 1e4; - state.SetCustomRateParameter("PHOTO.r1", k1); - state.SetCustomRateParameter("PHOTO.r2", k2); - state.SetCustomRateParameter("PHOTO.r3", k3); - - double temperature = 272.5; // [K] - double pressure = 101253.3; // [Pa] - double air_density = 1e6; // [mol m-3] - - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - double time_step = 200; // s - - for (int i = 0; i < 10; ++i) - { - double elapsed_solve_time = 0; - - while (elapsed_solve_time < time_step) - { - auto result = solver.Solve(time_step - elapsed_solve_time, state); - elapsed_solve_time = result.final_time_; - state.variables_ = result.result_; - } - } +#include "run_solver.hpp" - return state.variables_.AsVector(); -} +using namespace micm; -TEST(OpenMP, OneFileReadThreeThreads) +TEST(OpenMP, OneSolverManyStates) { - constexpr size_t n_threads = 3; + constexpr size_t n_threads = 8; SolverConfig solverConfig; @@ -68,19 +26,22 @@ TEST(OpenMP, OneFileReadThreeThreads) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - std::vector> results(3); + std::vector> results(n_threads); + + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; #pragma omp parallel num_threads(n_threads) { - std::vector result = test_solver_on_thread(chemical_system, reactions); + auto state = solver.GetState(); + std::vector result = run_solver_on_thread_with_own_state(solver, state); results[omp_get_thread_num()] = result; #pragma omp barrier } - for (int i = 0; i < results[0].size(); ++i) - { - EXPECT_EQ(results[0][i], results[1][i]); - EXPECT_EQ(results[0][i], results[2][i]); - EXPECT_EQ(results[1][i], results[2][i]); + // compare each thread to thread 1 + for (int i = 1; i < n_threads; ++i) { + for (int j = 0; j < results[0].size(); ++j) { + EXPECT_EQ(results[0][j], results[i][j]); + } } -} \ No newline at end of file +} diff --git a/test/unit/openmp/test_openmp_jit_solver.cpp b/test/unit/openmp/test_openmp_jit_solver.cpp new file mode 100644 index 000000000..5e43588a1 --- /dev/null +++ b/test/unit/openmp/test_openmp_jit_solver.cpp @@ -0,0 +1,64 @@ +#include +#include + +#include +#include +#include +#include + +#include "run_solver.hpp" + +using namespace micm; + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; + +TEST(OpenMP, JITOneSolverManyStates) +{ + constexpr size_t n_threads = 8; + + SolverConfig solverConfig; + + std::string config_path = "./unit_configs/robertson"; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != micm::ConfigParseStatus::Success) + { + throw "Parsing failed"; + } + + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + std::vector> results(n_threads); + + auto jit{ micm::JitCompiler::create() }; + JitRosenbrockSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + JitLinearSolver<1, Group1SparseVectorMatrix> + > solver( + jit.get(), + chemical_system, + reactions, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + ); + +#pragma omp parallel num_threads(n_threads) + { + auto state = solver.GetState(); + std::vector result = run_solver_on_thread_with_own_state(solver, state); + results[omp_get_thread_num()] = result; +#pragma omp barrier + } + + // compare each thread to thread 1 + for (int i = 1; i < n_threads; ++i) { + for (int j = 0; j < results[0].size(); ++j) { + EXPECT_EQ(results[0][j], results[i][j]); + } + } +} diff --git a/test/unit/process/test_arrhenius_rate_constant.cpp b/test/unit/process/test_arrhenius_rate_constant.cpp index 54dcd0f64..286d7279e 100644 --- a/test/unit/process/test_arrhenius_rate_constant.cpp +++ b/test/unit/process/test_arrhenius_rate_constant.cpp @@ -7,73 +7,75 @@ TEST(ArrheniusRateConstant, CalculateWithSystem) { micm::ArrheniusRateConstant zero{}; - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); - auto k = zero.calculate(state.conditions_[0], params); + micm::Conditions conditions = { + .temperature_ = 301.24 // [K] + }; + + auto k = zero.calculate(conditions); EXPECT_NEAR(k, 1, 0.01); micm::ArrheniusRateConstantParameters parameters; parameters.A_ = 1; micm::ArrheniusRateConstant basic(parameters); - k = basic.calculate(state.conditions_[0], params); + k = basic.calculate(conditions); EXPECT_NEAR(k, 1, 0.01); // values from https://jpldataeval.jpl.nasa.gov/pdf/JPL_00-03.pdf parameters.A_ = 2.2e-10; micm::ArrheniusRateConstant o1d(parameters); - k = o1d.calculate(state.conditions_[0], params); + k = o1d.calculate(conditions); EXPECT_NEAR(k, 2.2e-10, 0.01); // O + HO2 -> OH + O2 parameters.A_ = 3e-11; parameters.C_ = -200; micm::ArrheniusRateConstant hox(parameters); - k = hox.calculate(state.conditions_[0], params); + k = hox.calculate(conditions); EXPECT_NEAR(k, 3e-11 * std::exp(-200 / 301.24), 0.01); // OH + HCl → H2O + Cl parameters.A_ = 2.6e-12; parameters.C_ = -350; micm::ArrheniusRateConstant clox(parameters); - k = clox.calculate(state.conditions_[0], params); + k = clox.calculate(conditions); EXPECT_NEAR(k, 2.6e-12 * std::exp(-350 / 301.24), 0.01); } TEST(ArrheniusRateConstant, CalculateWithPrescribedArugments) { - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions = { + .temperature_ = 301.24 // [K] + }; + micm::ArrheniusRateConstant zero{}; - auto k = zero.calculate(state.conditions_[0], params); + auto k = zero.calculate(conditions); EXPECT_NEAR(k, 1, 0.01); micm::ArrheniusRateConstantParameters parameters; parameters.A_ = 1; micm::ArrheniusRateConstant basic(parameters); - k = basic.calculate(state.conditions_[0], params); + k = basic.calculate(conditions); EXPECT_NEAR(k, 1, 0.01); // values from https://jpldataeval.jpl.nasa.gov/pdf/JPL_00-03.pdf parameters.A_ = 2.2e-10; micm::ArrheniusRateConstant o1d(parameters); - k = o1d.calculate(state.conditions_[0], params); + k = o1d.calculate(conditions); EXPECT_NEAR(k, 2.2e-10, 0.01); // O + HO2 -> OH + O2 parameters.A_ = 3e-11; parameters.C_ = -200; micm::ArrheniusRateConstant hox(parameters); - k = hox.calculate(state.conditions_[0], params); + k = hox.calculate(conditions); EXPECT_NEAR(k, 3e-11 * std::exp(200 / 301.24), 0.01); // OH + HCl → H2O + Cl parameters.A_ = 2.6e-12; parameters.C_ = -350; micm::ArrheniusRateConstant clox(parameters); - k = clox.calculate(state.conditions_[0], params); + k = clox.calculate(conditions); EXPECT_NEAR(k, 2.6e-12 * std::exp(-350 / 301.24), 0.01); } diff --git a/test/unit/process/test_branched_rate_constant.cpp b/test/unit/process/test_branched_rate_constant.cpp index 8430c1b4b..d1f57a6af 100644 --- a/test/unit/process/test_branched_rate_constant.cpp +++ b/test/unit/process/test_branched_rate_constant.cpp @@ -7,14 +7,15 @@ TEST(BranchedRateConstant, CalculateAlkoxyBranchWithAllArugments) { - micm::State state{ 0, 0, 1 }; double temperature = 301.24; - state.conditions_[0].temperature_ = temperature; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions = { + .temperature_ = temperature, // [K] + .air_density_ = 42.2 // [mol mol-1] + }; + micm::BranchedRateConstant branched{ micm::BranchedRateConstantParameters{ .branch_ = micm::BranchedRateConstantParameters::Branch::Alkoxy, .X_ = 1.2, .Y_ = 204.3, .a0_ = 1.0e-3, .n_ = 2 } }; - auto k = branched.calculate(state.conditions_[0], params); + auto k = branched.calculate(conditions); double air_dens_n_cm3 = 42.2 * AVOGADRO_CONSTANT * 1.0e-6; double a = 2.0e-22 * std::exp(2) * 2.45e19; double b = 0.43 * std::pow((293.0 / 298.0), -8.0); @@ -28,14 +29,15 @@ TEST(BranchedRateConstant, CalculateAlkoxyBranchWithAllArugments) TEST(BranchedRateConstant, CalculateNitrateBranchWithAllArugments) { - micm::State state{ 0, 0, 1 }; double temperature = 301.24; - state.conditions_[0].temperature_ = temperature; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions = { + .temperature_ = temperature, // [K] + .air_density_ = 42.2 // [mol mol-1] + }; + micm::BranchedRateConstant branched{ micm::BranchedRateConstantParameters{ .branch_ = micm::BranchedRateConstantParameters::Branch::Nitrate, .X_ = 1.2, .Y_ = 204.3, .a0_ = 1.0e-3, .n_ = 2 } }; - auto k = branched.calculate(state.conditions_[0], params); + auto k = branched.calculate(conditions); double air_dens_n_cm3 = 42.2 * AVOGADRO_CONSTANT * 1.0e-6; double a = 2.0e-22 * std::exp(2) * 2.45e19; double b = 0.43 * std::pow((293.0 / 298.0), -8.0); diff --git a/test/unit/process/test_cuda_process_set.cpp b/test/unit/process/test_cuda_process_set.cpp index 2fbdc9bd2..cf063e97c 100644 --- a/test/unit/process/test_cuda_process_set.cpp +++ b/test/unit/process/test_cuda_process_set.cpp @@ -38,10 +38,12 @@ void testRandomSystemAddForcingTerms(std::size_t n_cells, std::size_t n_reaction species_names.push_back(std::to_string(i)); } micm::Phase gas_phase{ species }; - micm::State state{ micm::StateParameters{ .state_variable_names_{ species_names }, - .custom_rate_parameter_labels_{}, - .number_of_grid_cells_ = n_cells, - .number_of_rate_constants_ = n_reactions } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = n_cells, + .number_of_rate_constants_ = n_reactions, + .variable_names_{ species_names }, + .custom_rate_parameter_labels_{}, + } }; std::vector processes{}; for (std::size_t i = 0; i < n_reactions; ++i) @@ -61,8 +63,8 @@ void testRandomSystemAddForcingTerms(std::size_t n_cells, std::size_t n_reaction processes.push_back(micm::Process::create().reactants(reactants).products(products).phase(gas_phase)); } - micm::ProcessSet cpu_set{ processes, state }; - micm::CudaProcessSet gpu_set{ processes, state }; + micm::ProcessSet cpu_set{ processes, state.variable_map_ }; + micm::CudaProcessSet gpu_set{ processes, state.variable_map_ }; for (auto& elem : state.variables_.AsVector()) elem = get_double(); @@ -110,9 +112,11 @@ void testRandomSystemAddJacobianTerms(std::size_t n_cells, std::size_t n_reactio species_names.push_back(std::to_string(i)); } micm::Phase gas_phase{ species }; - micm::State state{ micm::StateParameters{ .state_variable_names_{ species_names }, - .number_of_grid_cells_ = n_cells, - .number_of_rate_constants_ = n_reactions } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = n_cells, + .number_of_rate_constants_ = n_reactions, + .variable_names_{ species_names }, + } }; std::vector processes{}; for (std::size_t i = 0; i < n_reactions; ++i) @@ -132,8 +136,8 @@ void testRandomSystemAddJacobianTerms(std::size_t n_cells, std::size_t n_reactio processes.push_back(micm::Process::create().reactants(reactants).products(products).phase(gas_phase)); } - micm::ProcessSet cpu_set{ processes, state }; - micm::CudaProcessSet gpu_set{ processes, state }; + micm::ProcessSet cpu_set{ processes, state.variable_map_ }; + micm::CudaProcessSet gpu_set{ processes, state.variable_map_ }; for (auto& elem : state.variables_.AsVector()) elem = get_double(); diff --git a/test/unit/process/test_jit_process_set.cpp b/test/unit/process/test_jit_process_set.cpp index 51190bfd5..e1476070d 100644 --- a/test/unit/process/test_jit_process_set.cpp +++ b/test/unit/process/test_jit_process_set.cpp @@ -30,8 +30,8 @@ TEST(JitProcessSet, VectorMatrix) } testProcessSet>( [&](const std::vector& processes, - const micm::State& state) -> micm::JitProcessSet<2> { - return micm::JitProcessSet<2>{ jit.get(), processes, state }; + const micm::State& state) -> micm::JitProcessSet<2> { + return micm::JitProcessSet<2>{ jit.get(), processes, state.variable_map_ }; }); } @@ -43,36 +43,36 @@ TEST(RandomJitProcessSet, VectorMatrix) llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - testRandomSystem>( + testRandomSystem>( 2000, 20, 30, [&](const std::vector& processes, const micm::State& state) -> micm::JitProcessSet<2000> { - return micm::JitProcessSet<2000>{ jit.get(), processes, state }; + return micm::JitProcessSet<2000>{ jit.get(), processes, state.variable_map_ }; }); - testRandomSystem>( + testRandomSystem>( 3000, 50, 40, [&](const std::vector& processes, const micm::State& state) -> micm::JitProcessSet<3000> { - return micm::JitProcessSet<3000>{ jit.get(), processes, state }; + return micm::JitProcessSet<3000>{ jit.get(), processes, state.variable_map_ }; }); - testRandomSystem>( + testRandomSystem>( 3000, 30, 20, [&](const std::vector& processes, const micm::State& state) -> micm::JitProcessSet<3000> { - return micm::JitProcessSet<3000>{ jit.get(), processes, state }; + return micm::JitProcessSet<3000>{ jit.get(), processes, state.variable_map_ }; }); - testRandomSystem>( + testRandomSystem>( 4000, 100, 80, [&](const std::vector& processes, const micm::State& state) -> micm::JitProcessSet<4000> { - return micm::JitProcessSet<4000>{ jit.get(), processes, state }; + return micm::JitProcessSet<4000>{ jit.get(), processes, state.variable_map_ }; }); } \ No newline at end of file diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index a3112e573..aa8ed0be5 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -30,9 +30,11 @@ void testProcessUpdateState(const std::size_t number_of_grid_cells) for (const auto& process : processes) for (auto& label : process.rate_constant_->CustomParameters()) param_labels.push_back(label); - micm::State state{ micm::StateParameters{ .custom_rate_parameter_labels_ = param_labels, - .number_of_grid_cells_ = number_of_grid_cells, - .number_of_rate_constants_ = processes.size() } }; + micm::State state{ micm::StateParameters{ .number_of_grid_cells_ = number_of_grid_cells, + .number_of_rate_constants_ = processes.size(), + .variable_names_ = { "foo", "bar'" }, + .custom_rate_parameter_labels_ = param_labels, + } }; MatrixPolicy expected_rate_constants(number_of_grid_cells, 3, 0.0); std::vector params = { 0.0, 0.0, 0.0 }; diff --git a/test/unit/process/test_process_set.cpp b/test/unit/process/test_process_set.cpp index 265008ce3..ba7a51d85 100644 --- a/test/unit/process/test_process_set.cpp +++ b/test/unit/process/test_process_set.cpp @@ -34,52 +34,52 @@ using Group4SparseVectorMatrix = micm::SparseMatrix( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); } TEST(ProcessSet, VectorMatrix) { testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); } TEST(RandomProcessSet, Matrix) { - testRandomSystem( + testRandomSystem( 2000, 500, 400, - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); - testRandomSystem( + testRandomSystem( 3000, 300, 200, - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); - testRandomSystem( + testRandomSystem( 4000, 100, 80, - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { - return micm::ProcessSet{ processes, state }; + [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + return micm::ProcessSet{ processes, state.variable_map_ }; }); } \ No newline at end of file diff --git a/test/unit/process/test_process_set_policy.hpp b/test/unit/process/test_process_set_policy.hpp index 38cb54ed4..e8609b605 100644 --- a/test/unit/process/test_process_set_policy.hpp +++ b/test/unit/process/test_process_set_policy.hpp @@ -14,7 +14,7 @@ void compare_pair(const index_pair& a, const index_pair& b) template class MatrixPolicy, template class SparseMatrixPolicy, class ProcessSetPolicy> void testProcessSet( - const std::function&, const micm::State&)> create_set) + const std::function&, const micm::State&)> create_set) { auto foo = micm::Species("foo"); auto bar = micm::Species("bar"); @@ -26,9 +26,9 @@ void testProcessSet( micm::Phase gas_phase{ std::vector{ foo, bar, qux, baz, quz, quuz } }; - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz", "quuz" }, - .number_of_grid_cells_ = 2, - .number_of_rate_constants_ = 3 } }; + micm::State state( + micm::StateParameters{ .number_of_grid_cells_ = 2, .number_of_rate_constants_ = 3, + .variable_names_{ "foo", "bar", "baz", "quz", "quuz" }}); micm::Process r1 = micm::Process::create().reactants({ foo, baz }).products({ yields(bar, 1), yields(quuz, 2.4) }).phase(gas_phase); @@ -116,12 +116,12 @@ void testProcessSet( EXPECT_EQ(jacobian[1][4][2], 100.0 + 2.4 * 110.0 * 1.1); } -template class MatrixPolicy, class ProcessSetPolicy> +template class MatrixPolicy, template class SparseMatrixPolicy, class ProcessSetPolicy> void testRandomSystem( std::size_t n_cells, std::size_t n_reactions, std::size_t n_species, - const std::function&, const micm::State&)> create_set) + const std::function&, const micm::State&)> create_set) { auto get_n_react = std::bind(std::uniform_int_distribution<>(0, 3), std::default_random_engine()); auto get_n_product = std::bind(std::uniform_int_distribution<>(0, 10), std::default_random_engine()); @@ -136,9 +136,11 @@ void testRandomSystem( species_names.push_back(std::to_string(i)); } micm::Phase gas_phase{ species }; - micm::State state{ micm::StateParameters{ .state_variable_names_{ species_names }, - .number_of_grid_cells_ = n_cells, - .number_of_rate_constants_ = n_reactions } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = n_cells, + .number_of_rate_constants_ = n_reactions, + .variable_names_{ species_names }, + } }; std::vector processes{}; for (std::size_t i = 0; i < n_reactions; ++i) { diff --git a/test/unit/process/test_surface_rate_constant.cpp b/test/unit/process/test_surface_rate_constant.cpp index 0444e7f48..5046cb7e1 100644 --- a/test/unit/process/test_surface_rate_constant.cpp +++ b/test/unit/process/test_surface_rate_constant.cpp @@ -7,7 +7,15 @@ TEST(SurfaceRateConstant, CalculateDefaultProbability) { micm::Species foo("foo", { { "molecular weight [kg mol-1]", 0.025 }, { "diffusion coefficient [m2 s-1]", 2.3e2 } }); - micm::State state{ 0, 2, 1 }; + + auto state_parameters_ = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = {"surface"}, + .custom_rate_parameter_labels_ = { "effective radius [m]", "particle number concentration [# m-3]" }, + }; + + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.0e-7; // effective radius [m] state.custom_rate_parameters_[0][1] = 2.5e6; // particle concentration [# m-3] state.conditions_[0].temperature_ = 273.65; // K @@ -25,7 +33,14 @@ TEST(SurfaceRateConstant, CalculateDefaultProbability) TEST(SurfaceRateConstant, CalculateSpecifiedProbability) { micm::Species foo("foo", { { "molecular weight [kg mol-1]", 0.025 }, { "diffusion coefficient [m2 s-1]", 2.3e2 } }); - micm::State state{ 0, 2, 1 }; + auto state_parameters_ = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = {"surface"}, + .custom_rate_parameter_labels_ = { "effective radius [m]", "particle number concentration [# m-3]" }, + }; + + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.0e-7; // effective radius [m] state.custom_rate_parameters_[0][1] = 2.5e6; // particle concentration [# m-3] state.conditions_[0].temperature_ = 273.65; // K diff --git a/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp b/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp index 85b39bdf1..630ec135b 100644 --- a/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp +++ b/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp @@ -6,15 +6,15 @@ TEST(TernaryChemicalActivationRateConstant, CalculateWithMinimalArugments) { - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions { + .temperature_ = 301.24, // [K] + .air_density_ = 42.2, // [mol mol-1] + }; micm::TernaryChemicalActivationRateConstantParameters ternary_params; ternary_params.k0_A_ = 1.0; ternary_params.kinf_A_ = 1.0; micm::TernaryChemicalActivationRateConstant ternary{ ternary_params }; - auto k = ternary.calculate(state.conditions_[0], params); + auto k = ternary.calculate(conditions); double k0 = 1.0; double kinf = 1.0; EXPECT_EQ(k, k0 / (1.0 + k0 * 42.2 / kinf) * std::pow(0.6, 1.0 / (1 + std::pow(std::log10(k0 * 42.2 / kinf), 2)))); @@ -22,11 +22,11 @@ TEST(TernaryChemicalActivationRateConstant, CalculateWithMinimalArugments) TEST(TernaryChemicalActivationRateConstant, CalculateWithAllArugments) { - micm::State state{ 0, 0, 1 }; - double temperature = 301.24; - state.conditions_[0].temperature_ = temperature; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + double temperature = 301.24; // [K] + micm::Conditions conditions { + .temperature_ = temperature, + .air_density_ = 42.2, // [mol mol-1] + }; micm::TernaryChemicalActivationRateConstant ternary{ micm::TernaryChemicalActivationRateConstantParameters{ .k0_A_ = 1.2, .k0_B_ = 2.3, @@ -36,7 +36,7 @@ TEST(TernaryChemicalActivationRateConstant, CalculateWithAllArugments) .kinf_C_ = 402.1, .Fc_ = 0.9, .N_ = 1.2 } }; - auto k = ternary.calculate(state.conditions_[0], params); + auto k = ternary.calculate(conditions); double k0 = 1.2 * std::exp(302.3 / temperature) * std::pow(temperature / 300.0, 2.3); double kinf = 2.6 * std::exp(402.1 / temperature) * std::pow(temperature / 300.0, -3.1); EXPECT_EQ( diff --git a/test/unit/process/test_troe_rate_constant.cpp b/test/unit/process/test_troe_rate_constant.cpp index 029def7d2..674631b8f 100644 --- a/test/unit/process/test_troe_rate_constant.cpp +++ b/test/unit/process/test_troe_rate_constant.cpp @@ -6,15 +6,15 @@ TEST(TroeRateConstant, CalculateWithMinimalArugments) { - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions { + .temperature_ = 301.24, // [K] + .air_density_ = 42.2, // [mol mol-1] + }; micm::TroeRateConstantParameters troe_params; troe_params.k0_A_ = 1.0; troe_params.kinf_A_ = 1.0; micm::TroeRateConstant troe{ troe_params }; - auto k = troe.calculate(state.conditions_[0], params); + auto k = troe.calculate(conditions); double k0 = 1.0; double kinf = 1.0; EXPECT_EQ(k, 42.2 * k0 / (1.0 + 42.2 * k0 / kinf) * std::pow(0.6, 1.0 / (1 + std::pow(std::log10(42.2 * k0 / kinf), 2)))); @@ -22,11 +22,11 @@ TEST(TroeRateConstant, CalculateWithMinimalArugments) TEST(TroeRateConstant, CalculateWithAllArugments) { - micm::State state{ 0, 0, 1 }; - double temperature = 301.24; - state.conditions_[0].temperature_ = temperature; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + double temperature = 301.24; // [K] + micm::Conditions conditions { + .temperature_ = temperature, + .air_density_ = 42.2, // [mol mol-1] + }; micm::TroeRateConstant troe{ micm::TroeRateConstantParameters{ .k0_A_ = 1.2, .k0_B_ = 2.3, .k0_C_ = 302.3, @@ -35,7 +35,7 @@ TEST(TroeRateConstant, CalculateWithAllArugments) .kinf_C_ = 402.1, .Fc_ = 0.9, .N_ = 1.2 } }; - auto k = troe.calculate(state.conditions_[0], params); + auto k = troe.calculate(conditions); double k0 = 1.2 * std::exp(302.3 / temperature) * std::pow(temperature / 300.0, 2.3); double kinf = 2.6 * std::exp(402.1 / temperature) * std::pow(temperature / 300.0, -3.1); EXPECT_EQ( @@ -47,17 +47,16 @@ TEST(TroeRateConstant, CalculateWithAllArugments) TEST(TroeRateConstant, AnalyticalTroeExampleAB) { // based off of the troe rate constants in the analytical integration test: - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - - auto params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions { + .temperature_ = 301.24, // [K] + .air_density_ = 42.2, // [mol mol-1] + }; micm::TroeRateConstantParameters troe_params; troe_params.k0_A_ = 4.0e-18; micm::TroeRateConstant troe{ troe_params }; - auto k = troe.calculate(state.conditions_[0], params); + auto k = troe.calculate(conditions); double k_0 = 4.0e-18; double k_inf = 1; @@ -70,18 +69,17 @@ TEST(TroeRateConstant, AnalyticalTroeExampleAB) TEST(TroeRateConstant, AnalyticalTroeExampleBC) { // based off of the troe rate constants in the analytical integration test: - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - state.conditions_[0].air_density_ = 42.2; // [mol mol-1] - - auto params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions { + .temperature_ = 301.24, // [K] + .air_density_ = 42.2, // [mol mol-1] + }; micm::TroeRateConstantParameters troe_params{ .k0_A_ = 1.2e-12, .k0_B_ = 167.0, .k0_C_ = 3.0, .kinf_A_ = 136.0, .kinf_B_ = 5.0, .kinf_C_ = 24.0, .Fc_ = 0.9, .N_ = 0.8 }; micm::TroeRateConstant troe{ troe_params }; - auto k = troe.calculate(state.conditions_[0], params); + auto k = troe.calculate(conditions); double k_0 = 1.2e-12 * std::exp(3.0 / 301.24) * std::pow(301.24 / 300.0, 167.0); double k_inf = 136.0 * std::exp(24.0 / 301.24) * std::pow(301.24 / 300.0, 5.0); diff --git a/test/unit/process/test_tunneling_rate_constant.cpp b/test/unit/process/test_tunneling_rate_constant.cpp index 212f32c33..e83ee5af1 100644 --- a/test/unit/process/test_tunneling_rate_constant.cpp +++ b/test/unit/process/test_tunneling_rate_constant.cpp @@ -6,22 +6,22 @@ TEST(TunnelingRateConstant, CalculateWithMinimalArugments) { - micm::State state{ 0, 0, 1 }; - state.conditions_[0].temperature_ = 301.24; // [K] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions { + .temperature_ = 301.24, // [K] + }; micm::TunnelingRateConstantParameters tunneling_params; micm::TunnelingRateConstant tunneling{ tunneling_params }; - auto k = tunneling.calculate(state.conditions_[0], params); + auto k = tunneling.calculate(conditions); EXPECT_NEAR(k, 1.0, 1.0e-8); } TEST(TunnelingRateConstant, CalculateWithAllArugments) { - micm::State state{ 0, 0, 1 }; double temperature = 301.24; - state.conditions_[0].temperature_ = temperature; // [K] - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::Conditions conditions { + .temperature_ = temperature, // [K] + }; micm::TunnelingRateConstant tunneling{ micm::TunnelingRateConstantParameters{ .A_ = 1.2, .B_ = 2.3, .C_ = 302.3 } }; - auto k = tunneling.calculate(state.conditions_[0], params); + auto k = tunneling.calculate(conditions); EXPECT_NEAR(k, 1.2 * std::exp(-2.3 / temperature) * std::exp(302.3 / std::pow(temperature, 3)), 1.0e-8); } diff --git a/test/unit/process/test_user_defined_rate_constant.cpp b/test/unit/process/test_user_defined_rate_constant.cpp index 9be644134..a92d9d641 100644 --- a/test/unit/process/test_user_defined_rate_constant.cpp +++ b/test/unit/process/test_user_defined_rate_constant.cpp @@ -6,8 +6,16 @@ TEST(UserDefinedRateConstant, CalculateWithSystem) { - micm::State state{ 0, 1, 1 }; + auto state_parameters_ = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = {"user"}, + .custom_rate_parameter_labels_ = { "my rate", }, + }; + + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 0.5; + std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); micm::UserDefinedRateConstant photo{}; auto k = photo.calculate(state.conditions_[0], params); @@ -16,8 +24,16 @@ TEST(UserDefinedRateConstant, CalculateWithSystem) TEST(UserDefinedRateConstant, ConstructorWithRate) { - micm::State state{ 0, 1, 1 }; + auto state_parameters_ = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = {"user"}, + .custom_rate_parameter_labels_ = { "my rate", }, + }; + + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.1; + std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); micm::UserDefinedRateConstant photo{}; auto k = photo.calculate(state.conditions_[0], params); @@ -26,7 +42,14 @@ TEST(UserDefinedRateConstant, ConstructorWithRate) TEST(UserDefinedRateConstant, ConstructorWithRateAndName) { - micm::State state{ 0, 1, 1 }; + auto state_parameters_ = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = {"user"}, + .custom_rate_parameter_labels_ = { "my rate", }, + }; + + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.1; std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); micm::UserDefinedRateConstant photo({ .label_ = "a name" }); diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index 52b99ded3..9651df19c 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -59,7 +59,7 @@ void testAlphaMinusJacobian(std::shared_ptr jit) { auto solver = getSolver(jit); // return; - auto jacobian = solver.jacobian_; + auto jacobian = solver.GetState().jacobian_; EXPECT_EQ(jacobian.size(), number_of_grid_cells); EXPECT_EQ(jacobian[0].size(), 5); diff --git a/test/unit/solver/test_linear_solver_policy.hpp b/test/unit/solver/test_linear_solver_policy.hpp index 522a426c6..8bdeca9b0 100644 --- a/test/unit/solver/test_linear_solver_policy.hpp +++ b/test/unit/solver/test_linear_solver_policy.hpp @@ -2,6 +2,7 @@ #include #include +#include template class MatrixPolicy, template class SparseMatrixPolicy> void check_results( @@ -84,8 +85,11 @@ void testDenseMatrix(const std::function(b, x); + auto lu = micm::LuDecomposition::GetLUMatrices(A, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + solver.Factor(A, lower_matrix, upper_matrix); + solver.template Solve(b, x, lower_matrix, upper_matrix); check_results( A, b, x, [&](const double a, const double b) -> void { EXPECT_NEAR(a, b, 1.0e-5); }); } @@ -119,8 +123,11 @@ void testRandomMatrix( b[i_block][i] = get_double(); LinearSolverPolicy solver = create_linear_solver(A, 1.0e-30); - solver.Factor(A); - solver.template Solve(b, x); + auto lu = micm::LuDecomposition::GetLUMatrices(A, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + solver.Factor(A, lower_matrix, upper_matrix); + solver.template Solve(b, x, lower_matrix, upper_matrix); check_results( A, b, x, [&](const double a, const double b) -> void { EXPECT_NEAR(a, b, 1.0e-5); }); } @@ -145,8 +152,11 @@ void testDiagonalMatrix( A[i_block][i][i] = get_double(); LinearSolverPolicy solver = create_linear_solver(A, 1.0e-30); - solver.Factor(A); - solver.template Solve(b, x); + auto lu = micm::LuDecomposition::GetLUMatrices(A, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + solver.Factor(A, lower_matrix, upper_matrix); + solver.template Solve(b, x, lower_matrix, upper_matrix); check_results( A, b, x, [&](const double a, const double b) -> void { EXPECT_NEAR(a, b, 1.0e-5); }); } diff --git a/test/unit/solver/test_rosenbrock.cpp b/test/unit/solver/test_rosenbrock.cpp index 48b44ede2..c54fb0f1d 100644 --- a/test/unit/solver/test_rosenbrock.cpp +++ b/test/unit/solver/test_rosenbrock.cpp @@ -53,7 +53,7 @@ template class MatrixPolicy, template class SparseMatrixP void testAlphaMinusJacobian(std::size_t number_of_grid_cells) { auto solver = getSolver(number_of_grid_cells); - auto jacobian = solver.jacobian_; + auto jacobian = solver.GetState().jacobian_; EXPECT_EQ(jacobian.size(), number_of_grid_cells); EXPECT_EQ(jacobian[0].size(), 5); diff --git a/test/unit/solver/test_state.cpp b/test/unit/solver/test_state.cpp index 053ea1bfd..db8217afd 100644 --- a/test/unit/solver/test_state.cpp +++ b/test/unit/solver/test_state.cpp @@ -4,10 +4,12 @@ TEST(State, Constructor) { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "quux", "corge" }, - .number_of_grid_cells_ = 3, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 3, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "quux", "corge" }, + } }; EXPECT_EQ(state.conditions_.size(), 3); EXPECT_EQ(state.variable_map_["foo"], 0); @@ -26,10 +28,12 @@ TEST(State, Constructor) TEST(State, SettingSingleConcentrationWithInvalidArgumentsThowsException) { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "quux", "corge" }, - .number_of_grid_cells_ = 3, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 3, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "quux", "corge" }, + } }; EXPECT_ANY_THROW(state.SetConcentration(micm::Species{ "foo" }, 1.0)); EXPECT_ANY_THROW(state.SetConcentration(micm::Species{ "foo" }, std::vector{ 1.0, 2.0 })); EXPECT_ANY_THROW(state.SetConcentration(micm::Species{ "not foo" }, 1.0)); @@ -39,20 +43,24 @@ TEST(State, SettingSingleConcentrationWithInvalidArgumentsThowsException) TEST(State, SetSingleConcentration) { { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "quux", "corge" }, - .number_of_grid_cells_ = 3, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 3, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "quux", "corge" }, + } }; std::vector concentrations{ 12.0, 42.0, 35.2 }; state.SetConcentration(micm::Species{ "bar" }, concentrations); for (std::size_t i = 0; i < concentrations.size(); ++i) EXPECT_EQ(state.variables_[i][state.variable_map_["bar"]], concentrations[i]); } { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "quux", "corge" }, - .number_of_grid_cells_ = 1, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "quux", "corge" }, + } }; state.SetConcentration(micm::Species{ "bar" }, 324.2); EXPECT_EQ(state.variables_[0][state.variable_map_["bar"]], 324.2); } @@ -60,10 +68,12 @@ TEST(State, SetSingleConcentration) TEST(State, SettingConcentrationsWithInvalidArguementsThrowsException) { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "quux", "corge" }, - .number_of_grid_cells_ = 3, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 3, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "quux", "corge" }, + } }; std::unordered_map> concentrations = { { "FUU", { 0.1 } }, { "bar", { 0.2 } }, { "baz", { 0.3 } }, { "quz", { 0.4 } } @@ -77,10 +87,12 @@ TEST(State, SetConcentrations) uint32_t num_grid_cells = 3; uint32_t num_species = 4; - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "quux", "corge" }, - .number_of_grid_cells_ = num_grid_cells, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = num_grid_cells, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "quux", "corge" }, + } }; std::unordered_map> concentrations = { { "bar", { 0.2, 0.22, 0.222 } }, { "baz", { 0.3, 0.33, 0.333 } }, @@ -106,10 +118,12 @@ TEST(State, SetConcentrations) TEST(State, SettingCustomRateParameterWithInvalidVectorSizeThrowsException) { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, - .number_of_grid_cells_ = 3, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 3, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, + } }; // user input for custom rate parameters (unordered) std::unordered_map> custom_params = { @@ -121,10 +135,12 @@ TEST(State, SettingCustomRateParameterWithInvalidVectorSizeThrowsException) TEST(State, SettingCustomRateParameterWithInvalidLabelThrowsException) { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, - .number_of_grid_cells_ = 1, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, + } }; // user input for custom rate parameters (unordered) std::unordered_map> custom_params = { @@ -136,10 +152,12 @@ TEST(State, SettingCustomRateParameterWithInvalidLabelThrowsException) TEST(State, SetCustomRateParameter) { - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, - .number_of_grid_cells_ = 1, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, + } }; state.SetCustomRateParameter("O2", 42.3); EXPECT_EQ(state.custom_rate_parameters_[0][1], 42.3); @@ -149,10 +167,12 @@ TEST(State, SetCustomRateParameters) { uint32_t num_grid_cells = 3; - micm::State state{ micm::StateParameters{ .state_variable_names_{ "foo", "bar", "baz", "quz" }, - .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, - .number_of_grid_cells_ = num_grid_cells, - .number_of_rate_constants_ = 10 } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = num_grid_cells, + .number_of_rate_constants_ = 10, + .variable_names_{ "foo", "bar", "baz", "quz" }, + .custom_rate_parameter_labels_{ "O1", "O2", "O3", "AAA", "BBB" }, + } }; // user input for custom rate parameters (unordered) std::unordered_map> custom_params = { { "O3", { 0.3, 0.33, 0.333 } }, diff --git a/test/unit/util/test_matrix_policy.hpp b/test/unit/util/test_matrix_policy.hpp index f9ca8dd5e..1ee20861c 100644 --- a/test/unit/util/test_matrix_policy.hpp +++ b/test/unit/util/test_matrix_policy.hpp @@ -170,7 +170,7 @@ MatrixPolicy testConversionFromVector() std::vector> bad_vector = { { 3 }, { 4, 5 }, { 5 } }; MatrixPolicy bad_matrix; - EXPECT_DEATH(bad_matrix = bad_vector, "Invalid vector for matrix assignment"); + EXPECT_ANY_THROW(bad_matrix = bad_vector); return matrix; } @@ -199,7 +199,7 @@ MatrixPolicy testAssignmentFromVector() EXPECT_EQ(matrix[2][2], 65.7); EXPECT_EQ(matrix[3][0], 0.0); - EXPECT_DEATH(matrix[2] = small_other, "Matrix row size mismatch in assignment from vector"); + EXPECT_ANY_THROW(matrix[2] = small_other); return matrix; } From ff54209d80fddf18623ca07552da5699fe6022e1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 27 Oct 2023 14:59:10 -0700 Subject: [PATCH 206/318] Auto-format code changes (#334) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/process/cuda_process_set.hpp | 6 +++-- include/micm/process/jit_process_set.hpp | 4 +-- include/micm/process/process_set.hpp | 4 ++- .../micm/process/surface_rate_constant.hpp | 6 ++--- .../process/user_defined_rate_constant.hpp | 3 ++- include/micm/solver/cuda_lu_decomposition.hpp | 2 +- include/micm/solver/linear_solver.hpp | 25 +++++++++++++------ include/micm/solver/rosenbrock.hpp | 14 ++++++++--- .../solver/rosenbrock_solver_parameters.hpp | 5 ++-- test/integration/e5.hpp | 4 ++- test/integration/hires.hpp | 4 ++- test/integration/oregonator.hpp | 4 ++- .../RosenbrockChapman/chapman_ode_solver.hpp | 2 +- test/tutorial/test_solver_configuration.cpp | 1 - test/unit/openmp/test_openmp.cpp | 6 +++-- test/unit/openmp/test_openmp_jit_solver.cpp | 18 +++++-------- test/unit/process/test_process.cpp | 11 ++++---- test/unit/process/test_process_set.cpp | 24 ++++++++++++------ test/unit/process/test_process_set_policy.hpp | 14 ++++++----- .../process/test_surface_rate_constant.cpp | 8 +++--- ...nary_chemical_activation_rate_constant.cpp | 10 ++++---- test/unit/process/test_troe_rate_constant.cpp | 14 +++++------ .../process/test_tunneling_rate_constant.cpp | 4 +-- .../test_user_defined_rate_constant.cpp | 6 ++--- .../unit/solver/test_linear_solver_policy.hpp | 2 +- 25 files changed, 117 insertions(+), 84 deletions(-) diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index 31b3d580a..39e8e5c08 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include -#include namespace micm { @@ -34,7 +34,9 @@ namespace micm const; }; - inline CudaProcessSet::CudaProcessSet(const std::vector& processes, const std::map& variable_map) + inline CudaProcessSet::CudaProcessSet( + const std::vector& processes, + const std::map& variable_map) : ProcessSet(processes, variable_map) { } diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 077de39df..539389f8b 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -36,7 +36,7 @@ namespace micm JitProcessSet( std::shared_ptr compiler, const std::vector &processes, - const std::map& variable_map); + const std::map &variable_map); ~JitProcessSet(); @@ -105,7 +105,7 @@ namespace micm inline JitProcessSet::JitProcessSet( std::shared_ptr compiler, const std::vector &processes, - const std::map& variable_map) + const std::map &variable_map) : ProcessSet(processes, variable_map), compiler_(compiler) { diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index f7836f7c6..ff3b0c0a6 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -73,7 +73,9 @@ namespace micm SparseMatrixPolicy& jacobian) const; }; - inline ProcessSet::ProcessSet(const std::vector& processes, const std::map& variable_map) + inline ProcessSet::ProcessSet( + const std::vector& processes, + const std::map& variable_map) : number_of_reactants_(), reactant_ids_(), number_of_products_(), diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index e72cb477e..714b7fbd7 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -75,10 +75,10 @@ namespace micm return std::unique_ptr{ new SurfaceRateConstant{ *this } }; } - inline double SurfaceRateConstant::calculate( - const Conditions& conditions) const + inline double SurfaceRateConstant::calculate(const Conditions& conditions) const { - throw std::runtime_error("Surface rate constants must be supplied with a radius and number density using the alternative calculate function"); + throw std::runtime_error( + "Surface rate constants must be supplied with a radius and number density using the alternative calculate function"); } inline double SurfaceRateConstant::calculate( diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index 68f48cd38..02130da45 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -67,7 +67,8 @@ namespace micm inline double UserDefinedRateConstant::calculate(const Conditions& conditions) const { - throw std::runtime_error("User defined rate constants must be supplied with custom rate parameters using the alternative calculate function"); + throw std::runtime_error( + "User defined rate constants must be supplied with custom rate parameters using the alternative calculate function"); } inline double UserDefinedRateConstant::calculate( diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index 85aca2a75..118a64fe7 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -4,10 +4,10 @@ #pragma once #include +#include #include #include #include -#include namespace micm { diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index 44569dc2a..9af334502 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -69,20 +69,31 @@ namespace micm const std::function&)> create_lu_decomp); /// @brief Decompose the matrix into upper and lower triangular matrices - void Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); - + void + Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); + /// @brief Decompose the matrix into upper and lower triangular matrices /// @param matrix Matrix to decompose into lower and upper triangular matrices /// @param is_singular Flag that is set to true if matrix is singular; false otherwise - void Factor(const SparseMatrixPolicy& matrix, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix, bool& is_singular); + void Factor( + const SparseMatrixPolicy& matrix, + SparseMatrixPolicy& lower_matrix, + SparseMatrixPolicy& upper_matrix, + bool& is_singular); /// @brief Solve for x in Ax = b template class MatrixPolicy> - requires(!VectorizableDense> || !VectorizableSparse>) - void Solve(const MatrixPolicy& b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); + requires(!VectorizableDense> || !VectorizableSparse>) void Solve( + const MatrixPolicy& b, + MatrixPolicy& x, + SparseMatrixPolicy& lower_matrix, + SparseMatrixPolicy& upper_matrix); template class MatrixPolicy> - requires(VectorizableDense> && VectorizableSparse>) - void Solve(const MatrixPolicy& b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix); + requires(VectorizableDense>&& VectorizableSparse>) void Solve( + const MatrixPolicy& b, + MatrixPolicy& x, + SparseMatrixPolicy& lower_matrix, + SparseMatrixPolicy& upper_matrix); }; } // namespace micm diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index e78ed639e..4c8b1037b 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -26,8 +26,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -75,7 +75,8 @@ namespace micm uint64_t decompositions{}; /// @brief The number of linear solves uint64_t solves{}; - /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix is never singular + /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix + /// is never singular uint64_t singular{}; /// @brief The cumulative amount of time spent calculating the forcing function std::chrono::duration total_forcing_time{}; @@ -100,7 +101,6 @@ namespace micm class RosenbrockSolver { public: - struct [[nodiscard]] SolverResult { /// @brief The new state computed by the solver @@ -193,7 +193,13 @@ namespace micm /// @param singular indicates if the matrix is singular /// @param number_densities constituent concentration (molec/cm^3) /// @param rate_constants Rate constants for each process (molecule/cm3)^(n-1) s-1 - void LinearFactor(double& H, const double gamma, bool& singular, const MatrixPolicy& number_densities, SolverStats& stats, State& state); + void LinearFactor( + double& H, + const double gamma, + bool& singular, + const MatrixPolicy& number_densities, + SolverStats& stats, + State& state); protected: /// @brief Computes the scaled norm of the vector errors diff --git a/include/micm/solver/rosenbrock_solver_parameters.hpp b/include/micm/solver/rosenbrock_solver_parameters.hpp index bfb4276fe..e492bb772 100644 --- a/include/micm/solver/rosenbrock_solver_parameters.hpp +++ b/include/micm/solver/rosenbrock_solver_parameters.hpp @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once +#include #include #include -#include namespace micm { @@ -458,5 +458,4 @@ namespace micm return parameters; } - -} \ No newline at end of file +} // namespace micm \ No newline at end of file diff --git a/test/integration/e5.hpp b/test/integration/e5.hpp index f5763df1e..f4118ba72 100644 --- a/test/integration/e5.hpp +++ b/test/integration/e5.hpp @@ -64,7 +64,9 @@ class E5 : public micm::RosenbrockSolver{ this->state_parameters_ }; state.jacobian_ = micm::build_jacobian( - nonzero_jacobian_elements_, this->state_parameters_.number_of_grid_cells_, this->state_parameters_.variable_names_.size()); + nonzero_jacobian_elements_, + this->state_parameters_.number_of_grid_cells_, + this->state_parameters_.variable_names_.size()); auto lu = micm::LuDecomposition::GetLUMatrices(state.jacobian_, 1.0e-30); auto lower_matrix = std::move(lu.first); diff --git a/test/integration/hires.hpp b/test/integration/hires.hpp index 0c39a896f..527696c44 100644 --- a/test/integration/hires.hpp +++ b/test/integration/hires.hpp @@ -65,7 +65,9 @@ class HIRES : public micm::RosenbrockSolver{ this->state_parameters_ }; state.jacobian_ = micm::build_jacobian( - nonzero_jacobian_elements_, this->state_parameters_.number_of_grid_cells_, this->state_parameters_.variable_names_.size()); + nonzero_jacobian_elements_, + this->state_parameters_.number_of_grid_cells_, + this->state_parameters_.variable_names_.size()); auto lu = micm::LuDecomposition::GetLUMatrices(state.jacobian_, 1.0e-30); auto lower_matrix = std::move(lu.first); diff --git a/test/integration/oregonator.hpp b/test/integration/oregonator.hpp index 6c457c282..c1bae4742 100644 --- a/test/integration/oregonator.hpp +++ b/test/integration/oregonator.hpp @@ -65,7 +65,9 @@ class Oregonator : public micm::RosenbrockSolver{ this->state_parameters_ }; state.jacobian_ = micm::build_jacobian( - nonzero_jacobian_elements_, this->state_parameters_.number_of_grid_cells_, this->state_parameters_.variable_names_.size()); + nonzero_jacobian_elements_, + this->state_parameters_.number_of_grid_cells_, + this->state_parameters_.variable_names_.size()); auto lu = micm::LuDecomposition::GetLUMatrices(state.jacobian_, 1.0e-30); auto lower_matrix = std::move(lu.first); diff --git a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp index 917710fc8..cd2f20eda 100644 --- a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp +++ b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp @@ -311,7 +311,7 @@ namespace micm .variable_names_ = species_names(), .custom_rate_parameter_labels_ = photolysis_names(), }; - + return micm::State{ state_parameters }; } diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp index 16616c400..5e45635d3 100644 --- a/test/tutorial/test_solver_configuration.cpp +++ b/test/tutorial/test_solver_configuration.cpp @@ -61,7 +61,6 @@ void test_solver_type(T solver) SolverStats total_stats; std::chrono::duration total_solve_time = std::chrono::nanoseconds::zero(); - // solve for ten iterations for (int i = 0; i < 10; ++i) { diff --git a/test/unit/openmp/test_openmp.cpp b/test/unit/openmp/test_openmp.cpp index 575c12a24..313809bfa 100644 --- a/test/unit/openmp/test_openmp.cpp +++ b/test/unit/openmp/test_openmp.cpp @@ -39,8 +39,10 @@ TEST(OpenMP, OneSolverManyStates) } // compare each thread to thread 1 - for (int i = 1; i < n_threads; ++i) { - for (int j = 0; j < results[0].size(); ++j) { + for (int i = 1; i < n_threads; ++i) + { + for (int j = 0; j < results[0].size(); ++j) + { EXPECT_EQ(results[0][j], results[i][j]); } } diff --git a/test/unit/openmp/test_openmp_jit_solver.cpp b/test/unit/openmp/test_openmp_jit_solver.cpp index 5e43588a1..0044cea35 100644 --- a/test/unit/openmp/test_openmp_jit_solver.cpp +++ b/test/unit/openmp/test_openmp_jit_solver.cpp @@ -36,16 +36,8 @@ TEST(OpenMP, JITOneSolverManyStates) std::vector> results(n_threads); auto jit{ micm::JitCompiler::create() }; - JitRosenbrockSolver< - Group1VectorMatrix, - Group1SparseVectorMatrix, - JitLinearSolver<1, Group1SparseVectorMatrix> - > solver( - jit.get(), - chemical_system, - reactions, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - ); + JitRosenbrockSolver> solver( + jit.get(), chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters()); #pragma omp parallel num_threads(n_threads) { @@ -56,8 +48,10 @@ TEST(OpenMP, JITOneSolverManyStates) } // compare each thread to thread 1 - for (int i = 1; i < n_threads; ++i) { - for (int j = 0; j < results[0].size(); ++j) { + for (int i = 1; i < n_threads; ++i) + { + for (int j = 0; j < results[0].size(); ++j) + { EXPECT_EQ(results[0][j], results[i][j]); } } diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index aa8ed0be5..2088a47ea 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -30,11 +30,12 @@ void testProcessUpdateState(const std::size_t number_of_grid_cells) for (const auto& process : processes) for (auto& label : process.rate_constant_->CustomParameters()) param_labels.push_back(label); - micm::State state{ micm::StateParameters{ .number_of_grid_cells_ = number_of_grid_cells, - .number_of_rate_constants_ = processes.size(), - .variable_names_ = { "foo", "bar'" }, - .custom_rate_parameter_labels_ = param_labels, - } }; + micm::State state{ micm::StateParameters{ + .number_of_grid_cells_ = number_of_grid_cells, + .number_of_rate_constants_ = processes.size(), + .variable_names_ = { "foo", "bar'" }, + .custom_rate_parameter_labels_ = param_labels, + } }; MatrixPolicy expected_rate_constants(number_of_grid_cells, 3, 0.0); std::vector params = { 0.0, 0.0, 0.0 }; diff --git a/test/unit/process/test_process_set.cpp b/test/unit/process/test_process_set.cpp index ba7a51d85..d181090f9 100644 --- a/test/unit/process/test_process_set.cpp +++ b/test/unit/process/test_process_set.cpp @@ -34,7 +34,8 @@ using Group4SparseVectorMatrix = micm::SparseMatrix( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); } @@ -42,19 +43,23 @@ TEST(ProcessSet, Matrix) TEST(ProcessSet, VectorMatrix) { testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testProcessSet( - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); } @@ -65,21 +70,24 @@ TEST(RandomProcessSet, Matrix) 2000, 500, 400, - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testRandomSystem( 3000, 300, 200, - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testRandomSystem( 4000, 100, 80, - [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { + [](const std::vector& processes, + const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); } \ No newline at end of file diff --git a/test/unit/process/test_process_set_policy.hpp b/test/unit/process/test_process_set_policy.hpp index e8609b605..f9d2fc66d 100644 --- a/test/unit/process/test_process_set_policy.hpp +++ b/test/unit/process/test_process_set_policy.hpp @@ -13,8 +13,9 @@ void compare_pair(const index_pair& a, const index_pair& b) } template class MatrixPolicy, template class SparseMatrixPolicy, class ProcessSetPolicy> -void testProcessSet( - const std::function&, const micm::State&)> create_set) +void testProcessSet(const std::function&, + const micm::State&)> create_set) { auto foo = micm::Species("foo"); auto bar = micm::Species("bar"); @@ -26,9 +27,8 @@ void testProcessSet( micm::Phase gas_phase{ std::vector{ foo, bar, qux, baz, quz, quuz } }; - micm::State state( - micm::StateParameters{ .number_of_grid_cells_ = 2, .number_of_rate_constants_ = 3, - .variable_names_{ "foo", "bar", "baz", "quz", "quuz" }}); + micm::State state(micm::StateParameters{ + .number_of_grid_cells_ = 2, .number_of_rate_constants_ = 3, .variable_names_{ "foo", "bar", "baz", "quz", "quuz" } }); micm::Process r1 = micm::Process::create().reactants({ foo, baz }).products({ yields(bar, 1), yields(quuz, 2.4) }).phase(gas_phase); @@ -121,7 +121,9 @@ void testRandomSystem( std::size_t n_cells, std::size_t n_reactions, std::size_t n_species, - const std::function&, const micm::State&)> create_set) + const std::function< + ProcessSetPolicy(const std::vector&, const micm::State&)> + create_set) { auto get_n_react = std::bind(std::uniform_int_distribution<>(0, 3), std::default_random_engine()); auto get_n_product = std::bind(std::uniform_int_distribution<>(0, 10), std::default_random_engine()); diff --git a/test/unit/process/test_surface_rate_constant.cpp b/test/unit/process/test_surface_rate_constant.cpp index 5046cb7e1..3833f072c 100644 --- a/test/unit/process/test_surface_rate_constant.cpp +++ b/test/unit/process/test_surface_rate_constant.cpp @@ -11,10 +11,10 @@ TEST(SurfaceRateConstant, CalculateDefaultProbability) auto state_parameters_ = micm::StateParameters{ .number_of_grid_cells_ = 1, .number_of_rate_constants_ = 1, - .variable_names_ = {"surface"}, + .variable_names_ = { "surface" }, .custom_rate_parameter_labels_ = { "effective radius [m]", "particle number concentration [# m-3]" }, }; - + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.0e-7; // effective radius [m] state.custom_rate_parameters_[0][1] = 2.5e6; // particle concentration [# m-3] @@ -36,10 +36,10 @@ TEST(SurfaceRateConstant, CalculateSpecifiedProbability) auto state_parameters_ = micm::StateParameters{ .number_of_grid_cells_ = 1, .number_of_rate_constants_ = 1, - .variable_names_ = {"surface"}, + .variable_names_ = { "surface" }, .custom_rate_parameter_labels_ = { "effective radius [m]", "particle number concentration [# m-3]" }, }; - + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.0e-7; // effective radius [m] state.custom_rate_parameters_[0][1] = 2.5e6; // particle concentration [# m-3] diff --git a/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp b/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp index 630ec135b..3210359f1 100644 --- a/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp +++ b/test/unit/process/test_ternary_chemical_activation_rate_constant.cpp @@ -6,7 +6,7 @@ TEST(TernaryChemicalActivationRateConstant, CalculateWithMinimalArugments) { - micm::Conditions conditions { + micm::Conditions conditions{ .temperature_ = 301.24, // [K] .air_density_ = 42.2, // [mol mol-1] }; @@ -22,10 +22,10 @@ TEST(TernaryChemicalActivationRateConstant, CalculateWithMinimalArugments) TEST(TernaryChemicalActivationRateConstant, CalculateWithAllArugments) { - double temperature = 301.24; // [K] - micm::Conditions conditions { - .temperature_ = temperature, - .air_density_ = 42.2, // [mol mol-1] + double temperature = 301.24; // [K] + micm::Conditions conditions{ + .temperature_ = temperature, + .air_density_ = 42.2, // [mol mol-1] }; micm::TernaryChemicalActivationRateConstant ternary{ micm::TernaryChemicalActivationRateConstantParameters{ .k0_A_ = 1.2, diff --git a/test/unit/process/test_troe_rate_constant.cpp b/test/unit/process/test_troe_rate_constant.cpp index 674631b8f..fbe5cc4e3 100644 --- a/test/unit/process/test_troe_rate_constant.cpp +++ b/test/unit/process/test_troe_rate_constant.cpp @@ -6,7 +6,7 @@ TEST(TroeRateConstant, CalculateWithMinimalArugments) { - micm::Conditions conditions { + micm::Conditions conditions{ .temperature_ = 301.24, // [K] .air_density_ = 42.2, // [mol mol-1] }; @@ -22,10 +22,10 @@ TEST(TroeRateConstant, CalculateWithMinimalArugments) TEST(TroeRateConstant, CalculateWithAllArugments) { - double temperature = 301.24; // [K] - micm::Conditions conditions { - .temperature_ = temperature, - .air_density_ = 42.2, // [mol mol-1] + double temperature = 301.24; // [K] + micm::Conditions conditions{ + .temperature_ = temperature, + .air_density_ = 42.2, // [mol mol-1] }; micm::TroeRateConstant troe{ micm::TroeRateConstantParameters{ .k0_A_ = 1.2, .k0_B_ = 2.3, @@ -47,7 +47,7 @@ TEST(TroeRateConstant, CalculateWithAllArugments) TEST(TroeRateConstant, AnalyticalTroeExampleAB) { // based off of the troe rate constants in the analytical integration test: - micm::Conditions conditions { + micm::Conditions conditions{ .temperature_ = 301.24, // [K] .air_density_ = 42.2, // [mol mol-1] }; @@ -69,7 +69,7 @@ TEST(TroeRateConstant, AnalyticalTroeExampleAB) TEST(TroeRateConstant, AnalyticalTroeExampleBC) { // based off of the troe rate constants in the analytical integration test: - micm::Conditions conditions { + micm::Conditions conditions{ .temperature_ = 301.24, // [K] .air_density_ = 42.2, // [mol mol-1] }; diff --git a/test/unit/process/test_tunneling_rate_constant.cpp b/test/unit/process/test_tunneling_rate_constant.cpp index e83ee5af1..8c77ce08a 100644 --- a/test/unit/process/test_tunneling_rate_constant.cpp +++ b/test/unit/process/test_tunneling_rate_constant.cpp @@ -6,7 +6,7 @@ TEST(TunnelingRateConstant, CalculateWithMinimalArugments) { - micm::Conditions conditions { + micm::Conditions conditions{ .temperature_ = 301.24, // [K] }; micm::TunnelingRateConstantParameters tunneling_params; @@ -18,7 +18,7 @@ TEST(TunnelingRateConstant, CalculateWithMinimalArugments) TEST(TunnelingRateConstant, CalculateWithAllArugments) { double temperature = 301.24; - micm::Conditions conditions { + micm::Conditions conditions{ .temperature_ = temperature, // [K] }; micm::TunnelingRateConstant tunneling{ micm::TunnelingRateConstantParameters{ .A_ = 1.2, .B_ = 2.3, .C_ = 302.3 } }; diff --git a/test/unit/process/test_user_defined_rate_constant.cpp b/test/unit/process/test_user_defined_rate_constant.cpp index a92d9d641..50e30e26b 100644 --- a/test/unit/process/test_user_defined_rate_constant.cpp +++ b/test/unit/process/test_user_defined_rate_constant.cpp @@ -12,7 +12,7 @@ TEST(UserDefinedRateConstant, CalculateWithSystem) .variable_names_ = {"user"}, .custom_rate_parameter_labels_ = { "my rate", }, }; - + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 0.5; @@ -30,7 +30,7 @@ TEST(UserDefinedRateConstant, ConstructorWithRate) .variable_names_ = {"user"}, .custom_rate_parameter_labels_ = { "my rate", }, }; - + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.1; @@ -48,7 +48,7 @@ TEST(UserDefinedRateConstant, ConstructorWithRateAndName) .variable_names_ = {"user"}, .custom_rate_parameter_labels_ = { "my rate", }, }; - + micm::State state{ state_parameters_ }; state.custom_rate_parameters_[0][0] = 1.1; std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); diff --git a/test/unit/solver/test_linear_solver_policy.hpp b/test/unit/solver/test_linear_solver_policy.hpp index 8bdeca9b0..b05e65c68 100644 --- a/test/unit/solver/test_linear_solver_policy.hpp +++ b/test/unit/solver/test_linear_solver_policy.hpp @@ -1,8 +1,8 @@ #include #include -#include #include +#include template class MatrixPolicy, template class SparseMatrixPolicy> void check_results( From 644a5182680b232f2d89754c090de691f7535ee4 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 27 Oct 2023 16:18:54 -0700 Subject: [PATCH 207/318] use JitProcessSet with JitRosenbrockSolver --- include/micm/process/jit_process_set.hpp | 2 + include/micm/solver/jit_rosenbrock.hpp | 15 +++-- include/micm/solver/rosenbrock.hpp | 9 ++- include/micm/solver/rosenbrock.inl | 66 ++++++++++--------- .../integration/analytical_jit_rosenbrock.cpp | 3 +- test/integration/jit_terminator.cpp | 9 ++- .../regression/RosenbrockChapman/jit_util.hpp | 30 ++++----- .../regression_test_jit_dforce_dy.cpp | 3 +- .../regression_test_jit_p_force.cpp | 6 +- .../regression_test_jit_solve.cpp | 15 +++-- test/unit/solver/test_jit_rosenbrock.cpp | 13 ++-- 11 files changed, 100 insertions(+), 71 deletions(-) diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 207dbc5d3..fdb21e226 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -30,6 +30,8 @@ namespace micm JitProcessSet(JitProcessSet &&); JitProcessSet &operator=(JitProcessSet &&); + JitProcessSet() = default; + /// @brief Create a JITed process set calculator for a given set of processes /// @param compiler JIT compiler /// @param processes Processes to create calculator for diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index bc35cf3a0..a8a307f05 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -31,8 +32,9 @@ namespace micm template< template class MatrixPolicy = VectorMatrix, template class SparseMatrixPolicy = VectorSparseMatrix, - class LinearSolverPolicy = JitLinearSolver> - class JitRosenbrockSolver : public RosenbrockSolver + class LinearSolverPolicy = JitLinearSolver, + class ProcessSetPolicy = JitProcessSet> + class JitRosenbrockSolver : public RosenbrockSolver { std::shared_ptr compiler_; llvm::orc::ResourceTrackerSP function_resource_tracker_; @@ -43,7 +45,7 @@ namespace micm JitRosenbrockSolver(const JitRosenbrockSolver&) = delete; JitRosenbrockSolver& operator=(const JitRosenbrockSolver&) = delete; JitRosenbrockSolver(JitRosenbrockSolver&& other) - : RosenbrockSolver(std::move(other)), + : RosenbrockSolver(std::move(other)), compiler_(std::move(other.compiler_)), function_resource_tracker_(std::move(other.function_resource_tracker_)), alpha_minus_jacobian_(std::move(other.alpha_minus_jacobian_)) @@ -53,7 +55,7 @@ namespace micm JitRosenbrockSolver& operator=(JitRosenbrockSolver&& other) { - RosenbrockSolver::operator=(std::move(other)); + RosenbrockSolver::operator=(std::move(other)); compiler_ = std::move(other.compiler_); function_resource_tracker_ = std::move(other.function_resource_tracker_); alpha_minus_jacobian_ = std::move(other.alpha_minus_jacobian_); @@ -69,12 +71,15 @@ namespace micm const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters) - : RosenbrockSolver( + : RosenbrockSolver( system, processes, parameters, [&](const SparseMatrixPolicy& matrix, double initial_value) -> LinearSolverPolicy { return LinearSolverPolicy{ compiler, matrix, initial_value }; + }, + [&](const std::vector& processes, const std::map& variable_map) -> ProcessSetPolicy { + return ProcessSetPolicy{ compiler, processes, variable_map }; }), compiler_(compiler) { diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 4c8b1037b..a945a0774 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -97,7 +97,8 @@ namespace micm template< template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = StandardSparseMatrix, - class LinearSolverPolicy = LinearSolver> + class LinearSolverPolicy = LinearSolver, + class ProcessSetPolicy = ProcessSet> class RosenbrockSolver { public: @@ -116,7 +117,7 @@ namespace micm std::vector processes_; RosenbrockSolverParameters parameters_; StateParameters state_parameters_; - ProcessSet process_set_; + ProcessSetPolicy process_set_; LinearSolverPolicy linear_solver_; static constexpr double delta_min_ = 1.0e-6; @@ -139,11 +140,13 @@ namespace micm /// @param processes The collection of chemical processes that will be applied during solving /// @param parameters Rosenbrock algorithm parameters /// @param create_linear_solver Function that will be used to create a linear solver instance + /// @param create_process_set Function that will be used to create a process set instance RosenbrockSolver( const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters, - const std::function, double)> create_linear_solver); + const std::function, double)> create_linear_solver, + const std::function&, const std::map&)> create_process_set); virtual ~RosenbrockSolver() = default; diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index ed9cb9342..fc4390e3a 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -52,8 +52,8 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline RosenbrockSolver::RosenbrockSolver() + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline RosenbrockSolver::RosenbrockSolver() : processes_(), parameters_(RosenbrockSolverParameters::three_stage_rosenbrock_parameters()), state_parameters_(), @@ -62,8 +62,8 @@ namespace micm { } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline RosenbrockSolver::RosenbrockSolver( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline RosenbrockSolver::RosenbrockSolver( const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters) @@ -73,16 +73,20 @@ namespace micm parameters, [](const SparseMatrixPolicy& matrix, double initial_value) -> LinearSolverPolicy { return LinearSolverPolicy{ matrix, initial_value }; + }, + [](const std::vector& processes, const std::map& variable_map) -> ProcessSetPolicy { + return ProcessSetPolicy{ processes, variable_map }; }) { } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline RosenbrockSolver::RosenbrockSolver( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline RosenbrockSolver::RosenbrockSolver( const System& system, const std::vector& processes, const RosenbrockSolverParameters& parameters, - const std::function, double)> create_linear_solver) + const std::function, double)> create_linear_solver, + const std::function&, const std::map&)> create_process_set) : processes_(processes), parameters_(parameters), state_parameters_(), @@ -100,7 +104,7 @@ namespace micm if (parameters_.reorder_state_) { // get unsorted Jacobian non-zero elements - auto unsorted_process_set = ProcessSet(processes, variable_map); + auto unsorted_process_set = std::move(create_process_set(processes, variable_map)); auto unsorted_jac_elements = unsorted_process_set.NonZeroJacobianElements(); MatrixPolicy unsorted_jac_non_zeros(system.StateSize(), system.StateSize(), 0); for (auto& elem : unsorted_jac_elements) @@ -122,7 +126,7 @@ namespace micm for (auto& label : process.rate_constant_->CustomParameters()) param_labels.push_back(label); - process_set_ = ProcessSet(processes, variable_map); + process_set_ = std::move(create_process_set(processes, variable_map)); auto jacobian = build_jacobian( process_set_.NonZeroJacobianElements(), @@ -147,20 +151,20 @@ namespace micm linear_solver_ = std::move(create_linear_solver(jacobian, 1.0e-30)); } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline State RosenbrockSolver::GetState() const + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline State RosenbrockSolver::GetState() const { return State{ state_parameters_ }; } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> template - inline typename RosenbrockSolver::SolverResult - RosenbrockSolver::Solve( + inline typename RosenbrockSolver::SolverResult + RosenbrockSolver::Solve( double time_step, State& state) noexcept { - typename RosenbrockSolver::SolverResult result{}; + typename RosenbrockSolver::SolverResult result{}; result.state_ = SolverState::Running; MatrixPolicy Y(state.variables_); MatrixPolicy Ynew(Y.size(), Y[0].size(), 0.0); @@ -335,18 +339,18 @@ namespace micm return result; } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::CalculateForcing( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline void RosenbrockSolver::CalculateForcing( const MatrixPolicy& rate_constants, const MatrixPolicy& number_densities, MatrixPolicy& forcing) { std::fill(forcing.AsVector().begin(), forcing.AsVector().end(), 0.0); - process_set_.AddForcingTerms(rate_constants, number_densities, forcing); + process_set_.template AddForcingTerms(rate_constants, number_densities, forcing); } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::AlphaMinusJacobian( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline void RosenbrockSolver::AlphaMinusJacobian( SparseMatrixPolicy& jacobian, const double& alpha) const requires(!VectorizableSparse>) @@ -361,8 +365,8 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::AlphaMinusJacobian( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline void RosenbrockSolver::AlphaMinusJacobian( SparseMatrixPolicy& jacobian, const double& alpha) const requires(VectorizableSparse>) @@ -379,24 +383,24 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::CalculateJacobian( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline void RosenbrockSolver::CalculateJacobian( const MatrixPolicy& rate_constants, const MatrixPolicy& number_densities, SparseMatrixPolicy& jacobian) { std::fill(jacobian.AsVector().begin(), jacobian.AsVector().end(), 0.0); - process_set_.AddJacobianTerms(rate_constants, number_densities, jacobian); + process_set_.template AddJacobianTerms(rate_constants, number_densities, jacobian); } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::UpdateState(State& state) + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline void RosenbrockSolver::UpdateState(State& state) { Process::UpdateState(processes_, state); } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline void RosenbrockSolver::LinearFactor( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline void RosenbrockSolver::LinearFactor( double& H, const double gamma, bool& singular, @@ -429,8 +433,8 @@ namespace micm } } - template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> - inline double RosenbrockSolver::NormalizedError( + template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> + inline double RosenbrockSolver::NormalizedError( const MatrixPolicy& Y, const MatrixPolicy& Ynew, const MatrixPolicy& errors) diff --git a/test/integration/analytical_jit_rosenbrock.cpp b/test/integration/analytical_jit_rosenbrock.cpp index 547d74ec6..af14b137e 100644 --- a/test/integration/analytical_jit_rosenbrock.cpp +++ b/test/integration/analytical_jit_rosenbrock.cpp @@ -13,7 +13,8 @@ using DefaultSparseVectorMatrix = micm::SparseMatrix>>; + micm::JitLinearSolver<1, DefaultSparseVectorMatrix, micm::JitLuDecomposition<1>>, + micm::JitProcessSet<1>>; TEST(AnalyticalExamplesJitRosenbrock, Troe) { diff --git a/test/integration/jit_terminator.cpp b/test/integration/jit_terminator.cpp index 1499054fc..6032bc5b9 100644 --- a/test/integration/jit_terminator.cpp +++ b/test/integration/jit_terminator.cpp @@ -17,12 +17,14 @@ void RunTerminatorTest() micm::JitRosenbrockSolver< MatrixPolicy, SparseMatrixPolicy, - micm::JitLinearSolver>>( + micm::JitLinearSolver, + micm::JitProcessSet>>( [&](const micm::System& s, const std::vector& p) -> micm::JitRosenbrockSolver< MatrixPolicy, SparseMatrixPolicy, - micm::JitLinearSolver> + micm::JitLinearSolver, + micm::JitProcessSet> { auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) @@ -37,7 +39,8 @@ void RunTerminatorTest() return micm::JitRosenbrockSolver< MatrixPolicy, SparseMatrixPolicy, - micm::JitLinearSolver>{ jit.get(), s, p, solver_params }; + micm::JitLinearSolver, + micm::JitProcessSet>{ jit.get(), s, p, solver_params }; }, number_of_grid_cells); } diff --git a/test/regression/RosenbrockChapman/jit_util.hpp b/test/regression/RosenbrockChapman/jit_util.hpp index 19c2d27c2..b1ed1770f 100644 --- a/test/regression/RosenbrockChapman/jit_util.hpp +++ b/test/regression/RosenbrockChapman/jit_util.hpp @@ -2,8 +2,8 @@ #include "util.hpp" -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -micm::JitRosenbrockSolver getTwoStageMultiCellJitChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> +micm::JitRosenbrockSolver getTwoStageMultiCellJitChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); @@ -15,15 +15,15 @@ micm::JitRosenbrockSolver llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - return micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -micm::JitRosenbrockSolver getThreeStageMultiCellJitChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> +micm::JitRosenbrockSolver getThreeStageMultiCellJitChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); @@ -35,15 +35,15 @@ micm::JitRosenbrockSolver llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - return micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -micm::JitRosenbrockSolver getFourStageMultiCellJitChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> +micm::JitRosenbrockSolver getFourStageMultiCellJitChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); @@ -55,15 +55,15 @@ micm::JitRosenbrockSolver llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - return micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -micm::JitRosenbrockSolver getFourStageDAMultiCellJitChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> +micm::JitRosenbrockSolver getFourStageDAMultiCellJitChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); @@ -75,15 +75,15 @@ micm::JitRosenbrockSolver llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - return micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> -micm::JitRosenbrockSolver getSixStageDAMultiCellJitChapmanSolver( +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> +micm::JitRosenbrockSolver getSixStageDAMultiCellJitChapmanSolver( const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); @@ -95,7 +95,7 @@ micm::JitRosenbrockSolver llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - return micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), diff --git a/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp b/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp index 05f4ed3a1..4a9b4af36 100644 --- a/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp +++ b/test/regression/RosenbrockChapman/regression_test_jit_dforce_dy.cpp @@ -11,6 +11,7 @@ TEST(RegressionJitRosenbrock, VectorJacobian) auto solver = getThreeStageMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testJacobian<>(solver); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp b/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp index c0c6aada4..e9215c143 100644 --- a/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp +++ b/test/regression/RosenbrockChapman/regression_test_jit_p_force.cpp @@ -11,7 +11,8 @@ TEST(RegressionJitRosenbrock, VectorRateConstants) auto solver = getThreeStageMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testRateConstants<>(solver); } @@ -20,6 +21,7 @@ TEST(RegressionJitRosenbrock, VectorForcing) auto solver = getThreeStageMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testForcing(solver); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp b/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp index b8a488113..5d3f8ee07 100644 --- a/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp +++ b/test/regression/RosenbrockChapman/regression_test_jit_solve.cpp @@ -11,7 +11,8 @@ TEST(RegressionJitRosenbrock, TwoStageSolve) auto solver = getTwoStageMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testSolve<>(solver, 1.0e-2); } @@ -20,7 +21,8 @@ TEST(RegressionJitRosenbrock, ThreeStageSolve) auto solver = getThreeStageMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testSolve<>(solver, 1.0e-2); } @@ -29,7 +31,8 @@ TEST(RegressionJitRosenbrock, FourStageSolve) auto solver = getFourStageMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testSolve<>(solver, 1.0e-2); } @@ -38,7 +41,8 @@ TEST(RegressionJitRosenbrock, FourStageDASolve) auto solver = getFourStageDAMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testSolve<>(solver, 1.0e-2); } @@ -47,6 +51,7 @@ TEST(RegressionJitRosenbrock, SixStageDASolve) auto solver = getSixStageDAMultiCellJitChapmanSolver< Group3VectorMatrix, Group3SparseVectorMatrix, - micm::JitLinearSolver<3, Group3SparseVectorMatrix>>(3); + micm::JitLinearSolver<3, Group3SparseVectorMatrix>, + micm::JitProcessSet<3>>(3); testSolve<>(solver, 1.0e-2); } \ No newline at end of file diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index 5515bc87c..b69a0c3b2 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -11,7 +11,7 @@ #include template class MatrixPolicy, template class SparseMatrixPolicy> -micm::JitRosenbrockSolver> +micm::JitRosenbrockSolver, micm::JitProcessSet> getSolver(std::shared_ptr jit) { // ---- foo bar baz quz quuz @@ -45,7 +45,7 @@ getSolver(std::shared_ptr jit) micm::ArrheniusRateConstant({ .A_ = 3.5e-6 })); return micm:: - JitRosenbrockSolver>( + JitRosenbrockSolver, micm::JitProcessSet>( jit, micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, @@ -182,7 +182,8 @@ TEST(JitRosenbrockSolver, MultipleInstances) micm::JitRosenbrockSolver< Group1VectorMatrix, Group1SparseVectorMatrix, - micm::JitLinearSolver<1, Group1SparseVectorMatrix> + micm::JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1> > solver1( jit.get(), chemical_system, @@ -192,7 +193,8 @@ TEST(JitRosenbrockSolver, MultipleInstances) micm::JitRosenbrockSolver< Group1VectorMatrix, Group1SparseVectorMatrix, - micm::JitLinearSolver<1, Group1SparseVectorMatrix> + micm::JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1> > solver2( jit.get(), chemical_system, @@ -202,7 +204,8 @@ TEST(JitRosenbrockSolver, MultipleInstances) micm::JitRosenbrockSolver< Group1VectorMatrix, Group1SparseVectorMatrix, - micm::JitLinearSolver<1, Group1SparseVectorMatrix> + micm::JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1> > solver3( jit.get(), chemical_system, From 73ac44a27731be8c0399bc14468ad1eab415d5eb Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sat, 28 Oct 2023 13:20:03 -0600 Subject: [PATCH 208/318] Split ParseObjectArray into ParseSpeciesArray and ParseMechanismArray. --- include/micm/configure/camp_config.hpp | 76 ++++++++++++++++++-------- 1 file changed, 53 insertions(+), 23 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index 2d05cd1aa..df452f08d 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -147,9 +147,6 @@ namespace micm std::cout << "config_file " << config_file << std::endl; - // The CAMP file list - std::vector camp_files; - // Load the CAMP file list JSON json camp_data = json::parse(std::ifstream(config_file)); if (!camp_data.contains(CAMP_FILES)) @@ -160,6 +157,8 @@ namespace micm return status; } + // Build a list of individual CAMP config files + std::vector camp_files; for (const auto& element : camp_data[CAMP_FILES]) { std::filesystem::path camp_file = config_dir / element.get(); @@ -179,20 +178,41 @@ namespace micm return status; } - // Iterate CAMP file list and form CAMP data object list - std::vector objects; + std::vector species_objects; + std::vector mechanism_objects; + // Iterate CAMP file list and form CAMP data object arrays for (const auto& camp_file : camp_files) { json config_subset = json::parse(std::ifstream(camp_file)); if (config_subset.contains(CAMP_DATA)) { + // Iterate JSON objects from CAMP data entry for (const auto& object : config_subset[CAMP_DATA]) { if (!object.is_null()) { - objects.push_back(object); + // Require object to have a type entry + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } + // Sort into object arrays by type + std::string type = object[TYPE].get(); + // CHEM_SPEC and RELATIVE_TOLERANCE parsed first by ParseSpeciesArray + if ((type == "CHEM_SPEC") || (type == "RELATIVE_TOLERANCE")) + { + species_objects.push_back(object); + } + // All other objects will be parsed by ParseMechanismArray + else + { + mechanism_objects.push_back(object); + } } } } @@ -214,36 +234,26 @@ namespace micm phases_.clear(); processes_.clear(); - // Parse data object list - status = ParseObjectArray(objects); + // Parse species object array + status = ParseSpeciesArray(species_objects); // Assign the parsed 'Species' to 'Phase' gas_phase_ = Phase(species_arr_); - for (auto& p : processes_) - { - for (const auto& s : species_arr_) - { - p.phase_.species_.push_back(s); - } - } + // Parse mechanism object array + status = ParseMechanismArray(mechanism_objects); return status; } private: - ConfigParseStatus ParseObjectArray(const std::vector& objects) + ConfigParseStatus ParseSpeciesArray(const std::vector& objects) { ConfigParseStatus status = ConfigParseStatus::None; for (const auto& object : objects) { - if (!ValidateJsonWithKey(object, TYPE)) - { - status = ConfigParseStatus::ObjectTypeNotFound; - break; - } std::string type = object[TYPE].get(); // debug statements @@ -258,7 +268,27 @@ namespace micm { status = ParseRelativeTolerance(object); } - else if (type == "MECHANISM") + + if (status != ConfigParseStatus::Success) + break; + } + + return status; + } + + ConfigParseStatus ParseMechanismArray(const std::vector& objects) + { + ConfigParseStatus status = ConfigParseStatus::None; + + for (const auto& object : objects) + { + std::string type = object[TYPE].get(); + + // debug statements + // std::cout << type << std::endl; + // std::cout << object.dump(4) << std::endl; + + if (type == "MECHANISM") { status = ParseMechanism(object); } @@ -368,7 +398,7 @@ namespace micm objects.push_back(element); } - return ParseObjectArray(objects); + return ParseMechanismArray(objects); } std::pair> ParseReactants(const json& object) From cc6ba124609973c1d79da2a948d2e4c783d3cc27 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sat, 28 Oct 2023 13:30:09 -0600 Subject: [PATCH 209/318] Commented some debug statements. --- include/micm/configure/camp_config.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp index df452f08d..e6dfcb538 100644 --- a/include/micm/configure/camp_config.hpp +++ b/include/micm/configure/camp_config.hpp @@ -145,7 +145,7 @@ namespace micm config_file = config_path; } - std::cout << "config_file " << config_file << std::endl; + // std::cout << "config_file " << config_file << std::endl; // Load the CAMP file list JSON json camp_data = json::parse(std::ifstream(config_file)); @@ -258,7 +258,7 @@ namespace micm // debug statements // std::cout << type << std::endl; - // std::cout << object.dump(4) << std::endl; + // std::cout << "ParseSpeciesArray object " << object.dump(4) << std::endl; if (type == "CHEM_SPEC") { @@ -286,7 +286,7 @@ namespace micm // debug statements // std::cout << type << std::endl; - // std::cout << object.dump(4) << std::endl; + // std::cout << "ParseMechanismArray object " << object.dump(4) << std::endl; if (type == "MECHANISM") { From 0a70ed69c39d27f8c8f030064dd1109897b81c23 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sat, 28 Oct 2023 13:43:36 -0600 Subject: [PATCH 210/318] Moved camp_config.hpp to solver_config.hpp. --- include/micm/configure/camp_config.hpp | 1034 ------------ include/micm/configure/solver_config.hpp | 1403 +++++++++-------- test/unit/configure/CMakeLists.txt | 1 - test/unit/configure/process/CMakeLists.txt | 9 - .../process/test_arrhenius_camp_config.cpp | 113 -- .../process/test_branched_camp_config.cpp | 131 -- .../process/test_emission_camp_config.cpp | 58 - .../test_first_order_loss_camp_config.cpp | 57 - .../process/test_photolysis_camp_config.cpp | 82 - .../process/test_surface_camp_config.cpp | 82 - ...ernary_chemical_activation_camp_config.cpp | 101 -- .../process/test_troe_camp_config.cpp | 97 -- .../process/test_tunneling_camp_config.cpp | 87 - test/unit/configure/test_camp_config.cpp | 151 -- test/unit/configure/test_solver_config.cpp | 35 +- 15 files changed, 740 insertions(+), 2701 deletions(-) delete mode 100644 include/micm/configure/camp_config.hpp delete mode 100644 test/unit/configure/process/test_arrhenius_camp_config.cpp delete mode 100644 test/unit/configure/process/test_branched_camp_config.cpp delete mode 100644 test/unit/configure/process/test_emission_camp_config.cpp delete mode 100644 test/unit/configure/process/test_first_order_loss_camp_config.cpp delete mode 100644 test/unit/configure/process/test_photolysis_camp_config.cpp delete mode 100644 test/unit/configure/process/test_surface_camp_config.cpp delete mode 100644 test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp delete mode 100644 test/unit/configure/process/test_troe_camp_config.cpp delete mode 100644 test/unit/configure/process/test_tunneling_camp_config.cpp delete mode 100644 test/unit/configure/test_camp_config.cpp diff --git a/include/micm/configure/camp_config.hpp b/include/micm/configure/camp_config.hpp deleted file mode 100644 index e6dfcb538..000000000 --- a/include/micm/configure/camp_config.hpp +++ /dev/null @@ -1,1034 +0,0 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, -// -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace micm -{ - enum class ConfigParseStatus - { - Success, - None, - InvalidKey, - UnknownKey, - InvalidCAMPFilePath, - NoConfigFilesFound, - CAMPFilesSectionNotFound, - CAMPDataSectionNotFound, - InvalidSpecies, - InvalidMechanism, - ObjectTypeNotFound, - RequiredKeyNotFound, - ContainsNonStandardKey, - MutuallyExclusiveOption - }; - - 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"; - } - } - - // Solver parameters - struct SolverParameters - { - System system_; - std::vector processes_; - - SolverParameters(const System& system, std::vector&& processes) - : system_(system), - processes_(std::move(processes)) - { - } - - SolverParameters(System&& system, std::vector&& processes) - : system_(std::move(system)), - processes_(std::move(processes)) - { - } - }; - - class JsonReaderPolicy - { - using json = nlohmann::json; - - public: - std::vector species_arr_; - - std::vector user_defined_rate_arr_; - std::vector arrhenius_rate_arr_; - std::vector troe_rate_arr_; - std::vector ternary_rate_arr_; - std::vector branched_rate_arr_; - std::vector tunneling_rate_arr_; - std::vector surface_rate_arr_; - - // Specific for solver parameters - Phase gas_phase_; - std::unordered_map phases_; - std::vector processes_; - - // Common JSON - static const inline std::string DEFAULT_CONFIG_FILE = "config.json"; - static const inline std::string CAMP_FILES = "camp-files"; - static const inline std::string CAMP_DATA = "camp-data"; - static const inline std::string TYPE = "type"; - - // Functions - - /// @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) - { - // Parse status - ConfigParseStatus status; - - // Look for CAMP config path - if (!std::filesystem::exists(config_path)) - { - status = ConfigParseStatus::InvalidCAMPFilePath; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } - - std::filesystem::path config_dir; - std::filesystem::path config_file; - - if (std::filesystem::is_directory(config_path)) - { - // If config path is a directory, use default config file name - config_dir = config_path; - config_file = config_dir / DEFAULT_CONFIG_FILE; - } - else - { - // Extract configuration dir from configuration file path - config_dir = config_path.parent_path(); - config_file = config_path; - } - - // std::cout << "config_file " << config_file << std::endl; - - // Load the CAMP file list JSON - json camp_data = json::parse(std::ifstream(config_file)); - if (!camp_data.contains(CAMP_FILES)) - { - status = ConfigParseStatus::CAMPFilesSectionNotFound; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } - - // Build a list of individual CAMP config files - std::vector camp_files; - for (const auto& element : camp_data[CAMP_FILES]) - { - std::filesystem::path camp_file = config_dir / element.get(); - if (std::filesystem::exists(camp_file)) - { - camp_files.push_back(camp_file); - } - // Error return here if CAMP files list has a missing file? - } - - // No config files found - if (camp_files.size() < 1) - { - status = ConfigParseStatus::NoConfigFilesFound; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } - - std::vector species_objects; - std::vector mechanism_objects; - - // Iterate CAMP file list and form CAMP data object arrays - for (const auto& camp_file : camp_files) - { - json config_subset = json::parse(std::ifstream(camp_file)); - - if (config_subset.contains(CAMP_DATA)) - { - // Iterate JSON objects from CAMP data entry - for (const auto& object : config_subset[CAMP_DATA]) - { - if (!object.is_null()) - { - // Require object to have a type entry - if (!ValidateJsonWithKey(object, TYPE)) - { - status = ConfigParseStatus::ObjectTypeNotFound; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } - // Sort into object arrays by type - std::string type = object[TYPE].get(); - // CHEM_SPEC and RELATIVE_TOLERANCE parsed first by ParseSpeciesArray - if ((type == "CHEM_SPEC") || (type == "RELATIVE_TOLERANCE")) - { - species_objects.push_back(object); - } - // All other objects will be parsed by ParseMechanismArray - else - { - mechanism_objects.push_back(object); - } - } - } - } - else - { - return ConfigParseStatus::CAMPDataSectionNotFound; - } - } - - // Clear vectors and maps - species_arr_.clear(); - user_defined_rate_arr_.clear(); - arrhenius_rate_arr_.clear(); - troe_rate_arr_.clear(); - ternary_rate_arr_.clear(); - branched_rate_arr_.clear(); - tunneling_rate_arr_.clear(); - surface_rate_arr_.clear(); - phases_.clear(); - processes_.clear(); - - // Parse species object array - status = ParseSpeciesArray(species_objects); - - // Assign the parsed 'Species' to 'Phase' - gas_phase_ = Phase(species_arr_); - - // Parse mechanism object array - status = ParseMechanismArray(mechanism_objects); - - return status; - } - - private: - - ConfigParseStatus ParseSpeciesArray(const std::vector& objects) - { - ConfigParseStatus status = ConfigParseStatus::None; - - for (const auto& object : objects) - { - std::string type = object[TYPE].get(); - - // debug statements - // std::cout << type << std::endl; - // std::cout << "ParseSpeciesArray object " << object.dump(4) << std::endl; - - if (type == "CHEM_SPEC") - { - status = ParseChemicalSpecies(object); - } - else if (type == "RELATIVE_TOLERANCE") - { - status = ParseRelativeTolerance(object); - } - - if (status != ConfigParseStatus::Success) - break; - } - - return status; - } - - ConfigParseStatus ParseMechanismArray(const std::vector& objects) - { - ConfigParseStatus status = ConfigParseStatus::None; - - for (const auto& object : objects) - { - std::string type = object[TYPE].get(); - - // debug statements - // std::cout << type << std::endl; - // std::cout << "ParseMechanismArray object " << object.dump(4) << std::endl; - - if (type == "MECHANISM") - { - status = ParseMechanism(object); - } - else if (type == "PHOTOLYSIS") - { - status = ParsePhotolysis(object); - } - else if (type == "EMISSION") - { - status = ParseEmission(object); - } - else if (type == "FIRST_ORDER_LOSS") - { - status = ParseFirstOrderLoss(object); - } - else if (type == "ARRHENIUS") - { - status = ParseArrhenius(object); - } - else if (type == "TROE") - { - status = ParseTroe(object); - } - else if (type == "TERNARY_CHEMICAL_ACTIVATION") - { - status = ParseTernaryChemicalActivation(object); - } - else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") - { - status = ParseBranched(object); - } - else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") - { - status = ParseTunneling(object); - } - else if (type == "SURFACE") - { - status = ParseSurface(object); - } - else - { - status = ConfigParseStatus::UnknownKey; - } - - if (status != ConfigParseStatus::Success) - break; - } - - return status; - } - - bool ValidateJsonWithKey(const json& object, const std::string& key) - { - if (!object.contains(key)) - { - std::string msg = "Key " + key + " was not found in the config file"; - std::cerr << msg << std::endl; - return false; - } - return true; - } - - ConfigParseStatus ParseChemicalSpecies(const json& object) - { - // required keys - const std::string NAME = "name"; - - auto status = ValidateSchema( - object, - { NAME, "type" }, - { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); - if (status != ConfigParseStatus::Success) - { - return status; - } - - std::string name = object[NAME].get(); - - // Load remaining keys as properties - std::map properties{}; - for (auto& [key, value] : object.items()) - { - if (value.is_number_float()) - properties[key] = value; - } - species_arr_.push_back(Species(name, properties)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseRelativeTolerance(const json& object) - { - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseMechanism(const json& object) - { - auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } - - std::vector objects; - for (const auto& element : object["reactions"]) - { - objects.push_back(element); - } - - return ParseMechanismArray(objects); - } - - std::pair> ParseReactants(const json& object) - { - const std::string QTY = "qty"; - std::vector reactants; - - ConfigParseStatus status = ConfigParseStatus::Success; - - for (auto& [key, value] : object.items()) - { - std::size_t qty = 1; - auto new_status = ValidateSchema(value, {}, { "qty" }); - if (new_status != ConfigParseStatus::Success) - { - status = new_status; - } - if (value.contains(QTY)) - qty = value[QTY]; - for (std::size_t i = 0; i < qty; ++i) - reactants.push_back(Species(key)); - } - return std::make_pair(status, reactants); - } - - std::pair>> ParseProducts(const json& object) - { - const std::string YIELD = "yield"; - - ConfigParseStatus status = ConfigParseStatus::Success; - - constexpr double DEFAULT_YIELD = 1.0; - std::vector> products; - for (auto& [key, value] : object.items()) - { - auto new_status = ValidateSchema(value, {}, { "yield" }); - if (new_status != ConfigParseStatus::Success) - { - status = new_status; - } - if (value.contains(YIELD)) - { - products.push_back(std::make_pair(Species(key), value[YIELD])); - } - else - { - products.push_back(std::make_pair(Species(key), DEFAULT_YIELD)); - } - } - return std::make_pair(status, products); - } - - ConfigParseStatus ParsePhotolysis(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - const std::string MUSICA_NAME = "MUSICA name"; - - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - std::string name = "PHOTO." + object[MUSICA_NAME].get(); - - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseEmission(const json& object) - { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; - - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } - - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - products_object[species] = { { "yield", 1.0 } }; - - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - std::string name = "EMIS." + object[MUSICA_NAME].get(); - - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseFirstOrderLoss(const json& object) - { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; - - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } - - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - reactants_object[species] = { {} }; - - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - std::string name = "LOSS." + object[MUSICA_NAME].get(); - - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseArrhenius(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - auto status = - ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); - if (status != ConfigParseStatus::Success) - { - return status; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - ArrheniusRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } - if (object.contains("D")) - { - parameters.D_ = object["D"].get(); - } - if (object.contains("E")) - { - parameters.E_ = object["E"].get(); - } - if (object.contains("Ea")) - { - if (parameters.C_ != 0) - { - std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl; - return ConfigParseStatus::MutuallyExclusiveOption; - } - // Calculate 'C' using 'Ea' - parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; - } - - arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTroe(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - auto status = ValidateSchema( - object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); - if (status != ConfigParseStatus::Success) - { - return status; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - TroeRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } - - troe_rate_arr_.push_back(TroeRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTernaryChemicalActivation(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - auto status = ValidateSchema( - object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); - if (status != ConfigParseStatus::Success) - { - return status; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - TernaryChemicalActivationRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } - - ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); - - std::unique_ptr rate_ptr = - std::make_unique(parameters); - - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseBranched(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string ALKOXY_PRODUCTS = "alkoxy products"; - const std::string NITRATE_PRODUCTS = "nitrate products"; - const std::string X = "X"; - const std::string Y = "Y"; - const std::string A0 = "a0"; - const std::string N = "n"; - - auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); - auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (alkoxy_products.first != ConfigParseStatus::Success) - { - return alkoxy_products.first; - } - - if (nitrate_products.first != ConfigParseStatus::Success) - { - return nitrate_products.first; - } - - BranchedRateConstantParameters parameters; - parameters.X_ = object[X].get(); - parameters.Y_ = object[Y].get(); - parameters.a0_ = object[A0].get(); - parameters.n_ = object[N].get(); - - // Alkoxy branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); - - // Nitrate branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTunneling(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); - if (status != ConfigParseStatus::Success) - { - return status; - } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - TunnelingRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } - - tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseSurface(const json& object) - { - const std::string REACTANTS = "gas-phase reactant"; - const std::string PRODUCTS = "gas-phase products"; - const std::string MUSICA_NAME = "MUSICA name"; - const std::string PROBABILITY = "reaction probability"; - - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); - if (status != ConfigParseStatus::Success) - { - return status; - } - - std::string species_name = object[REACTANTS].get(); - json reactants_object{}; - reactants_object[species_name] = { {} }; - - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } - - Species reactant_species = Species(""); - for (auto& species : species_arr_) - { - if (species.name_ == species_name) - { - reactant_species = species; - break; - } - } - SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), - .species_ = reactant_species }; - - if (object.contains(PROBABILITY)) - { - parameters.reaction_probability_ = object[PROBABILITY].get(); - } - - surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - /// @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())) - { - 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 << std::endl; - return ConfigParseStatus::ContainsNonStandardKey; - } - } - return ConfigParseStatus::Success; - } - }; - - /// @brief Public interface to read and parse config - template - class SolverConfig : public ConfigTypePolicy - { - private: - ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; - - 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]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) - { - last_parse_status_ = this->Parse(config_dir); - return last_parse_status_; - } - - /// @brief Creates and returns SolverParameters - /// @return SolverParameters that contains 'System' and a collection of 'Process' - SolverParameters GetSolverParams() - { - if (last_parse_status_ != ConfigParseStatus::Success) - { - std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_); - throw std::runtime_error(msg); - } - - return SolverParameters( - std::move(System(std::move(this->gas_phase_), std::move(this->phases_))), std::move(this->processes_)); - } - }; -} // namespace micm diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 4bb06d3f3..e6dfcb538 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -8,20 +8,20 @@ #include #include #include -#include -#include +#include #include -#include -#include -#include -#include -#include +#include #include #include -#include #include -#include -#include + +#include +#include +#include +#include +#include +#include +#include namespace micm { @@ -29,14 +29,13 @@ namespace micm { Success, None, - InvalidSpeciesFilePath, - InvalidReactionsFilePath, InvalidKey, UnknownKey, - InvalidSpecies, + InvalidCAMPFilePath, + NoConfigFilesFound, CAMPFilesSectionNotFound, - InvalidCAMPFileCount, CAMPDataSectionNotFound, + InvalidSpecies, InvalidMechanism, ObjectTypeNotFound, RequiredKeyNotFound, @@ -50,14 +49,13 @@ namespace micm { case ConfigParseStatus::Success: return "Success"; case ConfigParseStatus::None: return "None"; - case ConfigParseStatus::InvalidSpeciesFilePath: return "InvalidSpeciesFilePath"; - case ConfigParseStatus::InvalidReactionsFilePath: return "InvalidReactionsFilePath"; case ConfigParseStatus::InvalidKey: return "InvalidKey"; case ConfigParseStatus::UnknownKey: return "UnknownKey"; - case ConfigParseStatus::InvalidSpecies: return "InvalidSpecies"; + case ConfigParseStatus::InvalidCAMPFilePath: return "InvalidCAMPFilePath"; + case ConfigParseStatus::NoConfigFilesFound: return "NoConfigFilesFound"; case ConfigParseStatus::CAMPFilesSectionNotFound: return "CAMPFilesSectionNotFound"; - case ConfigParseStatus::InvalidCAMPFileCount: return "InvalidCAMPFileCount"; 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"; @@ -86,808 +84,849 @@ namespace micm } }; - // JSON Configure paser class JsonReaderPolicy { using json = nlohmann::json; - public: - // Read from species configure - std::vector species_arr_; - - // Read from reaction configure - std::vector user_defined_rate_arr_; - std::vector arrhenius_rate_arr_; - std::vector branched_rate_arr_; - std::vector surface_rate_arr_; - std::vector troe_rate_arr_; - std::vector ternary_rate_arr_; - std::vector tunneling_rate_arr_; - - // Specific for solver parameters - Phase gas_phase_; - std::unordered_map phases_; - std::vector processes_; + public: + std::vector species_arr_; - // Constants - // Configure files - static const inline std::string CAMP_CONFIG = "config.json"; - static const inline std::string SPECIES_CONFIG = "species.json"; - static const inline std::string MECHANISM_CONFIG = "mechanism.json"; - static const inline std::string REACTIONS_CONFIG = "reactions.json"; - static const inline std::string TOLERANCE_CONFIG = "tolerance.json"; + std::vector user_defined_rate_arr_; + std::vector arrhenius_rate_arr_; + std::vector troe_rate_arr_; + std::vector ternary_rate_arr_; + std::vector branched_rate_arr_; + std::vector tunneling_rate_arr_; + std::vector surface_rate_arr_; - // Common JSON - static const inline std::string CAMP_DATA = "camp-data"; - static const inline std::string CAMP_FILES = "camp-files"; - static const inline std::string TYPE = "type"; + // Specific for solver parameters + Phase gas_phase_; + std::unordered_map phases_; + std::vector processes_; - // Functions + // Common JSON + static const inline std::string DEFAULT_CONFIG_FILE = "config.json"; + static const inline std::string CAMP_FILES = "camp-files"; + static const inline std::string CAMP_DATA = "camp-data"; + static const inline std::string TYPE = "type"; - /// @brief Parse configures - /// @return True for successful parsing - ConfigParseStatus Parse(const std::filesystem::path& config_dir) - { - // Create configure paths - std::filesystem::path species_config(config_dir / SPECIES_CONFIG); - std::filesystem::path mechanism_config(config_dir / MECHANISM_CONFIG); - std::filesystem::path reactions_config(config_dir / REACTIONS_CONFIG); - // Note tolerance_config is defined here but not used - std::filesystem::path tolerance_config(config_dir / TOLERANCE_CONFIG); - - // Look for CAMP config file - std::filesystem::path camp_config(config_dir / CAMP_CONFIG); - if (std::filesystem::exists(camp_config)) + // Functions + + /// @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) { - json camp_data = json::parse(std::ifstream(camp_config)); + // Parse status + ConfigParseStatus status; + + // Look for CAMP config path + if (!std::filesystem::exists(config_path)) + { + status = ConfigParseStatus::InvalidCAMPFilePath; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } + + std::filesystem::path config_dir; + std::filesystem::path config_file; + + if (std::filesystem::is_directory(config_path)) + { + // If config path is a directory, use default config file name + config_dir = config_path; + config_file = config_dir / DEFAULT_CONFIG_FILE; + } + else + { + // Extract configuration dir from configuration file path + config_dir = config_path.parent_path(); + config_file = config_path; + } + + // std::cout << "config_file " << config_file << std::endl; + + // Load the CAMP file list JSON + json camp_data = json::parse(std::ifstream(config_file)); if (!camp_data.contains(CAMP_FILES)) - return ConfigParseStatus::CAMPFilesSectionNotFound; + { + status = ConfigParseStatus::CAMPFilesSectionNotFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } - std::vector camp_files; + // Build a list of individual CAMP config files + std::vector camp_files; for (const auto& element : camp_data[CAMP_FILES]) { - camp_files.push_back(element.get()); + std::filesystem::path camp_file = config_dir / element.get(); + if (std::filesystem::exists(camp_file)) + { + camp_files.push_back(camp_file); + } + // Error return here if CAMP files list has a missing file? } - if (camp_files.size() < 2) + + // No config files found + if (camp_files.size() < 1) { - std::string err_msg = "CAMP file list should contain at least two files [species.json, mechanism.json]"; - std::cerr << err_msg << std::endl; - return ConfigParseStatus::InvalidCAMPFileCount; + status = ConfigParseStatus::NoConfigFilesFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; } - // As a temporary implementation, assume camp files are ordered - species_config = config_dir / camp_files[0]; - mechanism_config = config_dir / camp_files[1]; - } - // Current reaction configs should be either mechanism_config or reactions_config - std::filesystem::path cur_reactions_config; + std::vector species_objects; + std::vector mechanism_objects; - // Check if species config exists - if (!std::filesystem::exists(species_config)) - { - std::string err_msg = "Species configuration file at path " + species_config.string() + " does not exist\n"; - std::cerr << err_msg << std::endl; - return ConfigParseStatus::InvalidSpeciesFilePath; - } + // Iterate CAMP file list and form CAMP data object arrays + for (const auto& camp_file : camp_files) + { + json config_subset = json::parse(std::ifstream(camp_file)); + + if (config_subset.contains(CAMP_DATA)) + { + // Iterate JSON objects from CAMP data entry + for (const auto& object : config_subset[CAMP_DATA]) + { + if (!object.is_null()) + { + // Require object to have a type entry + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } + // Sort into object arrays by type + std::string type = object[TYPE].get(); + // CHEM_SPEC and RELATIVE_TOLERANCE parsed first by ParseSpeciesArray + if ((type == "CHEM_SPEC") || (type == "RELATIVE_TOLERANCE")) + { + species_objects.push_back(object); + } + // All other objects will be parsed by ParseMechanismArray + else + { + mechanism_objects.push_back(object); + } + } + } + } + else + { + return ConfigParseStatus::CAMPDataSectionNotFound; + } + } - // Check if a reaction configure exists and decide which one - if (std::filesystem::exists(mechanism_config)) - { - cur_reactions_config = mechanism_config; - } - else if (std::filesystem::exists(reactions_config)) - { - cur_reactions_config = reactions_config; - } - else - { - std::string err_msg = "Reaction configuration file at path " + mechanism_config.string() + " or " + - reactions_config.string() + " does not exist\n"; - std::cerr << err_msg << std::endl; - return ConfigParseStatus::InvalidReactionsFilePath; - } + // Clear vectors and maps + species_arr_.clear(); + user_defined_rate_arr_.clear(); + arrhenius_rate_arr_.clear(); + troe_rate_arr_.clear(); + ternary_rate_arr_.clear(); + branched_rate_arr_.clear(); + tunneling_rate_arr_.clear(); + surface_rate_arr_.clear(); + phases_.clear(); + processes_.clear(); - // Read species file to create Species and Phase that needs to be known to System and Process + // Parse species object array + status = ParseSpeciesArray(species_objects); - auto species_status = ConfigureSpecies(species_config); - if (species_status != ConfigParseStatus::Success) - return species_status; + // Assign the parsed 'Species' to 'Phase' + gas_phase_ = Phase(species_arr_); - // Assign the parsed 'Species' to 'Phase' - gas_phase_ = Phase(species_arr_); + // Parse mechanism object array + status = ParseMechanismArray(mechanism_objects); - // Read reactions file - json reaction_data = json::parse(std::ifstream(cur_reactions_config)); + return status; + } - if (!reaction_data.contains(CAMP_DATA)) - return ConfigParseStatus::CAMPDataSectionNotFound; + private: - std::vector reaction_objects; - for (const auto& element : reaction_data[CAMP_DATA]) + ConfigParseStatus ParseSpeciesArray(const std::vector& objects) { - reaction_objects.push_back(element); - } + ConfigParseStatus status = ConfigParseStatus::None; - return ParseObjectArray(reaction_objects); - } + for (const auto& object : objects) + { + std::string type = object[TYPE].get(); + + // debug statements + // std::cout << type << std::endl; + // std::cout << "ParseSpeciesArray object " << object.dump(4) << std::endl; + + if (type == "CHEM_SPEC") + { + status = ParseChemicalSpecies(object); + } + else if (type == "RELATIVE_TOLERANCE") + { + status = ParseRelativeTolerance(object); + } + + if (status != ConfigParseStatus::Success) + break; + } - private: - /// @brief Create 'Species' and 'Phase' - /// @param path to 'Species' file - /// @return True at success - ConfigParseStatus ConfigureSpecies(const std::filesystem::path& file) - { - ConfigParseStatus status = ConfigParseStatus::None; - json file_data = json::parse(std::ifstream(file)); + return status; + } + + ConfigParseStatus ParseMechanismArray(const std::vector& objects) + { + ConfigParseStatus status = ConfigParseStatus::None; - if (!file_data.contains(CAMP_DATA)) - return ConfigParseStatus::CAMPDataSectionNotFound; + for (const auto& object : objects) + { + std::string type = object[TYPE].get(); + + // debug statements + // std::cout << type << std::endl; + // std::cout << "ParseMechanismArray object " << object.dump(4) << std::endl; + + if (type == "MECHANISM") + { + status = ParseMechanism(object); + } + else if (type == "PHOTOLYSIS") + { + status = ParsePhotolysis(object); + } + else if (type == "EMISSION") + { + status = ParseEmission(object); + } + else if (type == "FIRST_ORDER_LOSS") + { + status = ParseFirstOrderLoss(object); + } + else if (type == "ARRHENIUS") + { + status = ParseArrhenius(object); + } + else if (type == "TROE") + { + status = ParseTroe(object); + } + else if (type == "TERNARY_CHEMICAL_ACTIVATION") + { + status = ParseTernaryChemicalActivation(object); + } + else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") + { + status = ParseBranched(object); + } + else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") + { + status = ParseTunneling(object); + } + else if (type == "SURFACE") + { + status = ParseSurface(object); + } + else + { + status = ConfigParseStatus::UnknownKey; + } + + if (status != ConfigParseStatus::Success) + break; + } - std::vector objects; - for (const auto& element : file_data[CAMP_DATA]) - objects.push_back(element); + return status; + } - for (const auto& object : objects) + bool ValidateJsonWithKey(const json& object, const std::string& key) { - if (!ValidateJsonWithKey(object, TYPE)) + if (!object.contains(key)) { - status = ConfigParseStatus::ObjectTypeNotFound; - break; + std::string msg = "Key " + key + " was not found in the config file"; + std::cerr << msg << std::endl; + return false; } + return true; + } - std::string type = object[TYPE].get(); + ConfigParseStatus ParseChemicalSpecies(const json& object) + { + // required keys + const std::string NAME = "name"; - if (type == "CHEM_SPEC") + auto status = ValidateSchema( + object, + { NAME, "type" }, + { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); + if (status != ConfigParseStatus::Success) { - status = ParseChemicalSpecies(object); + return status; } - else if (type == "RELATIVE_TOLERANCE") + + std::string name = object[NAME].get(); + + // Load remaining keys as properties + std::map properties{}; + for (auto& [key, value] : object.items()) { - status = ParseRelativeTolerance(object); + if (value.is_number_float()) + properties[key] = value; } + species_arr_.push_back(Species(name, properties)); - if (status != ConfigParseStatus::Success) - break; + return ConfigParseStatus::Success; } - return status; - } - - bool ValidateJsonWithKey(const json& object, const std::string& key) - { - if (!object.contains(key)) + ConfigParseStatus ParseRelativeTolerance(const json& object) { - std::string msg = "Key " + key + " was not found in the config file"; - std::cerr << msg << std::endl; - return false; + return ConfigParseStatus::Success; } - return true; - } - ConfigParseStatus ParseObjectArray(const std::vector& objects) - { - ConfigParseStatus status = ConfigParseStatus::None; - - for (const auto& object : objects) + ConfigParseStatus ParseMechanism(const json& object) { - if (!ValidateJsonWithKey(object, TYPE)) + auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); + if (status != ConfigParseStatus::Success) { - status = ConfigParseStatus::ObjectTypeNotFound; - break; + return status; } - std::string type = object[TYPE].get(); - - if (type == "MECHANISM") - { - status = ParseMechanism(object); - } - else if (type == "PHOTOLYSIS") - { - status = ParsePhotolysis(object); - } - else if (type == "ARRHENIUS") - { - status = ParseArrhenius(object); - } - else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") + std::vector objects; + for (const auto& element : object["reactions"]) { - status = ParseBranched(object); + objects.push_back(element); } - else if (type == "TERNARY_CHEMICAL_ACTIVATION") - { - status = ParseTernaryChemicalActivation(object); - } - else if (type == "TROE") - { - status = ParseTroe(object); - } - else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") - { - status = ParseTunneling(object); - } - else if (type == "EMISSION") - { - status = ParseEmission(object); - } - else if (type == "FIRST_ORDER_LOSS") - { - status = ParseFirstOrderLoss(object); - } - else if (type == "SURFACE") - { - status = ParseSurface(object); - } - else - { - status = ConfigParseStatus::UnknownKey; - } - if (status != ConfigParseStatus::Success) - break; - } - return status; - } - - ConfigParseStatus ParseChemicalSpecies(const json& object) - { - // required keys - const std::string NAME = "name"; + return ParseMechanismArray(objects); + } - auto status = ValidateSchema( - object, - { NAME, "type" }, - { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); - if (status != ConfigParseStatus::Success) + std::pair> ParseReactants(const json& object) { - return status; - } + const std::string QTY = "qty"; + std::vector reactants; - std::string name = object[NAME].get(); + ConfigParseStatus status = ConfigParseStatus::Success; - // Load remaining keys as properties - std::map properties{}; - for (auto& [key, value] : object.items()) - { - if (value.is_number_float()) - properties[key] = value; + for (auto& [key, value] : object.items()) + { + std::size_t qty = 1; + auto new_status = ValidateSchema(value, {}, { "qty" }); + if (new_status != ConfigParseStatus::Success) + { + status = new_status; + } + if (value.contains(QTY)) + qty = value[QTY]; + for (std::size_t i = 0; i < qty; ++i) + reactants.push_back(Species(key)); + } + return std::make_pair(status, reactants); } - species_arr_.push_back(Species(name, properties)); - return ConfigParseStatus::Success; - } + std::pair>> ParseProducts(const json& object) + { + const std::string YIELD = "yield"; - ConfigParseStatus ParseRelativeTolerance(const json& object) - { - return ConfigParseStatus::Success; - } + ConfigParseStatus status = ConfigParseStatus::Success; - ConfigParseStatus ParseMechanism(const json& object) - { - auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); - if (status != ConfigParseStatus::Success) - { - return status; + constexpr double DEFAULT_YIELD = 1.0; + std::vector> products; + for (auto& [key, value] : object.items()) + { + auto new_status = ValidateSchema(value, {}, { "yield" }); + if (new_status != ConfigParseStatus::Success) + { + status = new_status; + } + if (value.contains(YIELD)) + { + products.push_back(std::make_pair(Species(key), value[YIELD])); + } + else + { + products.push_back(std::make_pair(Species(key), DEFAULT_YIELD)); + } + } + return std::make_pair(status, products); } - std::vector objects; - for (const auto& element : object["reactions"]) + ConfigParseStatus ParsePhotolysis(const json& object) { - objects.push_back(element); - } + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + const std::string MUSICA_NAME = "MUSICA name"; - return ParseObjectArray(objects); - } + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - std::pair> ParseReactants(const json& object) - { - const std::string QTY = "qty"; - std::vector reactants; + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - ConfigParseStatus status = ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - for (auto& [key, value] : object.items()) - { - std::size_t qty = 1; - auto new_status = ValidateSchema(value, {}, { "qty" }); - if (new_status != ConfigParseStatus::Success) + if (products.first != ConfigParseStatus::Success) { - status = new_status; + return products.first; } - if (value.contains(QTY)) - qty = value[QTY]; - for (std::size_t i = 0; i < qty; ++i) - reactants.push_back(Species(key)); - } - return std::make_pair(status, reactants); - } - std::pair>> ParseProducts(const json& object) - { - const std::string YIELD = "yield"; + std::string name = "PHOTO." + object[MUSICA_NAME].get(); - ConfigParseStatus status = ConfigParseStatus::Success; + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - constexpr double DEFAULT_YEILD = 1.0; - std::vector> products; - for (auto& [key, value] : object.items()) + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + + ConfigParseStatus ParseEmission(const json& object) { - auto new_status = ValidateSchema(value, {}, { "yield" }); - if (new_status != ConfigParseStatus::Success) + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; + + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) { - status = new_status; + return status; } - if (value.contains(YIELD)) + + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + products_object[species] = { { "yield", 1.0 } }; + + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); + + if (reactants.first != ConfigParseStatus::Success) { - products.push_back(std::make_pair(Species(key), value[YIELD])); + return reactants.first; } - else + + if (products.first != ConfigParseStatus::Success) { - products.push_back(std::make_pair(Species(key), DEFAULT_YEILD)); + return products.first; } - } - return std::make_pair(status, products); - } - ConfigParseStatus ParsePhotolysis(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - const std::string MUSICA_NAME = "MUSICA name"; + std::string name = "EMIS." + object[MUSICA_NAME].get(); - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; + return ConfigParseStatus::Success; } - if (products.first != ConfigParseStatus::Success) + ConfigParseStatus ParseFirstOrderLoss(const json& object) { - return products.first; - } + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; - std::string name = "PHOTO." + object[MUSICA_NAME].get(); + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + reactants_object[species] = { {} }; - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); - return ConfigParseStatus::Success; - } + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - ConfigParseStatus ParseArrhenius(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } - auto status = - ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); - if (status != ConfigParseStatus::Success) - { - return status; - } + std::string name = "LOSS." + object[MUSICA_NAME].get(); - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (products.first != ConfigParseStatus::Success) - { - return products.first; + return ConfigParseStatus::Success; } - ArrheniusRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } - if (object.contains("D")) - { - parameters.D_ = object["D"].get(); - } - if (object.contains("E")) - { - parameters.E_ = object["E"].get(); - } - if (object.contains("Ea")) + ConfigParseStatus ParseArrhenius(const json& object) { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + + auto status = + ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); + if (status != ConfigParseStatus::Success) + { + return status; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + + ArrheniusRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } + if (object.contains("D")) + { + parameters.D_ = object["D"].get(); + } + if (object.contains("E")) + { + parameters.E_ = object["E"].get(); + } + if (object.contains("Ea")) + { if (parameters.C_ != 0) { std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl; return ConfigParseStatus::MutuallyExclusiveOption; } - // Calculate 'C' using 'Ea' - parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; - } - - arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); + // Calculate 'C' using 'Ea' + parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; + } - std::unique_ptr rate_ptr = std::make_unique(parameters); + arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); + + std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - return ConfigParseStatus::Success; - } + return ConfigParseStatus::Success; + } - ConfigParseStatus ParseBranched(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string ALKOXY_PRODUCTS = "alkoxy products"; - const std::string NITRATE_PRODUCTS = "nitrate products"; - const std::string X = "X"; - const std::string Y = "Y"; - const std::string A0 = "a0"; - const std::string N = "n"; - - auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); - if (status != ConfigParseStatus::Success) + ConfigParseStatus ParseTroe(const json& object) { - return status; - } + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - auto reactants = ParseReactants(object[REACTANTS]); - auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); - auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) + { + return status; + } - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - if (alkoxy_products.first != ConfigParseStatus::Success) - { - return alkoxy_products.first; - } - if (nitrate_products.first != ConfigParseStatus::Success) - { - return nitrate_products.first; - } + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - BranchedRateConstantParameters parameters; - parameters.X_ = object[X].get(); - parameters.Y_ = object[Y].get(); - parameters.a0_ = object[A0].get(); - parameters.n_ = object[N].get(); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - // Alkoxy branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } - // Nitrate branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); + TroeRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } - return ConfigParseStatus::Success; - } + troe_rate_arr_.push_back(TroeRateConstant(parameters)); - ConfigParseStatus ParseTroe(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + std::unique_ptr rate_ptr = std::make_unique(parameters); - auto status = ValidateSchema( - object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); - if (status != ConfigParseStatus::Success) - { - return status; - } + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - if (products.first != ConfigParseStatus::Success) - { - return products.first; + return ConfigParseStatus::Success; } - TroeRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) + ConfigParseStatus ParseTernaryChemicalActivation(const json& object) { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } - - troe_rate_arr_.push_back(TroeRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); - - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - - return ConfigParseStatus::Success; - } - - ConfigParseStatus ParseTernaryChemicalActivation(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - auto status = ValidateSchema( + auto status = ValidateSchema( object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); - if (status != ConfigParseStatus::Success) - { - return status; - } + if (status != ConfigParseStatus::Success) + { + return status; + } - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - TernaryChemicalActivationRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } - std::unique_ptr rate_ptr = - std::make_unique(parameters); + TernaryChemicalActivationRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); - return ConfigParseStatus::Success; - } + std::unique_ptr rate_ptr = + std::make_unique(parameters); - ConfigParseStatus ParseTunneling(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); - if (status != ConfigParseStatus::Success) - { - return status; + return ConfigParseStatus::Success; } - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - if (products.first != ConfigParseStatus::Success) + ConfigParseStatus ParseBranched(const json& object) { - return products.first; - } + const std::string REACTANTS = "reactants"; + const std::string ALKOXY_PRODUCTS = "alkoxy products"; + const std::string NITRATE_PRODUCTS = "nitrate products"; + const std::string X = "X"; + const std::string Y = "Y"; + const std::string A0 = "a0"; + const std::string N = "n"; - TunnelingRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } + auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); + auto reactants = ParseReactants(object[REACTANTS]); + auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); + auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); - std::unique_ptr rate_ptr = std::make_unique(parameters); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + if (alkoxy_products.first != ConfigParseStatus::Success) + { + return alkoxy_products.first; + } - return ConfigParseStatus::Success; - } + if (nitrate_products.first != ConfigParseStatus::Success) + { + return nitrate_products.first; + } - ConfigParseStatus ParseEmission(const json& object) - { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; + BranchedRateConstantParameters parameters; + parameters.X_ = object[X].get(); + parameters.Y_ = object[Y].get(); + parameters.a0_ = object[A0].get(); + parameters.n_ = object[N].get(); - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + // Alkoxy branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - products_object[species] = { { "yield", 1.0 } }; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; + // Nitrate branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; } - if (products.first != ConfigParseStatus::Success) + + ConfigParseStatus ParseTunneling(const json& object) { - return products.first; - } + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - std::string name = "EMIS." + object[MUSICA_NAME].get(); + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); + if (status != ConfigParseStatus::Success) + { + return status; + } - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - return ConfigParseStatus::Success; - } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } - ConfigParseStatus ParseFirstOrderLoss(const json& object) - { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; + TunnelingRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - reactants_object[species] = {}; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + std::unique_ptr rate_ptr = std::make_unique(parameters); - std::string name = "LOSS." + object[MUSICA_NAME].get(); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + return ConfigParseStatus::Success; + } - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + ConfigParseStatus ParseSurface(const json& object) + { + const std::string REACTANTS = "gas-phase reactant"; + const std::string PRODUCTS = "gas-phase products"; + const std::string MUSICA_NAME = "MUSICA name"; + const std::string PROBABILITY = "reaction probability"; - return ConfigParseStatus::Success; - } + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); + if (status != ConfigParseStatus::Success) + { + return status; + } - ConfigParseStatus ParseSurface(const json& object) - { - const std::string REACTANTS = "gas-phase reactant"; - const std::string PRODUCTS = "gas-phase products"; - const std::string MUSICA_NAME = "MUSICA name"; - const std::string PROBABILITY = "reaction probability"; + std::string species_name = object[REACTANTS].get(); + json reactants_object{}; + reactants_object[species_name] = { {} }; - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); - if (status != ConfigParseStatus::Success) - { - return status; - } + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(object[PRODUCTS]); - std::string species_name = object[REACTANTS].get(); - json reactants_object{}; - reactants_object[species_name] = {}; - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(object[PRODUCTS]); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - Species reactant_species = Species(""); - for (auto& species : species_arr_) - { - if (species.name_ == species_name) + if (products.first != ConfigParseStatus::Success) { - reactant_species = species; - break; + return products.first; } - } - SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), - .species_ = reactant_species }; - if (object.contains(PROBABILITY)) - { - parameters.reaction_probability_ = object[PROBABILITY].get(); - } + Species reactant_species = Species(""); + for (auto& species : species_arr_) + { + if (species.name_ == species_name) + { + reactant_species = species; + break; + } + } + SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), + .species_ = reactant_species }; - surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); + if (object.contains(PROBABILITY)) + { + parameters.reaction_probability_ = object[PROBABILITY].get(); + } - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); - return ConfigParseStatus::Success; - } + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } /// @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 @@ -905,6 +944,14 @@ namespace micm // 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); @@ -944,6 +991,7 @@ namespace micm { if (!key.starts_with("__")) { + std::cerr << "non-standard key " << key << std::endl; return ConfigParseStatus::ContainsNonStandardKey; } } @@ -955,33 +1003,32 @@ namespace micm template class SolverConfig : public ConfigTypePolicy { - private: - ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; - - public: - /// @brief Reads and parses configures - /// @param config_dir A path to a configuration file - /// @return an enum indicating the success or failure of the parse - [[nodiscard]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) - { - last_parse_status_ = this->Parse(config_dir); - return last_parse_status_; - } + private: + ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; - /// @brief Creates and returns SolverParameters - /// @return SolverParameters that contains 'System' and a collection of 'Process' - SolverParameters GetSolverParams() - { - if (last_parse_status_ != ConfigParseStatus::Success) + 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]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) { - std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_); - throw std::runtime_error(msg); + last_parse_status_ = this->Parse(config_dir); + return last_parse_status_; } - return SolverParameters( + /// @brief Creates and returns SolverParameters + /// @return SolverParameters that contains 'System' and a collection of 'Process' + SolverParameters GetSolverParams() + { + if (last_parse_status_ != ConfigParseStatus::Success) + { + std::string msg = "Parsing configuration files failed. The parsing failed with error: " + + configParseStatusToString(last_parse_status_); + throw std::runtime_error(msg); + } + + return SolverParameters( std::move(System(std::move(this->gas_phase_), std::move(this->phases_))), std::move(this->processes_)); - } + } }; - } // namespace micm diff --git a/test/unit/configure/CMakeLists.txt b/test/unit/configure/CMakeLists.txt index d8a571056..0ff3e452e 100644 --- a/test/unit/configure/CMakeLists.txt +++ b/test/unit/configure/CMakeLists.txt @@ -7,7 +7,6 @@ include(test_util) # Tests create_standard_test(NAME solver_config SOURCES test_solver_config.cpp) -create_standard_test(NAME camp_config SOURCES test_camp_config.cpp) ################################################################################ # Tests diff --git a/test/unit/configure/process/CMakeLists.txt b/test/unit/configure/process/CMakeLists.txt index 4043124d2..437d7f4ac 100644 --- a/test/unit/configure/process/CMakeLists.txt +++ b/test/unit/configure/process/CMakeLists.txt @@ -16,12 +16,3 @@ create_standard_test(NAME ternary_chemical_activation_config SOURCES test_ternar create_standard_test(NAME troe_config SOURCES test_troe_config.cpp) create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp) -create_standard_test(NAME arrhenius_camp_config SOURCES test_arrhenius_camp_config.cpp) -create_standard_test(NAME branched_camp_config SOURCES test_branched_camp_config.cpp) -create_standard_test(NAME emission_camp_config SOURCES test_emission_camp_config.cpp) -create_standard_test(NAME first_order_loss_camp_config SOURCES test_first_order_loss_camp_config.cpp) -create_standard_test(NAME photolysis_camp_config SOURCES test_photolysis_camp_config.cpp) -create_standard_test(NAME surface_camp_config SOURCES test_surface_camp_config.cpp) -create_standard_test(NAME ternary_chemical_activation_camp_config SOURCES test_ternary_chemical_activation_camp_config.cpp) -create_standard_test(NAME troe_camp_config SOURCES test_troe_camp_config.cpp) -create_standard_test(NAME tunneling_camp_config SOURCES test_tunneling_camp_config.cpp) diff --git a/test/unit/configure/process/test_arrhenius_camp_config.cpp b/test/unit/configure/process/test_arrhenius_camp_config.cpp deleted file mode 100644 index 2b6b7aaf5..000000000 --- a/test/unit/configure/process/test_arrhenius_camp_config.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include - -#include - -TEST(ArrheniusConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/mutually_exclusive"); - EXPECT_EQ(micm::ConfigParseStatus::MutuallyExclusiveOption, status); -} - -TEST(ArrheniusConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 3); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); - EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::ArrheniusRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.A_, 1.0); - EXPECT_EQ(params.B_, 0.0); - EXPECT_EQ(params.C_, 0.0); - EXPECT_EQ(params.D_, 300); - EXPECT_EQ(params.E_, 0.0); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 2); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[1].products_.size(), 2); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 0.5); - EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[1].products_[1].second, 1.0); - micm::ArrheniusRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.A_, 32.1); - EXPECT_EQ(params.B_, -2.3); - EXPECT_EQ(params.C_, 102.3); - EXPECT_EQ(params.D_, 63.4); - EXPECT_EQ(params.E_, -1.3); - } - - // third reaction - { - EXPECT_EQ(process_vector[2].reactants_.size(), 2); - EXPECT_EQ(process_vector[2].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[2].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[2].products_.size(), 2); - EXPECT_EQ(process_vector[2].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[2].products_[0].second, 0.5); - EXPECT_EQ(process_vector[2].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[2].products_[1].second, 1.0); - micm::ArrheniusRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[2].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.A_, 32.1); - EXPECT_EQ(params.B_, -2.3); - EXPECT_EQ(params.C_, -1 * 2e23 / BOLTZMANN_CONSTANT); - EXPECT_EQ(params.D_, 63.4); - EXPECT_EQ(params.E_, -1.3); - } -} - -TEST(ArrheniusConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(ArrheniusConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(ArrheniusConfig, DetectsNonstandardReactantCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_branched_camp_config.cpp b/test/unit/configure/process/test_branched_camp_config.cpp deleted file mode 100644 index 225b1a441..000000000 --- a/test/unit/configure/process/test_branched_camp_config.cpp +++ /dev/null @@ -1,131 +0,0 @@ -#include - -#include - -TEST(BranchedConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/branched/missing_alkoxy_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/branched/missing_nitrate_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(BranchedConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - EXPECT_EQ(process_vector.size(), 4); - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 3); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); - EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::BranchedRateConstant* branched_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 12.3); - EXPECT_EQ(params.Y_, 42.3); - EXPECT_EQ(params.a0_, 1.0e-5); - EXPECT_EQ(params.n_, 3); - EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Alkoxy); - } - { - EXPECT_EQ(process_vector[1].reactants_.size(), 3); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[1].reactants_[1].name_, "quz"); - EXPECT_EQ(process_vector[1].reactants_[2].name_, "quz"); - EXPECT_EQ(process_vector[1].products_.size(), 1); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "quz"); - EXPECT_EQ(process_vector[1].products_[0].second, 1.0); - micm::BranchedRateConstant* branched_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 12.3); - EXPECT_EQ(params.Y_, 42.3); - EXPECT_EQ(params.a0_, 1.0e-5); - EXPECT_EQ(params.n_, 3); - EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Nitrate); - } - - // second reaction - { - EXPECT_EQ(process_vector[2].reactants_.size(), 2); - EXPECT_EQ(process_vector[2].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[2].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[2].products_.size(), 1); - EXPECT_EQ(process_vector[2].products_[0].first.name_, "baz"); - EXPECT_EQ(process_vector[2].products_[0].second, 1.0); - micm::BranchedRateConstant* branched_rate_constant = - dynamic_cast(process_vector[2].rate_constant_.get()); - auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 0.32); - EXPECT_EQ(params.Y_, 2.3e8); - EXPECT_EQ(params.a0_, 0.423); - EXPECT_EQ(params.n_, 6); - EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Alkoxy); - } - { - EXPECT_EQ(process_vector[3].reactants_.size(), 2); - EXPECT_EQ(process_vector[3].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[3].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[3].products_.size(), 2); - EXPECT_EQ(process_vector[3].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[3].products_[0].second, 0.5); - EXPECT_EQ(process_vector[3].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[3].products_[1].second, 1.0); - micm::BranchedRateConstant* branched_rate_constant = - dynamic_cast(process_vector[3].rate_constant_.get()); - auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 0.32); - EXPECT_EQ(params.Y_, 2.3e8); - EXPECT_EQ(params.a0_, 0.423); - EXPECT_EQ(params.n_, 6); - EXPECT_EQ(params.branch_, micm::BranchedRateConstantParameters::Branch::Nitrate); - } -} - -TEST(BranchedConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(BranchedConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_alkoxy_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); - - status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_nitrate_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(BranchedConfig, DetectsNonstandardReactantCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/branched/nonstandard_reactant_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_emission_camp_config.cpp b/test/unit/configure/process/test_emission_camp_config.cpp deleted file mode 100644 index e820f54d0..000000000 --- a/test/unit/configure/process/test_emission_camp_config.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include - -#include - -TEST(EmissionConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/emission/missing_MUSICA_name"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(EmissionConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 0); - EXPECT_EQ(process_vector[0].products_.size(), 1); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "foo"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - micm::UserDefinedRateConstant* emission_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); - EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.foo"); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 0); - EXPECT_EQ(process_vector[1].products_.size(), 1); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 1.0); - micm::UserDefinedRateConstant* emission_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); - EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.bar"); - } -} - -TEST(EmissionConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/emission/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_first_order_loss_camp_config.cpp b/test/unit/configure/process/test_first_order_loss_camp_config.cpp deleted file mode 100644 index 300b2561b..000000000 --- a/test/unit/configure/process/test_first_order_loss_camp_config.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include - -#include - -TEST(FirstOrderLossConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/missing_MUSICA_name"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(FirstOrderLossConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/first_order_loss/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 1); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].products_.size(), 0); - micm::UserDefinedRateConstant* first_order_loss_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); - EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.foo"); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 1); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].products_.size(), 0); - micm::UserDefinedRateConstant* first_order_loss_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); - EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.bar"); - } -} - -TEST(FirstOrderLossConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/first_order_loss/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_photolysis_camp_config.cpp b/test/unit/configure/process/test_photolysis_camp_config.cpp deleted file mode 100644 index 4a303d1e1..000000000 --- a/test/unit/configure/process/test_photolysis_camp_config.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include - -#include - -TEST(PhotolysisConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/photolysis/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/photolysis/missing_MUSICA_name"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(PhotolysisConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 1); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::UserDefinedRateConstant* photo_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); - EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jfoo"); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 1); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].products_.size(), 2); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 0.5); - EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[1].products_[1].second, 1.0); - micm::UserDefinedRateConstant* photo_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); - EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jbar"); - } -} - -TEST(PhotolysisConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(PhotolysisConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/nonstandard_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(PhotolysisConfig, DetectsNonstandardReactantCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/photolysis/nonstandard_reactant_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_surface_camp_config.cpp b/test/unit/configure/process/test_surface_camp_config.cpp deleted file mode 100644 index b85e84a4d..000000000 --- a/test/unit/configure/process/test_surface_camp_config.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include - -#include - -TEST(SurfaceConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/surface/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/surface/missing_MUSICA_name"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(SurfaceConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 1); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::SurfaceRateConstant* surface_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - EXPECT_EQ(surface_rate_constant->SizeCustomParameters(), 2); - EXPECT_EQ(surface_rate_constant->CustomParameters()[0], "SURF.kfoo.effective radius [m]"); - EXPECT_EQ(surface_rate_constant->CustomParameters()[1], "SURF.kfoo.particle number concentration [# m-3]"); - EXPECT_EQ(surface_rate_constant->parameters_.reaction_probability_, 1.0); - EXPECT_EQ(surface_rate_constant->diffusion_coefficient_, 2.3e-4); - EXPECT_EQ(surface_rate_constant->mean_free_speed_factor_, 8.0 * GAS_CONSTANT / (M_PI * 0.123)); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 1); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].products_.size(), 2); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 0.5); - EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[1].products_[1].second, 1.0); - micm::SurfaceRateConstant* surface_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - EXPECT_EQ(surface_rate_constant->SizeCustomParameters(), 2); - EXPECT_EQ(surface_rate_constant->CustomParameters()[0], "SURF.kbar.effective radius [m]"); - EXPECT_EQ(surface_rate_constant->CustomParameters()[1], "SURF.kbar.particle number concentration [# m-3]"); - EXPECT_EQ(surface_rate_constant->parameters_.reaction_probability_, 0.5); - EXPECT_EQ(surface_rate_constant->diffusion_coefficient_, 0.4e-5); - EXPECT_EQ(surface_rate_constant->mean_free_speed_factor_, 8.0 * GAS_CONSTANT / (M_PI * 0.321)); - } -} - -TEST(SurfaceConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(SurfaceConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/surface/nonstandard_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp b/test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp deleted file mode 100644 index 64ad3ecd2..000000000 --- a/test/unit/configure/process/test_ternary_chemical_activation_camp_config.cpp +++ /dev/null @@ -1,101 +0,0 @@ -#include - -#include - -TEST(TernaryChemicalActivationConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(TernaryChemicalActivationConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 3); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); - EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::TernaryChemicalActivationRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 1.0); - EXPECT_EQ(params.k0_B_, 0.0); - EXPECT_EQ(params.k0_C_, 0.0); - EXPECT_EQ(params.kinf_A_, 1.0); - EXPECT_EQ(params.kinf_B_, 0.0); - EXPECT_EQ(params.kinf_C_, 0.0); - EXPECT_EQ(params.Fc_, 0.6); - EXPECT_EQ(params.N_, 1.0); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 2); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[1].products_.size(), 2); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 0.5); - EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[1].products_[1].second, 1.0); - micm::TernaryChemicalActivationRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 32.1); - EXPECT_EQ(params.k0_B_, -2.3); - EXPECT_EQ(params.k0_C_, 102.3); - EXPECT_EQ(params.kinf_A_, 63.4); - EXPECT_EQ(params.kinf_B_, -1.3); - EXPECT_EQ(params.kinf_C_, 908.5); - EXPECT_EQ(params.Fc_, 1.3); - EXPECT_EQ(params.N_, 32.1); - } -} - -TEST(TernaryChemicalActivationConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(TernaryChemicalActivationConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(TernaryChemicalActivationConfig, DetectsNonstandardReactantCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = - solver_config.ReadAndParse("./unit_configs/process/ternary_chemical_activation/nonstandard_reactant_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_troe_camp_config.cpp b/test/unit/configure/process/test_troe_camp_config.cpp deleted file mode 100644 index 99934568f..000000000 --- a/test/unit/configure/process/test_troe_camp_config.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include - -#include - -TEST(TroeConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/troe/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(TroeConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 3); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); - EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::TroeRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 1.0); - EXPECT_EQ(params.k0_B_, 0.0); - EXPECT_EQ(params.k0_C_, 0.0); - EXPECT_EQ(params.kinf_A_, 1.0); - EXPECT_EQ(params.kinf_B_, 0.0); - EXPECT_EQ(params.kinf_C_, 0.0); - EXPECT_EQ(params.Fc_, 0.6); - EXPECT_EQ(params.N_, 1.0); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 2); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[1].products_.size(), 2); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 0.5); - EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[1].products_[1].second, 1.0); - micm::TroeRateConstant* ternary_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 32.1); - EXPECT_EQ(params.k0_B_, -2.3); - EXPECT_EQ(params.k0_C_, 102.3); - EXPECT_EQ(params.kinf_A_, 63.4); - EXPECT_EQ(params.kinf_B_, -1.3); - EXPECT_EQ(params.kinf_C_, 908.5); - EXPECT_EQ(params.Fc_, 1.3); - EXPECT_EQ(params.N_, 32.1); - } -} - -TEST(TroeConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(TroeConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/nonstandard_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(TroeConfig, DetectsNonstandardReactantCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/troe/nonstandard_reactant_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/process/test_tunneling_camp_config.cpp b/test/unit/configure/process/test_tunneling_camp_config.cpp deleted file mode 100644 index 726362336..000000000 --- a/test/unit/configure/process/test_tunneling_camp_config.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#include - -TEST(TunnelingConfig, DetectsInvalidConfig) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/missing_reactants"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); - status = solver_config.ReadAndParse("./unit_configs/process/tunneling/missing_products"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); -} - -TEST(TunnelingConfig, ParseConfig) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // first reaction - { - EXPECT_EQ(process_vector[0].reactants_.size(), 3); - EXPECT_EQ(process_vector[0].reactants_[0].name_, "foo"); - EXPECT_EQ(process_vector[0].reactants_[1].name_, "quz"); - EXPECT_EQ(process_vector[0].reactants_[2].name_, "quz"); - EXPECT_EQ(process_vector[0].products_.size(), 2); - EXPECT_EQ(process_vector[0].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[0].products_[0].second, 1.0); - EXPECT_EQ(process_vector[0].products_[1].first.name_, "baz"); - EXPECT_EQ(process_vector[0].products_[1].second, 3.2); - micm::TunnelingRateConstant* tunneling_rate_constant = - dynamic_cast(process_vector[0].rate_constant_.get()); - auto& params = tunneling_rate_constant->parameters_; - EXPECT_EQ(params.A_, 1.0); - EXPECT_EQ(params.B_, 0.0); - EXPECT_EQ(params.C_, 0.0); - } - - // second reaction - { - EXPECT_EQ(process_vector[1].reactants_.size(), 2); - EXPECT_EQ(process_vector[1].reactants_[0].name_, "bar"); - EXPECT_EQ(process_vector[1].reactants_[1].name_, "baz"); - EXPECT_EQ(process_vector[1].products_.size(), 2); - EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); - EXPECT_EQ(process_vector[1].products_[0].second, 0.5); - EXPECT_EQ(process_vector[1].products_[1].first.name_, "foo"); - EXPECT_EQ(process_vector[1].products_[1].second, 1.0); - micm::TunnelingRateConstant* tunneling_rate_constant = - dynamic_cast(process_vector[1].rate_constant_.get()); - auto& params = tunneling_rate_constant->parameters_; - EXPECT_EQ(params.A_, 32.1); - EXPECT_EQ(params.B_, -2.3); - EXPECT_EQ(params.C_, 102.3); - } -} - -TEST(TunnelingConfig, DetectsNonstandardKeys) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/tunneling/contains_nonstandard_key"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(TunnelingConfig, DetectsNonstandardProductCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_product_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} - -TEST(TunnelingConfig, DetectsNonstandardReactantCoefficient) -{ - micm::SolverConfig solver_config; - - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/arrhenius/nonstandard_reactant_coef"); - EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); -} diff --git a/test/unit/configure/test_camp_config.cpp b/test/unit/configure/test_camp_config.cpp deleted file mode 100644 index 0d9010a6d..000000000 --- a/test/unit/configure/test_camp_config.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include - -#include - -TEST(SolverConfig, DetectsInvalidConfigFile) -{ - micm::SolverConfig solverConfig{}; - auto status = solverConfig.ReadAndParse("not_a_config_file"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); -} - -TEST(SolverConfig, NoConfigFilesFound) -{ - micm::SolverConfig solverConfig{}; - auto status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid/config.json"); - EXPECT_EQ(micm::ConfigParseStatus::NoConfigFilesFound, status); -} - -TEST(SolverConfig, ReadAndParseCAMPFiles) -{ - micm::SolverConfig solverConfig{}; - - // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid/config.json"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); -} - -TEST(SolverConfig, ReadAndParseCAMPFilesFromDir) -{ - micm::SolverConfig solverConfig{}; - - // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); -} - -TEST(SolverConfig, ReadAndParseSystemObject) -{ - micm::SolverConfig solverConfig; - - // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman/config.json"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - // Check 'gas_phase' in 'System' - EXPECT_EQ(solver_params.system_.gas_phase_.species_.size(), 9); - - // Check 'phases' in 'System' - EXPECT_EQ(solver_params.system_.phases_.size(), 0); - - // Check 'name' and 'properties' in 'Species' - std::vector> species_name_and_num_properties = { - std::make_pair("M", 0), std::make_pair("Ar", 1), std::make_pair("CO2", 1), - std::make_pair("H2O", 1), std::make_pair("N2", 1), std::make_pair("O1D", 1), - std::make_pair("O", 1), std::make_pair("O2", 1), std::make_pair("O3", 1) - }; - - short idx = 0; - for (const auto& s : solver_params.system_.gas_phase_.species_) - { - EXPECT_EQ(s.name_, species_name_and_num_properties[idx].first); - EXPECT_EQ(s.properties_.size(), species_name_and_num_properties[idx].second); - idx++; - } -} - -TEST(SolverConfig, ReadAndParseProcessObjects) -{ - micm::SolverConfig solverConfig; - - // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman/config.json"); - EXPECT_EQ(micm::ConfigParseStatus::Success, status); - - // Get solver parameters ('System', the collection of 'Process') - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // Check the number of 'Process' created - EXPECT_EQ(process_vector.size(), 15); - - // Check the number of 'reactants' and 'products' in each 'Process' - // Check 'yield' value for the first product and the number of 'spieces in 'phase' in each 'Process' - int num_reactants_in_each_process[] = { 1, 1, 1, 2, 2, 2, 3, 0, 0, 0, 1, 1, 1, 1, 1 }; - int num_products_in_each_process[] = { 1, 2, 2, 2, 2, 1, 2, 1, 1, 1, 0, 0, 0, 0, 0 }; - double yield_value_of_first_product_in_each_process[] = { 2.0, 1.0, 1.0, 1.0, 1.0, 2.0, 1.0, 1.0, - 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; - int num_phase_in_each_process = 9; - - short idx = 0; - for (const auto& p : process_vector) - { - EXPECT_EQ(p.reactants_.size(), num_reactants_in_each_process[idx]); - EXPECT_EQ(p.products_.size(), num_products_in_each_process[idx]); - if (num_products_in_each_process[idx] > 0) - EXPECT_EQ(p.products_[0].second, yield_value_of_first_product_in_each_process[idx]); - EXPECT_EQ(p.phase_.species_.size(), num_phase_in_each_process); - idx++; - } - - // Check the parameters for 'ArrheniusRateConstant' - micm::ArrheniusRateConstant* arrhenius_rate_const = nullptr; - double A_param[] = { 2.15e-11, 3.3e-11, 8.0e-12, 6.0e-34 }; - double B_param[] = { 0.0, 0.0, 0.0, 2.4 }; - double C_param[] = { 110.0, 55.0, -2060.00, 0.0 }; - double D_param[] = { 300.0, 300.0, 300.0, 300.0 }; - double E_param[] = { 0.0, 0.0, 0.0, 0.0 }; - - for (short i = 3; i < 7; i++) - { - arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); - - EXPECT_TRUE(arrhenius_rate_const != nullptr); - EXPECT_EQ(arrhenius_rate_const->parameters_.A_, A_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.B_, B_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.C_, C_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.D_, D_param[i - 3]); - EXPECT_EQ(arrhenius_rate_const->parameters_.E_, E_param[i - 3]); - } - - // Check the number of custom parameters of 'rate constant' in each 'Process' - std::vector> custom_rate_labels = { { "PHOTO.O2_1" }, - { "PHOTO.O3_1" }, - { "PHOTO.O3_2" }, - {}, - {}, - {}, - {}, - { "EMIS.O1D" }, - { "EMIS.O" }, - { "EMIS.O3" }, - { "LOSS.N2" }, - { "LOSS.O2" }, - { "LOSS.CO2" }, - { "LOSS.Ar" }, - { "LOSS.H2O" } }; - - // check photlysis, emissions, and loss reaction labels - idx = 0; - std::vector::iterator it; - for (it = solver_params.processes_.begin(); it != solver_params.processes_.end(); it++, idx++) - { - EXPECT_EQ(it->rate_constant_->SizeCustomParameters(), custom_rate_labels[idx].size()); - for (std::size_t i = 0; i < custom_rate_labels[idx].size(); ++i) - EXPECT_EQ(it->rate_constant_->CustomParameters()[i], custom_rate_labels[idx][i]); - } -} diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 55c7fe82c..cf229a0b7 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -5,8 +5,15 @@ TEST(SolverConfig, DetectsInvalidConfigFile) { micm::SolverConfig solverConfig{}; - auto status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidSpeciesFilePath, status); + auto status = solverConfig.ReadAndParse("not_a_config_file"); + EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFilePath, status); +} + +TEST(SolverConfig, NoConfigFilesFound) +{ + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid/config.json"); + EXPECT_EQ(micm::ConfigParseStatus::NoConfigFilesFound, status); } TEST(SolverConfig, ReadAndParseCAMPFiles) @@ -14,21 +21,17 @@ TEST(SolverConfig, ReadAndParseCAMPFiles) micm::SolverConfig solverConfig{}; // Read and parse the CAMP configure file - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); } -TEST(SolverConfig, DetectsInvalidCAMPConfigFile) +TEST(SolverConfig, ReadAndParseCAMPFilesFromDir) { micm::SolverConfig solverConfig{}; // Read and parse the CAMP configure file - micm::ConfigParseStatus status; - status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_invalid"); - EXPECT_EQ(micm::ConfigParseStatus::InvalidCAMPFileCount, status); - - status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_no_files_key"); - EXPECT_EQ(micm::ConfigParseStatus::CAMPFilesSectionNotFound, status); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); } TEST(SolverConfig, ReadAndParseSystemObject) @@ -36,7 +39,7 @@ TEST(SolverConfig, ReadAndParseSystemObject) micm::SolverConfig solverConfig; // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); // Get solver parameters ('System', the collection of 'Process') @@ -69,7 +72,7 @@ TEST(SolverConfig, ReadAndParseProcessObjects) micm::SolverConfig solverConfig; // Read and parse the configure files - micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman"); + micm::ConfigParseStatus status = solverConfig.ReadAndParse("./unit_configs/chapman/config.json"); EXPECT_EQ(micm::ConfigParseStatus::Success, status); // Get solver parameters ('System', the collection of 'Process') @@ -146,11 +149,3 @@ TEST(SolverConfig, ReadAndParseProcessObjects) EXPECT_EQ(it->rate_constant_->CustomParameters()[i], custom_rate_labels[idx][i]); } } - -TEST(SolverConfig, GettingSolverParamsThrowsExceptionWithFailedParsing) -{ - micm::SolverConfig solverConfig; - micm::ConfigParseStatus status = solverConfig.ReadAndParse("not_a_config_file_directory"); - EXPECT_NE(micm::ConfigParseStatus::Success, status); - EXPECT_ANY_THROW(solverConfig.GetSolverParams()); -} From ca2dd2be0884424fb15c2af1e52f20951411137d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 30 Oct 2023 23:55:40 +0000 Subject: [PATCH 211/318] Added test parameters. --- test/surface_rxn/test_surface_rxn.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index 99200294a..2981b9c31 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -9,8 +8,19 @@ int main(const int argc, const char* argv[]) { - micm::SolverConfig solver_config; + const double rxn_gamma = 2.0e-2; // [unitless] + const double bar_yield = 1.0; // [unitless] + const double baz_yield = 0.4; // [unitless] + const double DENSITY_stuff = 1000.0; // [kg m-3] + const double DENSITY_more_stuff = 1000.0; // [kg m-3] + const double MW_stuff = 0.5; // [kg mol-1] + const double MW_more_stuff = 0.2; // [kg mol-1] + const double MW_foo = 0.04607; // [kg mol-1] + const double Dg_foo = 0.95e-5; // diffusion coeff [m2 s-1] + const double sp_number = 1.3e6; // single-particle number concentration [# m-3] + const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] + const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] return 0; } From eacb3a08068dbecaa802944f3e92b59b148b2ac7 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 31 Oct 2023 16:30:48 +0000 Subject: [PATCH 212/318] Auto-format code using Clang-Format --- include/micm/configure/solver_config.hpp | 1374 +++++++++++----------- 1 file changed, 686 insertions(+), 688 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index e6dfcb538..cad5c6e84 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -8,20 +8,19 @@ #include #include #include -#include +#include +#include #include -#include +#include +#include +#include +#include +#include #include #include +#include #include - -#include -#include -#include -#include -#include -#include -#include +#include namespace micm { @@ -88,845 +87,844 @@ namespace micm { using json = nlohmann::json; - public: - std::vector species_arr_; + public: + std::vector species_arr_; + + std::vector user_defined_rate_arr_; + std::vector arrhenius_rate_arr_; + std::vector troe_rate_arr_; + std::vector ternary_rate_arr_; + std::vector branched_rate_arr_; + std::vector tunneling_rate_arr_; + std::vector surface_rate_arr_; - std::vector user_defined_rate_arr_; - std::vector arrhenius_rate_arr_; - std::vector troe_rate_arr_; - std::vector ternary_rate_arr_; - std::vector branched_rate_arr_; - std::vector tunneling_rate_arr_; - std::vector surface_rate_arr_; + // Specific for solver parameters + Phase gas_phase_; + std::unordered_map phases_; + std::vector processes_; - // Specific for solver parameters - Phase gas_phase_; - std::unordered_map phases_; - std::vector processes_; + // Common JSON + static const inline std::string DEFAULT_CONFIG_FILE = "config.json"; + static const inline std::string CAMP_FILES = "camp-files"; + static const inline std::string CAMP_DATA = "camp-data"; + static const inline std::string TYPE = "type"; - // Common JSON - static const inline std::string DEFAULT_CONFIG_FILE = "config.json"; - static const inline std::string CAMP_FILES = "camp-files"; - static const inline std::string CAMP_DATA = "camp-data"; - static const inline std::string TYPE = "type"; + // Functions - // Functions + /// @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) + { + // Parse status + ConfigParseStatus status; - /// @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) + // Look for CAMP config path + if (!std::filesystem::exists(config_path)) { - // Parse status - ConfigParseStatus status; + status = ConfigParseStatus::InvalidCAMPFilePath; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } - // Look for CAMP config path - if (!std::filesystem::exists(config_path)) - { - status = ConfigParseStatus::InvalidCAMPFilePath; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } + std::filesystem::path config_dir; + std::filesystem::path config_file; - std::filesystem::path config_dir; - std::filesystem::path config_file; + if (std::filesystem::is_directory(config_path)) + { + // If config path is a directory, use default config file name + config_dir = config_path; + config_file = config_dir / DEFAULT_CONFIG_FILE; + } + else + { + // Extract configuration dir from configuration file path + config_dir = config_path.parent_path(); + config_file = config_path; + } - if (std::filesystem::is_directory(config_path)) - { - // If config path is a directory, use default config file name - config_dir = config_path; - config_file = config_dir / DEFAULT_CONFIG_FILE; - } - else - { - // Extract configuration dir from configuration file path - config_dir = config_path.parent_path(); - config_file = config_path; - } + // std::cout << "config_file " << config_file << std::endl; - // std::cout << "config_file " << config_file << std::endl; + // Load the CAMP file list JSON + json camp_data = json::parse(std::ifstream(config_file)); + if (!camp_data.contains(CAMP_FILES)) + { + status = ConfigParseStatus::CAMPFilesSectionNotFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } - // Load the CAMP file list JSON - json camp_data = json::parse(std::ifstream(config_file)); - if (!camp_data.contains(CAMP_FILES)) + // Build a list of individual CAMP config files + std::vector camp_files; + for (const auto& element : camp_data[CAMP_FILES]) + { + std::filesystem::path camp_file = config_dir / element.get(); + if (std::filesystem::exists(camp_file)) { - status = ConfigParseStatus::CAMPFilesSectionNotFound; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; + camp_files.push_back(camp_file); } + // Error return here if CAMP files list has a missing file? + } - // Build a list of individual CAMP config files - std::vector camp_files; - for (const auto& element : camp_data[CAMP_FILES]) - { - std::filesystem::path camp_file = config_dir / element.get(); - if (std::filesystem::exists(camp_file)) - { - camp_files.push_back(camp_file); - } - // Error return here if CAMP files list has a missing file? - } + // No config files found + if (camp_files.size() < 1) + { + status = ConfigParseStatus::NoConfigFilesFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } - // No config files found - if (camp_files.size() < 1) - { - status = ConfigParseStatus::NoConfigFilesFound; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } + std::vector species_objects; + std::vector mechanism_objects; - std::vector species_objects; - std::vector mechanism_objects; + // Iterate CAMP file list and form CAMP data object arrays + for (const auto& camp_file : camp_files) + { + json config_subset = json::parse(std::ifstream(camp_file)); - // Iterate CAMP file list and form CAMP data object arrays - for (const auto& camp_file : camp_files) + if (config_subset.contains(CAMP_DATA)) { - json config_subset = json::parse(std::ifstream(camp_file)); - - if (config_subset.contains(CAMP_DATA)) + // Iterate JSON objects from CAMP data entry + for (const auto& object : config_subset[CAMP_DATA]) { - // Iterate JSON objects from CAMP data entry - for (const auto& object : config_subset[CAMP_DATA]) + if (!object.is_null()) { - if (!object.is_null()) + // Require object to have a type entry + if (!ValidateJsonWithKey(object, TYPE)) + { + status = ConfigParseStatus::ObjectTypeNotFound; + std::string msg = configParseStatusToString(status); + std::cerr << msg << std::endl; + return status; + } + // Sort into object arrays by type + std::string type = object[TYPE].get(); + // CHEM_SPEC and RELATIVE_TOLERANCE parsed first by ParseSpeciesArray + if ((type == "CHEM_SPEC") || (type == "RELATIVE_TOLERANCE")) { - // Require object to have a type entry - if (!ValidateJsonWithKey(object, TYPE)) - { - status = ConfigParseStatus::ObjectTypeNotFound; - std::string msg = configParseStatusToString(status); - std::cerr << msg << std::endl; - return status; - } - // Sort into object arrays by type - std::string type = object[TYPE].get(); - // CHEM_SPEC and RELATIVE_TOLERANCE parsed first by ParseSpeciesArray - if ((type == "CHEM_SPEC") || (type == "RELATIVE_TOLERANCE")) - { - species_objects.push_back(object); - } - // All other objects will be parsed by ParseMechanismArray - else - { - mechanism_objects.push_back(object); - } + species_objects.push_back(object); + } + // All other objects will be parsed by ParseMechanismArray + else + { + mechanism_objects.push_back(object); } } } - else - { - return ConfigParseStatus::CAMPDataSectionNotFound; - } } + else + { + return ConfigParseStatus::CAMPDataSectionNotFound; + } + } - // Clear vectors and maps - species_arr_.clear(); - user_defined_rate_arr_.clear(); - arrhenius_rate_arr_.clear(); - troe_rate_arr_.clear(); - ternary_rate_arr_.clear(); - branched_rate_arr_.clear(); - tunneling_rate_arr_.clear(); - surface_rate_arr_.clear(); - phases_.clear(); - processes_.clear(); + // Clear vectors and maps + species_arr_.clear(); + user_defined_rate_arr_.clear(); + arrhenius_rate_arr_.clear(); + troe_rate_arr_.clear(); + ternary_rate_arr_.clear(); + branched_rate_arr_.clear(); + tunneling_rate_arr_.clear(); + surface_rate_arr_.clear(); + phases_.clear(); + processes_.clear(); - // Parse species object array - status = ParseSpeciesArray(species_objects); + // Parse species object array + status = ParseSpeciesArray(species_objects); - // Assign the parsed 'Species' to 'Phase' - gas_phase_ = Phase(species_arr_); + // Assign the parsed 'Species' to 'Phase' + gas_phase_ = Phase(species_arr_); - // Parse mechanism object array - status = ParseMechanismArray(mechanism_objects); + // Parse mechanism object array + status = ParseMechanismArray(mechanism_objects); - return status; - } + return status; + } - private: + private: + ConfigParseStatus ParseSpeciesArray(const std::vector& objects) + { + ConfigParseStatus status = ConfigParseStatus::None; - ConfigParseStatus ParseSpeciesArray(const std::vector& objects) + for (const auto& object : objects) { - ConfigParseStatus status = ConfigParseStatus::None; + std::string type = object[TYPE].get(); - for (const auto& object : objects) - { - std::string type = object[TYPE].get(); - - // debug statements - // std::cout << type << std::endl; - // std::cout << "ParseSpeciesArray object " << object.dump(4) << std::endl; + // debug statements + // std::cout << type << std::endl; + // std::cout << "ParseSpeciesArray object " << object.dump(4) << std::endl; - if (type == "CHEM_SPEC") - { - status = ParseChemicalSpecies(object); - } - else if (type == "RELATIVE_TOLERANCE") - { - status = ParseRelativeTolerance(object); - } - - if (status != ConfigParseStatus::Success) - break; + if (type == "CHEM_SPEC") + { + status = ParseChemicalSpecies(object); + } + else if (type == "RELATIVE_TOLERANCE") + { + status = ParseRelativeTolerance(object); } - return status; + if (status != ConfigParseStatus::Success) + break; } - ConfigParseStatus ParseMechanismArray(const std::vector& objects) - { - ConfigParseStatus status = ConfigParseStatus::None; + return status; + } - for (const auto& object : objects) - { - std::string type = object[TYPE].get(); + ConfigParseStatus ParseMechanismArray(const std::vector& objects) + { + ConfigParseStatus status = ConfigParseStatus::None; - // debug statements - // std::cout << type << std::endl; - // std::cout << "ParseMechanismArray object " << object.dump(4) << std::endl; + for (const auto& object : objects) + { + std::string type = object[TYPE].get(); - if (type == "MECHANISM") - { - status = ParseMechanism(object); - } - else if (type == "PHOTOLYSIS") - { - status = ParsePhotolysis(object); - } - else if (type == "EMISSION") - { - status = ParseEmission(object); - } - else if (type == "FIRST_ORDER_LOSS") - { - status = ParseFirstOrderLoss(object); - } - else if (type == "ARRHENIUS") - { - status = ParseArrhenius(object); - } - else if (type == "TROE") - { - status = ParseTroe(object); - } - else if (type == "TERNARY_CHEMICAL_ACTIVATION") - { - status = ParseTernaryChemicalActivation(object); - } - else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") - { - status = ParseBranched(object); - } - else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") - { - status = ParseTunneling(object); - } - else if (type == "SURFACE") - { - status = ParseSurface(object); - } - else - { - status = ConfigParseStatus::UnknownKey; - } + // debug statements + // std::cout << type << std::endl; + // std::cout << "ParseMechanismArray object " << object.dump(4) << std::endl; - if (status != ConfigParseStatus::Success) - break; + if (type == "MECHANISM") + { + status = ParseMechanism(object); } - - return status; - } - - bool ValidateJsonWithKey(const json& object, const std::string& key) - { - if (!object.contains(key)) + else if (type == "PHOTOLYSIS") { - std::string msg = "Key " + key + " was not found in the config file"; - std::cerr << msg << std::endl; - return false; + status = ParsePhotolysis(object); } - return true; - } - - ConfigParseStatus ParseChemicalSpecies(const json& object) - { - // required keys - const std::string NAME = "name"; - - auto status = ValidateSchema( - object, - { NAME, "type" }, - { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); - if (status != ConfigParseStatus::Success) + else if (type == "EMISSION") { - return status; + status = ParseEmission(object); } - - std::string name = object[NAME].get(); - - // Load remaining keys as properties - std::map properties{}; - for (auto& [key, value] : object.items()) + else if (type == "FIRST_ORDER_LOSS") { - if (value.is_number_float()) - properties[key] = value; + status = ParseFirstOrderLoss(object); + } + else if (type == "ARRHENIUS") + { + status = ParseArrhenius(object); + } + else if (type == "TROE") + { + status = ParseTroe(object); + } + else if (type == "TERNARY_CHEMICAL_ACTIVATION") + { + status = ParseTernaryChemicalActivation(object); + } + else if (type == "BRANCHED" || type == "WENNBERG_NO_RO2") + { + status = ParseBranched(object); + } + else if (type == "TUNNELING" || type == "WENNBERG_TUNNELING") + { + status = ParseTunneling(object); + } + else if (type == "SURFACE") + { + status = ParseSurface(object); + } + else + { + status = ConfigParseStatus::UnknownKey; } - species_arr_.push_back(Species(name, properties)); - return ConfigParseStatus::Success; + if (status != ConfigParseStatus::Success) + break; } - ConfigParseStatus ParseRelativeTolerance(const json& object) + return status; + } + + bool ValidateJsonWithKey(const json& object, const std::string& key) + { + if (!object.contains(key)) { - return ConfigParseStatus::Success; + std::string msg = "Key " + key + " was not found in the config file"; + std::cerr << msg << std::endl; + return false; } + return true; + } + + ConfigParseStatus ParseChemicalSpecies(const json& object) + { + // required keys + const std::string NAME = "name"; - ConfigParseStatus ParseMechanism(const json& object) + auto status = ValidateSchema( + object, + { NAME, "type" }, + { "tracer type", "absolute tolerance", "diffusion coefficient [m2 s-1]", "molecular weight [kg mol-1]" }); + if (status != ConfigParseStatus::Success) { - auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + return status; + } - std::vector objects; - for (const auto& element : object["reactions"]) - { - objects.push_back(element); - } + std::string name = object[NAME].get(); - return ParseMechanismArray(objects); + // Load remaining keys as properties + std::map properties{}; + for (auto& [key, value] : object.items()) + { + if (value.is_number_float()) + properties[key] = value; } + species_arr_.push_back(Species(name, properties)); - std::pair> ParseReactants(const json& object) - { - const std::string QTY = "qty"; - std::vector reactants; + return ConfigParseStatus::Success; + } - ConfigParseStatus status = ConfigParseStatus::Success; + ConfigParseStatus ParseRelativeTolerance(const json& object) + { + return ConfigParseStatus::Success; + } - for (auto& [key, value] : object.items()) - { - std::size_t qty = 1; - auto new_status = ValidateSchema(value, {}, { "qty" }); - if (new_status != ConfigParseStatus::Success) - { - status = new_status; - } - if (value.contains(QTY)) - qty = value[QTY]; - for (std::size_t i = 0; i < qty; ++i) - reactants.push_back(Species(key)); - } - return std::make_pair(status, reactants); + ConfigParseStatus ParseMechanism(const json& object) + { + auto status = ValidateSchema(object, { "name", "reactions", "type" }, {}); + if (status != ConfigParseStatus::Success) + { + return status; } - std::pair>> ParseProducts(const json& object) + std::vector objects; + for (const auto& element : object["reactions"]) { - const std::string YIELD = "yield"; + objects.push_back(element); + } + + return ParseMechanismArray(objects); + } + + std::pair> ParseReactants(const json& object) + { + const std::string QTY = "qty"; + std::vector reactants; - ConfigParseStatus status = ConfigParseStatus::Success; + ConfigParseStatus status = ConfigParseStatus::Success; - constexpr double DEFAULT_YIELD = 1.0; - std::vector> products; - for (auto& [key, value] : object.items()) + for (auto& [key, value] : object.items()) + { + std::size_t qty = 1; + auto new_status = ValidateSchema(value, {}, { "qty" }); + if (new_status != ConfigParseStatus::Success) { - auto new_status = ValidateSchema(value, {}, { "yield" }); - if (new_status != ConfigParseStatus::Success) - { - status = new_status; - } - if (value.contains(YIELD)) - { - products.push_back(std::make_pair(Species(key), value[YIELD])); - } - else - { - products.push_back(std::make_pair(Species(key), DEFAULT_YIELD)); - } + status = new_status; } - return std::make_pair(status, products); + if (value.contains(QTY)) + qty = value[QTY]; + for (std::size_t i = 0; i < qty; ++i) + reactants.push_back(Species(key)); } + return std::make_pair(status, reactants); + } - ConfigParseStatus ParsePhotolysis(const json& object) - { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; - const std::string MUSICA_NAME = "MUSICA name"; + std::pair>> ParseProducts(const json& object) + { + const std::string YIELD = "yield"; - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) + ConfigParseStatus status = ConfigParseStatus::Success; + + constexpr double DEFAULT_YIELD = 1.0; + std::vector> products; + for (auto& [key, value] : object.items()) + { + auto new_status = ValidateSchema(value, {}, { "yield" }); + if (new_status != ConfigParseStatus::Success) { - return status; + status = new_status; } - - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); - - if (reactants.first != ConfigParseStatus::Success) + if (value.contains(YIELD)) { - return reactants.first; + products.push_back(std::make_pair(Species(key), value[YIELD])); } - - if (products.first != ConfigParseStatus::Success) + else { - return products.first; + products.push_back(std::make_pair(Species(key), DEFAULT_YIELD)); } + } + return std::make_pair(status, products); + } - std::string name = "PHOTO." + object[MUSICA_NAME].get(); + ConfigParseStatus ParsePhotolysis(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + const std::string MUSICA_NAME = "MUSICA name"; - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - return ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; } - ConfigParseStatus ParseEmission(const json& object) + if (products.first != ConfigParseStatus::Success) { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; + return products.first; + } - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + std::string name = "PHOTO." + object[MUSICA_NAME].get(); - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - products_object[species] = { { "yield", 1.0 } }; + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + return ConfigParseStatus::Success; + } - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + ConfigParseStatus ParseEmission(const json& object) + { + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; - std::string name = "EMIS." + object[MUSICA_NAME].get(); + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + products_object[species] = { { "yield", 1.0 } }; - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); - return ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; } - ConfigParseStatus ParseFirstOrderLoss(const json& object) + if (products.first != ConfigParseStatus::Success) { - const std::string SPECIES = "species"; - const std::string MUSICA_NAME = "MUSICA name"; + return products.first; + } - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + std::string name = "EMIS." + object[MUSICA_NAME].get(); - std::string species = object["species"].get(); - json reactants_object{}; - json products_object{}; - reactants_object[species] = { {} }; + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(products_object); + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + return ConfigParseStatus::Success; + } - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + ConfigParseStatus ParseFirstOrderLoss(const json& object) + { + const std::string SPECIES = "species"; + const std::string MUSICA_NAME = "MUSICA name"; - std::string name = "LOSS." + object[MUSICA_NAME].get(); + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + std::string species = object["species"].get(); + json reactants_object{}; + json products_object{}; + reactants_object[species] = { {} }; - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(products_object); - return ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; } - ConfigParseStatus ParseArrhenius(const json& object) + if (products.first != ConfigParseStatus::Success) { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + return products.first; + } - auto status = - ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); - if (status != ConfigParseStatus::Success) - { - return status; - } + std::string name = "LOSS." + object[MUSICA_NAME].get(); - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + return ConfigParseStatus::Success; + } - ArrheniusRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } - if (object.contains("D")) - { - parameters.D_ = object["D"].get(); - } - if (object.contains("E")) - { - parameters.E_ = object["E"].get(); - } - if (object.contains("Ea")) - { - if (parameters.C_ != 0) - { - std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl; - return ConfigParseStatus::MutuallyExclusiveOption; - } - // Calculate 'C' using 'Ea' - parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; - } + ConfigParseStatus ParseArrhenius(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); - - std::unique_ptr rate_ptr = std::make_unique(parameters); + auto status = + ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C", "D", "E", "Ea", "MUSICA name" }); + if (status != ConfigParseStatus::Success) + { + return status; + } - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - return ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; } - ConfigParseStatus ParseTroe(const json& object) + if (products.first != ConfigParseStatus::Success) { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + return products.first; + } - auto status = ValidateSchema( - object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); - if (status != ConfigParseStatus::Success) + ArrheniusRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) + { + parameters.C_ = object["C"].get(); + } + if (object.contains("D")) + { + parameters.D_ = object["D"].get(); + } + if (object.contains("E")) + { + parameters.E_ = object["E"].get(); + } + if (object.contains("Ea")) + { + if (parameters.C_ != 0) { - return status; + std::cerr << "Ea is specified when C is also specified for an Arrhenius reaction. Pick one." << std::endl; + return ConfigParseStatus::MutuallyExclusiveOption; } + // Calculate 'C' using 'Ea' + parameters.C_ = -1 * object["Ea"].get() / BOLTZMANN_CONSTANT; + } - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); + arrhenius_rate_arr_.push_back(ArrheniusRateConstant(parameters)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + std::unique_ptr rate_ptr = std::make_unique(parameters); - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - TroeRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } + return ConfigParseStatus::Success; + } - troe_rate_arr_.push_back(TroeRateConstant(parameters)); + ConfigParseStatus ParseTroe(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - std::unique_ptr rate_ptr = std::make_unique(parameters); + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) + { + return status; + } - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - return ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; } - ConfigParseStatus ParseTernaryChemicalActivation(const json& object) + if (products.first != ConfigParseStatus::Success) { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + return products.first; + } - auto status = ValidateSchema( - object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); - if (status != ConfigParseStatus::Success) - { - return status; - } + TroeRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); + troe_rate_arr_.push_back(TroeRateConstant(parameters)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + std::unique_ptr rate_ptr = std::make_unique(parameters); - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - TernaryChemicalActivationRateConstantParameters parameters; - if (object.contains("k0_A")) - { - parameters.k0_A_ = object["k0_A"].get(); - } - if (object.contains("k0_B")) - { - parameters.k0_B_ = object["k0_B"].get(); - } - if (object.contains("k0_C")) - { - parameters.k0_C_ = object["k0_C"].get(); - } - if (object.contains("kinf_A")) - { - parameters.kinf_A_ = object["kinf_A"].get(); - } - if (object.contains("kinf_B")) - { - parameters.kinf_B_ = object["kinf_B"].get(); - } - if (object.contains("kinf_C")) - { - parameters.kinf_C_ = object["kinf_C"].get(); - } - if (object.contains("Fc")) - { - parameters.Fc_ = object["Fc"].get(); - } - if (object.contains("N")) - { - parameters.N_ = object["N"].get(); - } + return ConfigParseStatus::Success; + } - ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); + ConfigParseStatus ParseTernaryChemicalActivation(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - std::unique_ptr rate_ptr = - std::make_unique(parameters); + auto status = ValidateSchema( + object, { "type", REACTANTS, PRODUCTS }, { "k0_A", "k0_B", "k0_C", "kinf_A", "kinf_B", "kinf_C", "Fc", "N" }); + if (status != ConfigParseStatus::Success) + { + return status; + } - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - return ConfigParseStatus::Success; + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; } - ConfigParseStatus ParseBranched(const json& object) + if (products.first != ConfigParseStatus::Success) { - const std::string REACTANTS = "reactants"; - const std::string ALKOXY_PRODUCTS = "alkoxy products"; - const std::string NITRATE_PRODUCTS = "nitrate products"; - const std::string X = "X"; - const std::string Y = "Y"; - const std::string A0 = "a0"; - const std::string N = "n"; + return products.first; + } - auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); - if (status != ConfigParseStatus::Success) - { - return status; - } + TernaryChemicalActivationRateConstantParameters parameters; + if (object.contains("k0_A")) + { + parameters.k0_A_ = object["k0_A"].get(); + } + if (object.contains("k0_B")) + { + parameters.k0_B_ = object["k0_B"].get(); + } + if (object.contains("k0_C")) + { + parameters.k0_C_ = object["k0_C"].get(); + } + if (object.contains("kinf_A")) + { + parameters.kinf_A_ = object["kinf_A"].get(); + } + if (object.contains("kinf_B")) + { + parameters.kinf_B_ = object["kinf_B"].get(); + } + if (object.contains("kinf_C")) + { + parameters.kinf_C_ = object["kinf_C"].get(); + } + if (object.contains("Fc")) + { + parameters.Fc_ = object["Fc"].get(); + } + if (object.contains("N")) + { + parameters.N_ = object["N"].get(); + } - auto reactants = ParseReactants(object[REACTANTS]); - auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); - auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); + ternary_rate_arr_.push_back(TernaryChemicalActivationRateConstant(parameters)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + std::unique_ptr rate_ptr = + std::make_unique(parameters); - if (alkoxy_products.first != ConfigParseStatus::Success) - { - return alkoxy_products.first; - } + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (nitrate_products.first != ConfigParseStatus::Success) - { - return nitrate_products.first; - } + return ConfigParseStatus::Success; + } - BranchedRateConstantParameters parameters; - parameters.X_ = object[X].get(); - parameters.Y_ = object[Y].get(); - parameters.a0_ = object[A0].get(); - parameters.n_ = object[N].get(); + ConfigParseStatus ParseBranched(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string ALKOXY_PRODUCTS = "alkoxy products"; + const std::string NITRATE_PRODUCTS = "nitrate products"; + const std::string X = "X"; + const std::string Y = "Y"; + const std::string A0 = "a0"; + const std::string N = "n"; + + auto status = ValidateSchema(object, { "type", REACTANTS, ALKOXY_PRODUCTS, NITRATE_PRODUCTS, X, Y, A0, N }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } - // Alkoxy branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); + auto reactants = ParseReactants(object[REACTANTS]); + auto alkoxy_products = ParseProducts(object[ALKOXY_PRODUCTS]); + auto nitrate_products = ParseProducts(object[NITRATE_PRODUCTS]); - // Nitrate branch - parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; - branched_rate_arr_.push_back(BranchedRateConstant(parameters)); - rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - return ConfigParseStatus::Success; + if (alkoxy_products.first != ConfigParseStatus::Success) + { + return alkoxy_products.first; } - ConfigParseStatus ParseTunneling(const json& object) + if (nitrate_products.first != ConfigParseStatus::Success) { - const std::string REACTANTS = "reactants"; - const std::string PRODUCTS = "products"; + return nitrate_products.first; + } - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); - if (status != ConfigParseStatus::Success) - { - return status; - } + BranchedRateConstantParameters parameters; + parameters.X_ = object[X].get(); + parameters.Y_ = object[Y].get(); + parameters.a0_ = object[A0].get(); + parameters.n_ = object[N].get(); - auto reactants = ParseReactants(object[REACTANTS]); - auto products = ParseProducts(object[PRODUCTS]); + // Alkoxy branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Alkoxy; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants.second, alkoxy_products.second, std::move(rate_ptr), gas_phase_)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + // Nitrate branch + parameters.branch_ = BranchedRateConstantParameters::Branch::Nitrate; + branched_rate_arr_.push_back(BranchedRateConstant(parameters)); + rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants.second, nitrate_products.second, std::move(rate_ptr), gas_phase_)); - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + return ConfigParseStatus::Success; + } - TunnelingRateConstantParameters parameters; - if (object.contains("A")) - { - parameters.A_ = object["A"].get(); - } - if (object.contains("B")) - { - parameters.B_ = object["B"].get(); - } - if (object.contains("C")) - { - parameters.C_ = object["C"].get(); - } + ConfigParseStatus ParseTunneling(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; - tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS }, { "A", "B", "C" }); + if (status != ConfigParseStatus::Success) + { + return status; + } - std::unique_ptr rate_ptr = std::make_unique(parameters); + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } - return ConfigParseStatus::Success; + if (products.first != ConfigParseStatus::Success) + { + return products.first; } - ConfigParseStatus ParseSurface(const json& object) + TunnelingRateConstantParameters parameters; + if (object.contains("A")) + { + parameters.A_ = object["A"].get(); + } + if (object.contains("B")) + { + parameters.B_ = object["B"].get(); + } + if (object.contains("C")) { - const std::string REACTANTS = "gas-phase reactant"; - const std::string PRODUCTS = "gas-phase products"; - const std::string MUSICA_NAME = "MUSICA name"; - const std::string PROBABILITY = "reaction probability"; + parameters.C_ = object["C"].get(); + } - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); - if (status != ConfigParseStatus::Success) - { - return status; - } + tunneling_rate_arr_.push_back(TunnelingRateConstant(parameters)); - std::string species_name = object[REACTANTS].get(); - json reactants_object{}; - reactants_object[species_name] = { {} }; + std::unique_ptr rate_ptr = std::make_unique(parameters); - auto reactants = ParseReactants(reactants_object); - auto products = ParseProducts(object[PRODUCTS]); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - if (reactants.first != ConfigParseStatus::Success) - { - return reactants.first; - } + return ConfigParseStatus::Success; + } - if (products.first != ConfigParseStatus::Success) - { - return products.first; - } + ConfigParseStatus ParseSurface(const json& object) + { + const std::string REACTANTS = "gas-phase reactant"; + const std::string PRODUCTS = "gas-phase products"; + const std::string MUSICA_NAME = "MUSICA name"; + const std::string PROBABILITY = "reaction probability"; - Species reactant_species = Species(""); - for (auto& species : species_arr_) - { - if (species.name_ == species_name) - { - reactant_species = species; - break; - } - } - SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), - .species_ = reactant_species }; + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { PROBABILITY }); + if (status != ConfigParseStatus::Success) + { + return status; + } - if (object.contains(PROBABILITY)) + std::string species_name = object[REACTANTS].get(); + json reactants_object{}; + reactants_object[species_name] = { {} }; + + auto reactants = ParseReactants(reactants_object); + auto products = ParseProducts(object[PRODUCTS]); + + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + + Species reactant_species = Species(""); + for (auto& species : species_arr_) + { + if (species.name_ == species_name) { - parameters.reaction_probability_ = object[PROBABILITY].get(); + reactant_species = species; + break; } + } + SurfaceRateConstantParameters parameters{ .label_ = "SURF." + object[MUSICA_NAME].get(), + .species_ = reactant_species }; + + if (object.contains(PROBABILITY)) + { + parameters.reaction_probability_ = object[PROBABILITY].get(); + } - surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); + surface_rate_arr_.push_back(SurfaceRateConstant(parameters)); - std::unique_ptr rate_ptr = std::make_unique(parameters); - processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + std::unique_ptr rate_ptr = std::make_unique(parameters); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); - return ConfigParseStatus::Success; - } + return ConfigParseStatus::Success; + } /// @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 @@ -1003,32 +1001,32 @@ namespace micm template class SolverConfig : public ConfigTypePolicy { - private: - ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; + private: + ConfigParseStatus last_parse_status_ = ConfigParseStatus::None; + + 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]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) + { + last_parse_status_ = this->Parse(config_dir); + return last_parse_status_; + } - 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]] ConfigParseStatus ReadAndParse(const std::filesystem::path& config_dir) + /// @brief Creates and returns SolverParameters + /// @return SolverParameters that contains 'System' and a collection of 'Process' + SolverParameters GetSolverParams() + { + if (last_parse_status_ != ConfigParseStatus::Success) { - last_parse_status_ = this->Parse(config_dir); - return last_parse_status_; + std::string msg = "Parsing configuration files failed. The parsing failed with error: " + + configParseStatusToString(last_parse_status_); + throw std::runtime_error(msg); } - /// @brief Creates and returns SolverParameters - /// @return SolverParameters that contains 'System' and a collection of 'Process' - SolverParameters GetSolverParams() - { - if (last_parse_status_ != ConfigParseStatus::Success) - { - std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_); - throw std::runtime_error(msg); - } - - return SolverParameters( + return SolverParameters( std::move(System(std::move(this->gas_phase_), std::move(this->phases_))), std::move(this->processes_)); - } + } }; } // namespace micm From ecfcba7ec757adb8a3c8140fccb9abb3925ce946 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 31 Oct 2023 13:12:37 -0500 Subject: [PATCH 213/318] correcting config for cmake integration test --- test/integration/cmake/configs/robertson/config.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 test/integration/cmake/configs/robertson/config.json diff --git a/test/integration/cmake/configs/robertson/config.json b/test/integration/cmake/configs/robertson/config.json new file mode 100644 index 000000000..61165b00d --- /dev/null +++ b/test/integration/cmake/configs/robertson/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json"]} From fa541155859e59d0cb413cefbca9de15d47828f4 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 31 Oct 2023 16:04:29 -0500 Subject: [PATCH 214/318] fixing bad merge --- include/micm/configure/solver_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index fb51639c3..1f69dd332 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -895,7 +895,7 @@ namespace micm std::string species_name = object[REACTANTS].get(); json reactants_object{}; - reactants_object[species_name] = { {} }; + reactants_object[species_name] = { }; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(object[PRODUCTS]); From 25a3f820f97c5da9a8b6792c637a465fad08f4bd Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Tue, 31 Oct 2023 16:19:31 -0700 Subject: [PATCH 215/318] optimize jit functions --- docker/Dockerfile.llvm | 1 + include/micm/jit/jit_compiler.hpp | 19 ++++ include/micm/jit/jit_function.hpp | 3 + test/unit/process/CMakeLists.txt | 1 + test/unit/process/forcing_calculation.cpp | 63 ++++++++++++ test/unit/process/forcing_calculation.hpp | 11 +++ .../process/test_jit_forcing_calculation.cpp | 95 +++++++++++++++++++ test/unit/process/test_jit_process_set.cpp | 38 ++++---- test/unit/process/test_process_set.cpp | 12 +-- 9 files changed, 218 insertions(+), 25 deletions(-) create mode 100644 test/unit/process/forcing_calculation.cpp create mode 100644 test/unit/process/forcing_calculation.hpp create mode 100644 test/unit/process/test_jit_forcing_calculation.cpp diff --git a/docker/Dockerfile.llvm b/docker/Dockerfile.llvm index 78767b7a8..a23e6ab68 100644 --- a/docker/Dockerfile.llvm +++ b/docker/Dockerfile.llvm @@ -2,6 +2,7 @@ FROM fedora:37 RUN dnf -y update \ && dnf -y install \ + clang \ cmake \ gcc-c++ \ gdb \ diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 356f0fbf8..12e63762d 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -11,6 +11,7 @@ #include #include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/LoopAccessAnalysis.h" #include "llvm/ExecutionEngine/JITSymbol.h" #include "llvm/ExecutionEngine/Orc/CompileUtils.h" #include "llvm/ExecutionEngine/Orc/Core.h" @@ -26,8 +27,11 @@ #include "llvm/IR/LegacyPassManager.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Transforms/InstCombine/InstCombine.h" +#include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Scalar/GVN.h" +#include "llvm/Transforms/Utils.h" +#include "llvm/Transforms/Vectorize.h" namespace micm { @@ -129,17 +133,32 @@ namespace micm // Create a function pass manager. auto pass_manager = std::make_unique(&module); + llvm::VectorizerParams::VectorizationFactor = 2; + // Add some optimizations. + pass_manager->add(llvm::createLoopRotatePass()); + pass_manager->add(llvm::createLICMPass()); pass_manager->add(llvm::createInstructionCombiningPass()); pass_manager->add(llvm::createReassociatePass()); + pass_manager->add(llvm::createPromoteMemoryToRegisterPass()); pass_manager->add(llvm::createGVNPass()); pass_manager->add(llvm::createCFGSimplificationPass()); + pass_manager->add(llvm::createLoopVectorizePass()); + pass_manager->add(llvm::createSLPVectorizerPass()); + //pass_manager->add(llvm::createGlobalOptimizerPass()); pass_manager->doInitialization(); // Run the optimizations over all functions in the module being added to // the JIT. for (auto &function : module) + { pass_manager->run(function); +#if 0 + std::cout << "Generated function definition:" << std::endl; + function.print(llvm::errs()); + std::cout << std::endl; +#endif + } }); return std::move(threadsafe_module); diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp index b7ba12fee..d8178d180 100644 --- a/include/micm/jit/jit_function.hpp +++ b/include/micm/jit/jit_function.hpp @@ -10,6 +10,7 @@ #include #include "jit_compiler.hpp" +#include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" @@ -184,6 +185,8 @@ namespace micm arg.arg_ = arg_iter++; arg.arg_->setName(arg.name_); } + for (unsigned int i = 0; i < arguments_.size(); ++i) + function_->addParamAttr(i, llvm::Attribute::NoAlias); // function body diff --git a/test/unit/process/CMakeLists.txt b/test/unit/process/CMakeLists.txt index ce767e174..9d81cdc65 100644 --- a/test/unit/process/CMakeLists.txt +++ b/test/unit/process/CMakeLists.txt @@ -27,4 +27,5 @@ endif() if(ENABLE_LLVM) create_standard_test(NAME jit_process_set SOURCES test_jit_process_set.cpp) + create_standard_test(NAME jit_forcing_calculation SOURCES test_jit_forcing_calculation.cpp forcing_calculation.cpp) endif() \ No newline at end of file diff --git a/test/unit/process/forcing_calculation.cpp b/test/unit/process/forcing_calculation.cpp new file mode 100644 index 000000000..c402b1c0d --- /dev/null +++ b/test/unit/process/forcing_calculation.cpp @@ -0,0 +1,63 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// SPDX-License-Identifier: Apache-2.0 +// +// A function hard-coded to calculate forcing for a toy chemistry system. +// This can be used to compare with the corresponding JITed function + +#include "forcing_calculation.hpp" + +#define SPEC_A 0 +#define SPEC_B 1 +#define SPEC_C 2 +#define SPEC_D 3 +#define SPEC_E 4 +#define SPEC_F 5 + +void calculate_forcing(double* rate_constants, double* state, double* forcing) +{ + double rate[NUM_GRID_CELLS]; + + // Reaction 1: A + B -> 3.2 D + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] = rate_constants[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] *= state[SPEC_A * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] *= state[SPEC_B * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_A * NUM_GRID_CELLS + i] -= rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_B * NUM_GRID_CELLS + i] -= rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_D * NUM_GRID_CELLS + i] += 3.2 * rate[i]; + + // Reaction 2: E + C -> A + 2 F + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] = rate_constants[NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] *= state[SPEC_E * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] *= state[SPEC_C * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_E * NUM_GRID_CELLS + i] -= rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_C * NUM_GRID_CELLS + i] -= rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_A * NUM_GRID_CELLS + i] += rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_F * NUM_GRID_CELLS + i] += 2.0 * rate[i]; + + // Reaction 3: C + B -> A + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] = rate_constants[2 * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] *= state[SPEC_C * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + rate[i] *= state[SPEC_B * NUM_GRID_CELLS + i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_C * NUM_GRID_CELLS + i] -= rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_B * NUM_GRID_CELLS + i] -= rate[i]; + for (int i = 0; i < NUM_GRID_CELLS; ++i) + forcing[SPEC_A * NUM_GRID_CELLS + i] += rate[i]; +} \ No newline at end of file diff --git a/test/unit/process/forcing_calculation.hpp b/test/unit/process/forcing_calculation.hpp new file mode 100644 index 000000000..116d2ba75 --- /dev/null +++ b/test/unit/process/forcing_calculation.hpp @@ -0,0 +1,11 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// SPDX-License-Identifier: Apache-2.0 +// +// A function hard-coded to calculate forcing for a toy chemistry system. +// This can be used to compare with the corresponding JITed function + +#pragma once + +#define NUM_GRID_CELLS 1000 + +void calculate_forcing(double* rate_constants, double* state, double* forcing); \ No newline at end of file diff --git a/test/unit/process/test_jit_forcing_calculation.cpp b/test/unit/process/test_jit_forcing_calculation.cpp new file mode 100644 index 000000000..4b7d93261 --- /dev/null +++ b/test/unit/process/test_jit_forcing_calculation.cpp @@ -0,0 +1,95 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// SPDX-License-Identifier: Apache-2.0 +// +// A comparison of the JITed forcing function with a hard-coded equivalent +// for a small chemical system. +// +// This code can be used as a test and a way to compare the IR of the +// JITed function with an optimized compilation of the hard-coded function + +#include + +#include +#include +#include +#include +#include + +#include "forcing_calculation.hpp" + +#define NUM_ITERATIONS 100 + +template +using ForcingTestVectorMatrix = micm::VectorMatrix; +template +using ForcingTestSparseVectorMatrix = micm::SparseMatrix>; + +TEST(JitProcessSet, ForcingFunction) +{ + auto a = micm::Species("A"); + auto b = micm::Species("B"); + auto c = micm::Species("C"); + auto d = micm::Species("D"); + auto e = micm::Species("E"); + auto f = micm::Species("F"); + + micm::Phase gas_phase{ std::vector{ a, b, c, d, e, f } }; + + micm::State state( + micm::StateParameters{ .number_of_grid_cells_ = NUM_GRID_CELLS, + .number_of_rate_constants_ = 3, + .variable_names_{ "A", "B", "C", "D", "E", "F" } }); + + micm::Process r1 = micm::Process::create().reactants({ a, b }).products({ micm::Yield{ d, 3.2 } }).phase(gas_phase); + + micm::Process r2 = micm::Process::create() + .reactants({ e, c }) + .products({ micm::Yield{ a, 1.0 }, micm::Yield{ f, 2.0 } }) + .phase(gas_phase); + + micm::Process r3 = micm::Process::create().reactants({ c, b }).products({ micm::Yield{ a, 1.0 } }).phase(gas_phase); + + std::vector processes{ r1, r2, r3 }; + + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + micm::JitProcessSet process_set{ jit.get(), processes, state.variable_map_ }; + + ForcingTestVectorMatrix rate_constants{ NUM_GRID_CELLS, 3 }; + ForcingTestVectorMatrix forcing{ NUM_GRID_CELLS, 6, 0.0 }; + ForcingTestVectorMatrix jit_forcing{ NUM_GRID_CELLS, 6, 0.0 }; + for (int i = 0; i < 3 * NUM_GRID_CELLS; ++i) + rate_constants.AsVector()[i] = i * 2.0; + for (int i = 0; i < 6 * NUM_GRID_CELLS; ++i) + state.variables_.AsVector()[i] = i * 1.0; + + std::chrono::nanoseconds fixed_time{ 0 }; + std::chrono::nanoseconds jit_time{ 0 }; + for (std::size_t i = 0; i < NUM_ITERATIONS; ++i) + { + for (auto& elem : forcing.AsVector()) + elem = 0.0; + auto start = std::chrono::high_resolution_clock::now(); + calculate_forcing(rate_constants.AsVector().data(), state.variables_.AsVector().data(), forcing.AsVector().data()); + auto end = std::chrono::high_resolution_clock::now(); + fixed_time += std::chrono::duration_cast(end - start); + for (auto& elem : jit_forcing.AsVector()) + elem = 0.0; + start = std::chrono::high_resolution_clock::now(); + process_set.template AddForcingTerms(rate_constants, state.variables_, jit_forcing); + end = std::chrono::high_resolution_clock::now(); + jit_time += std::chrono::duration_cast(end - start); + for (std::size_t i = 0; i < forcing.AsVector().size(); ++i) + { + EXPECT_EQ(forcing.AsVector()[i], jit_forcing.AsVector()[i]); + } + } + std::cout << "Fixed forcing calculation time " << ((double)fixed_time.count()) / NUM_GRID_CELLS / NUM_ITERATIONS + << " ns per grid cell" << std::endl; + std::cout << "JITed forcing calculation time " << ((double)jit_time.count()) / NUM_GRID_CELLS / NUM_ITERATIONS + << " ns per grid cell" << std::endl; +} \ No newline at end of file diff --git a/test/unit/process/test_jit_process_set.cpp b/test/unit/process/test_jit_process_set.cpp index e1476070d..3b63d6c39 100644 --- a/test/unit/process/test_jit_process_set.cpp +++ b/test/unit/process/test_jit_process_set.cpp @@ -11,11 +11,11 @@ template using Group2VectorMatrix = micm::VectorMatrix; template -using Group2000VectorMatrix = micm::VectorMatrix; +using Group200VectorMatrix = micm::VectorMatrix; template -using Group3000VectorMatrix = micm::VectorMatrix; +using Group300VectorMatrix = micm::VectorMatrix; template -using Group4000VectorMatrix = micm::VectorMatrix; +using Group400VectorMatrix = micm::VectorMatrix; template using Group2SparseVectorMatrix = micm::SparseMatrix>; @@ -43,36 +43,36 @@ TEST(RandomJitProcessSet, VectorMatrix) llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); EXPECT_TRUE(false); } - testRandomSystem>( - 2000, + testRandomSystem>( + 200, 20, 30, [&](const std::vector& processes, - const micm::State& state) -> micm::JitProcessSet<2000> { - return micm::JitProcessSet<2000>{ jit.get(), processes, state.variable_map_ }; + const micm::State& state) -> micm::JitProcessSet<200> { + return micm::JitProcessSet<200>{ jit.get(), processes, state.variable_map_ }; }); - testRandomSystem>( - 3000, + testRandomSystem>( + 300, 50, 40, [&](const std::vector& processes, - const micm::State& state) -> micm::JitProcessSet<3000> { - return micm::JitProcessSet<3000>{ jit.get(), processes, state.variable_map_ }; + const micm::State& state) -> micm::JitProcessSet<300> { + return micm::JitProcessSet<300>{ jit.get(), processes, state.variable_map_ }; }); - testRandomSystem>( - 3000, + testRandomSystem>( + 300, 30, 20, [&](const std::vector& processes, - const micm::State& state) -> micm::JitProcessSet<3000> { - return micm::JitProcessSet<3000>{ jit.get(), processes, state.variable_map_ }; + const micm::State& state) -> micm::JitProcessSet<300> { + return micm::JitProcessSet<300>{ jit.get(), processes, state.variable_map_ }; }); - testRandomSystem>( - 4000, + testRandomSystem>( + 400, 100, 80, [&](const std::vector& processes, - const micm::State& state) -> micm::JitProcessSet<4000> { - return micm::JitProcessSet<4000>{ jit.get(), processes, state.variable_map_ }; + const micm::State& state) -> micm::JitProcessSet<400> { + return micm::JitProcessSet<400>{ jit.get(), processes, state.variable_map_ }; }); } \ No newline at end of file diff --git a/test/unit/process/test_process_set.cpp b/test/unit/process/test_process_set.cpp index d181090f9..48d7d9500 100644 --- a/test/unit/process/test_process_set.cpp +++ b/test/unit/process/test_process_set.cpp @@ -67,24 +67,24 @@ TEST(ProcessSet, VectorMatrix) TEST(RandomProcessSet, Matrix) { testRandomSystem( - 2000, - 500, + 20, + 50, 400, [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testRandomSystem( - 3000, - 300, + 30, + 30, 200, [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { return micm::ProcessSet{ processes, state.variable_map_ }; }); testRandomSystem( - 4000, - 100, + 40, + 10, 80, [](const std::vector& processes, const micm::State& state) -> micm::ProcessSet { From e2ea7935d7b5c524fd2feb421946c33b26f55559 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 1 Nov 2023 10:20:09 -0500 Subject: [PATCH 216/318] removing typename --- test/tutorial/test_jit_tutorial.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index 1d6b33961..a4fd968b2 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -20,7 +20,7 @@ using GroupSparseVectorMatrix = micm::SparseMatrix auto run_solver(T& solver) { - typename T::SolverStats total_stats; + SolverStats total_stats; State state = solver.GetState(); state.variables_ = 1; From 998c77bf718b56448b452f28b5ab000465dfb2a8 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 1 Nov 2023 11:11:27 -0500 Subject: [PATCH 217/318] space --- docs/source/user_guide/jit.rst | 2 +- test/tutorial/test_jit_tutorial.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst index 774661467..f91a28753 100644 --- a/docs/source/user_guide/jit.rst +++ b/docs/source/user_guide/jit.rst @@ -74,7 +74,7 @@ grid cells to be solved simultaneously. :lines: 14-18 Now, all at once, is the function which runs either type of solver. We set all species -concentrations to 1 :math:`\mathrm{mol m^-3}` and set the rate paramters for all of the +concentrations to 1 :math:`\mathrm{mol\ m^-3}` and set the rate paramters for all of the photolysis and emissions reactions. Additionally, we are collecting all of the solver stats across all solving timesteps diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index a4fd968b2..f9fbdc2c0 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -17,8 +17,8 @@ using GroupVectorMatrix = micm::VectorMatrix; template using GroupSparseVectorMatrix = micm::SparseMatrix>; -template -auto run_solver(T& solver) + +auto run_solver(auto& solver) { SolverStats total_stats; State state = solver.GetState(); From cd8e3166b5a5e1d684c781a5b61fec58b98dfd44 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 1 Nov 2023 17:44:38 +0000 Subject: [PATCH 218/318] Auto-format code using Clang-Format --- include/micm/configure/solver_config.hpp | 24 ++-- include/micm/util/matrix.hpp | 5 +- include/micm/util/sparse_matrix.hpp | 5 +- include/micm/util/vector_matrix.hpp | 5 +- test/tutorial/test_jit_tutorial.cpp | 118 +++++++++---------- test/unit/util/test_matrix_policy.hpp | 3 +- test/unit/util/test_sparse_matrix_policy.hpp | 3 +- 7 files changed, 85 insertions(+), 78 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 1f69dd332..ddf79c85c 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -456,7 +456,7 @@ namespace micm const std::string MUSICA_NAME = "MUSICA name"; const std::string SCALING_FACTOR = "scaling_factor"; - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {SCALING_FACTOR}); + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { SCALING_FACTOR }); if (status != ConfigParseStatus::Success) { return status; @@ -474,8 +474,9 @@ namespace micm { return products.first; } - - if (object.contains(SCALING_FACTOR)){ + + if (object.contains(SCALING_FACTOR)) + { std::cerr << "Scaling factor supplied to photolysis rate. This is not yet implemented." << std::endl; } @@ -803,7 +804,7 @@ namespace micm const std::string PRODUCTS = "products"; const std::string SCALING_FACTOR = "scaling factor"; - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {SCALING_FACTOR, PRODUCTS}); + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, { SCALING_FACTOR, PRODUCTS }); if (status != ConfigParseStatus::Success) { return status; @@ -823,12 +824,15 @@ namespace micm { return products.first; } - - if (object.contains(PRODUCTS)) { - std::cerr << "Emission contains products, presumably to record the integrated reaction rate. Ignoring for now" << std::endl; + + if (object.contains(PRODUCTS)) + { + std::cerr << "Emission contains products, presumably to record the integrated reaction rate. Ignoring for now" + << std::endl; } - - if (object.contains(SCALING_FACTOR)){ + + if (object.contains(SCALING_FACTOR)) + { std::cerr << "Scaling factor supplied to emission rate. This is not yet implemented." << std::endl; } @@ -895,7 +899,7 @@ namespace micm std::string species_name = object[REACTANTS].get(); json reactants_object{}; - reactants_object[species_name] = { }; + reactants_object[species_name] = {}; auto reactants = ParseReactants(reactants_object); auto products = ParseProducts(object[PRODUCTS]); diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index 0011e2605..7aa5c9300 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -189,8 +189,9 @@ namespace micm return Proxy(*this, x * y_dim_, y_dim_); } - Matrix& operator=(T val) { - std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _){ return val;}); + Matrix &operator=(T val) + { + std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto &_) { return val; }); return *this; } diff --git a/include/micm/util/sparse_matrix.hpp b/include/micm/util/sparse_matrix.hpp index 065c17be2..a23f7e109 100644 --- a/include/micm/util/sparse_matrix.hpp +++ b/include/micm/util/sparse_matrix.hpp @@ -229,8 +229,9 @@ namespace micm return ProxyRow(*this, b); } - SparseMatrix& operator=(T val) { - std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _){ return val;}); + SparseMatrix& operator=(T val) + { + std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _) { return val; }); return *this; } diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index 35a179408..abb411dcb 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -209,8 +209,9 @@ namespace micm return Proxy(*this, std::floor(x / L), x % L, y_dim_); } - VectorMatrix& operator=(T val) { - std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto& _){ return val;}); + VectorMatrix &operator=(T val) + { + std::transform(data_.begin(), data_.end(), data_.begin(), [&](auto &_) { return val; }); return *this; } diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index f9fbdc2c0..d5a4353d5 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -1,6 +1,5 @@ #include #include - #include #include #include @@ -17,7 +16,6 @@ using GroupVectorMatrix = micm::VectorMatrix; template using GroupSparseVectorMatrix = micm::SparseMatrix>; - auto run_solver(auto& solver) { SolverStats total_stats; @@ -25,46 +23,52 @@ auto run_solver(auto& solver) state.variables_ = 1; - for(int i = 0; i < n_grid_cells; ++i) { + for (int i = 0; i < n_grid_cells; ++i) + { state.conditions_[i].temperature_ = 287.45; // K state.conditions_[i].pressure_ = 101319.9; // Pa state.conditions_[i].air_density_ = 1e6; // mol m-3 } - state.SetCustomRateParameter("EMIS.NO", std::vector(solver.parameters_.number_of_grid_cells_, 1.44e-10)); - state.SetCustomRateParameter("EMIS.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.56e-12)); - state.SetCustomRateParameter("EMIS.CO", std::vector(solver.parameters_.number_of_grid_cells_, 1.9600000000000003e-09)); - state.SetCustomRateParameter("EMIS.SO2", std::vector(solver.parameters_.number_of_grid_cells_, 1.06e-09)); - state.SetCustomRateParameter("EMIS.FORM", std::vector(solver.parameters_.number_of_grid_cells_, 1.02e-11)); - state.SetCustomRateParameter("EMIS.MEOH", std::vector(solver.parameters_.number_of_grid_cells_, 5.920000000000001e-13)); - state.SetCustomRateParameter("EMIS.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 4.25e-12)); - state.SetCustomRateParameter("EMIS.PAR", std::vector(solver.parameters_.number_of_grid_cells_, 4.27e-10)); - state.SetCustomRateParameter("EMIS.ETH", std::vector(solver.parameters_.number_of_grid_cells_, 4.62e-11)); - state.SetCustomRateParameter("EMIS.OLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); - state.SetCustomRateParameter("EMIS.IOLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); - state.SetCustomRateParameter("EMIS.TOL", std::vector(solver.parameters_.number_of_grid_cells_, 1.53e-11)); - state.SetCustomRateParameter("EMIS.XYL", std::vector(solver.parameters_.number_of_grid_cells_, 1.4e-11)); - state.SetCustomRateParameter("EMIS.ISOP", std::vector(solver.parameters_.number_of_grid_cells_, 6.03e-12)); - state.SetCustomRateParameter("PHOTO.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.00477)); - state.SetCustomRateParameter("PHOTO.O3->O1D", std::vector(solver.parameters_.number_of_grid_cells_, 2.26e-06)); - state.SetCustomRateParameter("PHOTO.O3->O3P", std::vector(solver.parameters_.number_of_grid_cells_, 0.00025299999999999997)); - state.SetCustomRateParameter("PHOTO.NO3->NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.11699999999999999)); - state.SetCustomRateParameter("PHOTO.NO3->NO", std::vector(solver.parameters_.number_of_grid_cells_, 0.0144)); - state.SetCustomRateParameter("PHOTO.HONO", std::vector(solver.parameters_.number_of_grid_cells_, 0.000918)); - state.SetCustomRateParameter("PHOTO.H2O2", std::vector(solver.parameters_.number_of_grid_cells_, 2.59e-06)); - state.SetCustomRateParameter("PHOTO.PNA", std::vector(solver.parameters_.number_of_grid_cells_, 1.89e-06)); - state.SetCustomRateParameter("PHOTO.HNO3", std::vector(solver.parameters_.number_of_grid_cells_, 8.61e-08)); - state.SetCustomRateParameter("PHOTO.NTR", std::vector(solver.parameters_.number_of_grid_cells_, 4.77e-07)); - state.SetCustomRateParameter("PHOTO.ROOH", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); - state.SetCustomRateParameter("PHOTO.MEPX", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); + state.SetCustomRateParameter("EMIS.NO", std::vector(solver.parameters_.number_of_grid_cells_, 1.44e-10)); + state.SetCustomRateParameter("EMIS.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.56e-12)); + state.SetCustomRateParameter( + "EMIS.CO", std::vector(solver.parameters_.number_of_grid_cells_, 1.9600000000000003e-09)); + state.SetCustomRateParameter("EMIS.SO2", std::vector(solver.parameters_.number_of_grid_cells_, 1.06e-09)); + state.SetCustomRateParameter("EMIS.FORM", std::vector(solver.parameters_.number_of_grid_cells_, 1.02e-11)); + state.SetCustomRateParameter( + "EMIS.MEOH", std::vector(solver.parameters_.number_of_grid_cells_, 5.920000000000001e-13)); + state.SetCustomRateParameter("EMIS.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 4.25e-12)); + state.SetCustomRateParameter("EMIS.PAR", std::vector(solver.parameters_.number_of_grid_cells_, 4.27e-10)); + state.SetCustomRateParameter("EMIS.ETH", std::vector(solver.parameters_.number_of_grid_cells_, 4.62e-11)); + state.SetCustomRateParameter("EMIS.OLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); + state.SetCustomRateParameter("EMIS.IOLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); + state.SetCustomRateParameter("EMIS.TOL", std::vector(solver.parameters_.number_of_grid_cells_, 1.53e-11)); + state.SetCustomRateParameter("EMIS.XYL", std::vector(solver.parameters_.number_of_grid_cells_, 1.4e-11)); + state.SetCustomRateParameter("EMIS.ISOP", std::vector(solver.parameters_.number_of_grid_cells_, 6.03e-12)); + state.SetCustomRateParameter("PHOTO.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.00477)); + state.SetCustomRateParameter("PHOTO.O3->O1D", std::vector(solver.parameters_.number_of_grid_cells_, 2.26e-06)); + state.SetCustomRateParameter( + "PHOTO.O3->O3P", std::vector(solver.parameters_.number_of_grid_cells_, 0.00025299999999999997)); + state.SetCustomRateParameter( + "PHOTO.NO3->NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.11699999999999999)); + state.SetCustomRateParameter("PHOTO.NO3->NO", std::vector(solver.parameters_.number_of_grid_cells_, 0.0144)); + state.SetCustomRateParameter("PHOTO.HONO", std::vector(solver.parameters_.number_of_grid_cells_, 0.000918)); + state.SetCustomRateParameter("PHOTO.H2O2", std::vector(solver.parameters_.number_of_grid_cells_, 2.59e-06)); + state.SetCustomRateParameter("PHOTO.PNA", std::vector(solver.parameters_.number_of_grid_cells_, 1.89e-06)); + state.SetCustomRateParameter("PHOTO.HNO3", std::vector(solver.parameters_.number_of_grid_cells_, 8.61e-08)); + state.SetCustomRateParameter("PHOTO.NTR", std::vector(solver.parameters_.number_of_grid_cells_, 4.77e-07)); + state.SetCustomRateParameter("PHOTO.ROOH", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); + state.SetCustomRateParameter("PHOTO.MEPX", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); state.SetCustomRateParameter("PHOTO.FORM->HO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.93e-06)); - state.SetCustomRateParameter("PHOTO.FORM->CO", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-05)); - state.SetCustomRateParameter("PHOTO.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); - state.SetCustomRateParameter("PHOTO.PACD", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); - state.SetCustomRateParameter("PHOTO.ALDX", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); - state.SetCustomRateParameter("PHOTO.OPEN", std::vector(solver.parameters_.number_of_grid_cells_, 0.0006450000000000001)); - state.SetCustomRateParameter("PHOTO.MGLY", std::vector(solver.parameters_.number_of_grid_cells_, 7.64e-05)); - state.SetCustomRateParameter("PHOTO.ISPD", std::vector(solver.parameters_.number_of_grid_cells_, 1.98e-09)); + state.SetCustomRateParameter("PHOTO.FORM->CO", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-05)); + state.SetCustomRateParameter("PHOTO.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); + state.SetCustomRateParameter("PHOTO.PACD", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); + state.SetCustomRateParameter("PHOTO.ALDX", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); + state.SetCustomRateParameter( + "PHOTO.OPEN", std::vector(solver.parameters_.number_of_grid_cells_, 0.0006450000000000001)); + state.SetCustomRateParameter("PHOTO.MGLY", std::vector(solver.parameters_.number_of_grid_cells_, 7.64e-05)); + state.SetCustomRateParameter("PHOTO.ISPD", std::vector(solver.parameters_.number_of_grid_cells_, 1.98e-09)); // choose a timestep and print the initial state double time_step = 500; // s @@ -116,24 +120,13 @@ int main(const int argc, const char* argv[]) auto solver_parameters = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(n_grid_cells); - RosenbrockSolver< - GroupVectorMatrix, - GroupSparseVectorMatrix - > solver{ chemical_system, reactions, solver_parameters }; + RosenbrockSolver solver{ chemical_system, reactions, solver_parameters }; auto jit{ micm::JitCompiler::create() }; auto start = std::chrono::high_resolution_clock::now(); - JitRosenbrockSolver< - GroupVectorMatrix, - GroupSparseVectorMatrix, - JitLinearSolver - > jit_solver( - jit.get(), - chemical_system, - reactions, - solver_parameters - ); + JitRosenbrockSolver> + jit_solver(jit.get(), chemical_system, reactions, solver_parameters); auto end = std::chrono::high_resolution_clock::now(); auto jit_compile_time = std::chrono::duration_cast(end - start); @@ -163,10 +156,10 @@ int main(const int argc, const char* argv[]) std::cout << "decompositions: " << result_stats.decompositions << std::endl; std::cout << "solves: " << result_stats.solves << std::endl; std::cout << "singular: " << result_stats.singular << std::endl; - std::cout << "total_forcing_time: " << result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; - std::cout << "total_jacobian_time: " << result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "total_forcing_time: " << result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "total_jacobian_time: " << result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; std::cout << "total_linear_factor_time: " << result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; auto jit_result_stats = jit_result_pair.second; std::cout << "JIT result stats: " << std::endl; @@ -179,17 +172,22 @@ int main(const int argc, const char* argv[]) std::cout << "decompositions: " << jit_result_stats.decompositions << std::endl; std::cout << "solves: " << jit_result_stats.solves << std::endl; std::cout << "singular: " << jit_result_stats.singular << std::endl; - std::cout << "total_forcing_time: " << jit_result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; - std::cout << "total_jacobian_time: " << jit_result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_factor_time: " << jit_result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_solve_time: " << jit_result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; + std::cout << "total_forcing_time: " << jit_result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "total_jacobian_time: " << jit_result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "total_linear_factor_time: " << jit_result_stats.total_linear_factor_time.count() << " nanoseconds" + << std::endl; + std::cout << "total_linear_solve_time: " << jit_result_stats.total_linear_solve_time.count() << " nanoseconds" + << std::endl; auto result = result_pair.first; auto jit_result = result_pair.first; - for(auto& species : result.variable_names_) { - for(int i = 0; i < n_grid_cells; ++i) { - if (result.variables_[i][result.variable_map_[species]] != jit_result.variables_[i][jit_result.variable_map_[species]]) { + for (auto& species : result.variable_names_) + { + for (int i = 0; i < n_grid_cells; ++i) + { + if (result.variables_[i][result.variable_map_[species]] != jit_result.variables_[i][jit_result.variable_map_[species]]) + { std::cout << species << " do not match final concentration" << std::endl; } } diff --git a/test/unit/util/test_matrix_policy.hpp b/test/unit/util/test_matrix_policy.hpp index fec7deb77..666e58a55 100644 --- a/test/unit/util/test_matrix_policy.hpp +++ b/test/unit/util/test_matrix_policy.hpp @@ -240,7 +240,8 @@ MatrixPolicy testSetScalar() matrix = 2.0; - for(auto& elem : matrix.AsVector()) { + for (auto& elem : matrix.AsVector()) + { EXPECT_EQ(elem, 2.0); } diff --git a/test/unit/util/test_sparse_matrix_policy.hpp b/test/unit/util/test_sparse_matrix_policy.hpp index 74b44a36e..f06eb7af2 100644 --- a/test/unit/util/test_sparse_matrix_policy.hpp +++ b/test/unit/util/test_sparse_matrix_policy.hpp @@ -466,7 +466,8 @@ micm::SparseMatrix testSetScalar() matrix = 2.0; - for(auto& elem : matrix.AsVector()) { + for (auto& elem : matrix.AsVector()) + { EXPECT_EQ(elem, 2.0); } From beb0c0c44602ae10829d8f5a3ba642f03b018aee Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 3 Nov 2023 10:17:20 -0700 Subject: [PATCH 219/318] start optimizing JITed code --- docker/Dockerfile | 2 +- docker/Dockerfile.intel | 4 +- docker/Dockerfile.llvm | 2 +- docker/Dockerfile.memcheck | 2 +- docker/Dockerfile.no_json | 2 +- include/micm/jit/jit_compiler.hpp | 2 +- include/micm/solver/rosenbrock.hpp | 2 + include/micm/solver/rosenbrock.inl | 11 +-- include/micm/util/matrix.hpp | 4 +- include/micm/util/vector_matrix.hpp | 4 +- test/tutorial/test_jit_tutorial.cpp | 101 +++++++++++++------------- test/unit/util/test_matrix_policy.hpp | 4 +- 12 files changed, 73 insertions(+), 67 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 89d7c89ea..8760be4dc 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -17,7 +17,7 @@ COPY . /micm/ RUN mkdir /build \ && cd /build \ && cmake \ - -D CMAKE_BUILD_TYPE=debug \ + -D CMAKE_BUILD_TYPE=release \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ ../micm \ && make install -j diff --git a/docker/Dockerfile.intel b/docker/Dockerfile.intel index ac0b0e331..180f7c493 100644 --- a/docker/Dockerfile.intel +++ b/docker/Dockerfile.intel @@ -27,8 +27,8 @@ COPY . /micm/ RUN mkdir /build \ && cd /build \ && cmake \ - -D CMAKE_BUILD_TYPE=debug \ - -D CMAKE_CXX_FLAGS_DEBUG="-g -Rno-debug-disables-optimization" \ + -D CMAKE_BUILD_TYPE=release \ + # -D CMAKE_CXX_FLAGS_DEBUG="-g -Rno-debug-disables-optimization" \ ../micm \ && make -j 8 install diff --git a/docker/Dockerfile.llvm b/docker/Dockerfile.llvm index a23e6ab68..74d4e54f4 100644 --- a/docker/Dockerfile.llvm +++ b/docker/Dockerfile.llvm @@ -20,7 +20,7 @@ COPY . /micm/ RUN mkdir /build \ && cd /build \ && cmake \ - -D CMAKE_BUILD_TYPE=debug \ + -D CMAKE_BUILD_TYPE=release \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ -D ENABLE_LLVM:BOOL=TRUE \ -D ENABLE_MEMCHECK:BOOL=TRUE \ diff --git a/docker/Dockerfile.memcheck b/docker/Dockerfile.memcheck index 4483611ae..3366ce906 100644 --- a/docker/Dockerfile.memcheck +++ b/docker/Dockerfile.memcheck @@ -16,7 +16,7 @@ COPY . /micm/ RUN mkdir /build \ && cd /build \ && cmake \ - -D CMAKE_BUILD_TYPE=Debug \ + -D CMAKE_BUILD_TYPE=release \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ -D ENABLE_MEMCHECK:BOOL=TRUE \ ../micm \ diff --git a/docker/Dockerfile.no_json b/docker/Dockerfile.no_json index 38be2055f..af36a152e 100644 --- a/docker/Dockerfile.no_json +++ b/docker/Dockerfile.no_json @@ -16,7 +16,7 @@ COPY . /micm/ RUN mkdir /build \ && cd /build \ && cmake \ - -D CMAKE_BUILD_TYPE=debug \ + -D CMAKE_BUILD_TYPE=release \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ -D ENABLE_JSON:BOOL=FALSE \ ../micm \ diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 794d5f4b9..56daf9929 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -135,7 +135,7 @@ namespace micm // Create a function pass manager. auto pass_manager = std::make_unique(&module); - llvm::VectorizerParams::VectorizationFactor = 2; + llvm::VectorizerParams::VectorizationFactor = 4; // Add some optimizations. // many from diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index a945a0774..5fa83e440 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -78,6 +78,8 @@ namespace micm /// @brief The number of times a singular matrix is detected. For now, this will always be zero as we assume the matrix /// is never singular uint64_t singular{}; + /// @brief The cumulative amount of time spent updating the state (including rate constant calculations) + std::chrono::duration total_update_state_time{}; /// @brief The cumulative amount of time spent calculating the forcing function std::chrono::duration total_forcing_time{}; /// @brief The cumulative amount of time spent calculating the jacobian diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index fc4390e3a..d028c6022 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -31,6 +31,7 @@ namespace micm decompositions = 0; solves = 0; singular = 0; + total_update_state_time = std::chrono::nanoseconds::zero(); total_forcing_time = std::chrono::nanoseconds::zero(); total_jacobian_time = std::chrono::nanoseconds::zero(); total_linear_factor_time = std::chrono::nanoseconds::zero(); @@ -177,7 +178,7 @@ namespace micm parameters_.h_start_ == 0.0 ? std::max(parameters_.h_min_, delta_min_) : std::min(h_max, parameters_.h_start_); SolverStats stats; - UpdateState(state); + TIMED_METHOD(stats.total_update_state_time, time_it, UpdateState, state); for (std::size_t i = 0; i < parameters_.stages_; ++i) K.push_back(MatrixPolicy(Y.size(), Y[0].size(), 0.0)); @@ -248,7 +249,7 @@ namespace micm for (uint64_t j = 0; j < stage; ++j) { auto a = parameters_.a_[stage_combinations + j]; - Ynew.ForEach([&](double& iYnew, double& iKj) { iYnew += a * iKj; }, K[j]); + Ynew.ForEach([&](double& iYnew, const double& iKj) { iYnew += a * iKj; }, K[j]); } TIMED_METHOD(stats.total_forcing_time, time_it, CalculateForcing, state.rate_constants_, Ynew, forcing); stats.function_calls += 1; @@ -258,7 +259,7 @@ namespace micm for (uint64_t j = 0; j < stage; ++j) { auto HC = parameters_.c_[stage_combinations + j] / H; - K[stage].ForEach([&](double& iKstage, double& iKj) { iKstage += HC * iKj; }, K[j]); + K[stage].ForEach([&](double& iKstage, const double& iKj) { iKstage += HC * iKj; }, K[j]); } temp.AsVector().assign(K[stage].AsVector().begin(), K[stage].AsVector().end()); TIMED_METHOD(stats.total_linear_solve_time, time_it, linear_solver_.template Solve, temp, K[stage], state.lower_matrix_, state.upper_matrix_); @@ -268,12 +269,12 @@ namespace micm // Compute the new solution Ynew.AsVector().assign(Y.AsVector().begin(), Y.AsVector().end()); for (uint64_t stage = 0; stage < parameters_.stages_; ++stage) - Ynew.ForEach([&](double& iYnew, double& iKstage) { iYnew += parameters_.m_[stage] * iKstage; }, K[stage]); + Ynew.ForEach([&](double& iYnew, const double& iKstage) { iYnew += parameters_.m_[stage] * iKstage; }, K[stage]); // Compute the error estimation MatrixPolicy Yerror(Y.size(), Y[0].size(), 0); for (uint64_t stage = 0; stage < parameters_.stages_; ++stage) - Yerror.ForEach([&](double& iYerror, double& iKstage) { iYerror += parameters_.e_[stage] * iKstage; }, K[stage]); + Yerror.ForEach([&](double& iYerror, const double& iKstage) { iYerror += parameters_.e_[stage] * iKstage; }, K[stage]); auto error = NormalizedError(Y, Ynew, Yerror); diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index 7aa5c9300..1d2f4afc1 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -195,14 +195,14 @@ namespace micm return *this; } - void ForEach(const std::function f, Matrix &a) + void ForEach(const std::function f, const Matrix &a) { auto a_iter = a.AsVector().begin(); for (auto &elem : data_) f(elem, *(a_iter++)); } - void ForEach(const std::function f, Matrix &a, Matrix &b) + void ForEach(const std::function f, const Matrix &a, const Matrix &b) { auto a_iter = a.AsVector().begin(); auto b_iter = b.AsVector().begin(); diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index abb411dcb..e1f99d661 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -215,7 +215,7 @@ namespace micm return *this; } - void ForEach(const std::function f, VectorMatrix &a) + void ForEach(const std::function f, const VectorMatrix &a) { auto this_iter = data_.begin(); auto a_iter = a.AsVector().begin(); @@ -228,7 +228,7 @@ namespace micm f(this_iter[y * L + x], a_iter[y * L + x]); } - void ForEach(const std::function f, VectorMatrix &a, VectorMatrix &b) + void ForEach(const std::function f, const VectorMatrix &a, const VectorMatrix &b) { auto this_iter = data_.begin(); auto a_iter = a.AsVector().begin(); diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index 468bc6862..47781137a 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -68,6 +68,8 @@ auto run_solver(auto& solver) // choose a timestep and print the initial state double time_step = 500; // s + auto total_solve_time = std::chrono::nanoseconds::zero(); + // solve for ten iterations for (int i = 0; i < 10; ++i) { @@ -75,7 +77,10 @@ auto run_solver(auto& solver) while (elapsed_solve_time < time_step) { + auto start = std::chrono::high_resolution_clock::now(); auto result = solver.template Solve(time_step - elapsed_solve_time, state); + auto end = std::chrono::high_resolution_clock::now(); + total_solve_time += std::chrono::duration_cast(end - start); elapsed_solve_time = result.final_time_; state.variables_ = result.result_; @@ -87,6 +92,7 @@ auto run_solver(auto& solver) total_stats.decompositions += result.stats_.decompositions; total_stats.solves += result.stats_.solves; total_stats.singular += result.stats_.singular; + total_stats.total_update_state_time += result.stats_.total_update_state_time; total_stats.total_forcing_time += result.stats_.total_forcing_time; total_stats.total_jacobian_time += result.stats_.total_jacobian_time; total_stats.total_linear_factor_time += result.stats_.total_linear_factor_time; @@ -94,7 +100,7 @@ auto run_solver(auto& solver) } } - return std::make_pair(state, total_stats); + return std::make_tuple(state, total_stats, total_solve_time); } int main(const int argc, const char* argv[]) @@ -131,63 +137,60 @@ int main(const int argc, const char* argv[]) std::cout << "Jit compile time: " << jit_compile_time.count() << " nanoseconds" << std::endl; - start = std::chrono::high_resolution_clock::now(); - auto result_pair = run_solver(solver); - end = std::chrono::high_resolution_clock::now(); - auto result_time = std::chrono::duration_cast(end - start); - - start = std::chrono::high_resolution_clock::now(); - auto jit_result_pair = run_solver(jit_solver); - end = std::chrono::high_resolution_clock::now(); - auto jit_time = std::chrono::duration_cast(end - start); - - std::cout << "Result time: " << result_time.count() << " nanoseconds" << std::endl; - std::cout << "JIT result time: " << jit_time.count() << " nanoseconds" << std::endl; - - auto result_stats = result_pair.second; - std::cout << "Result stats: " << std::endl; - std::cout << "accepted: " << result_stats.accepted << std::endl; - std::cout << "function_calls: " << result_stats.function_calls << std::endl; - std::cout << "jacobian_updates: " << result_stats.jacobian_updates << std::endl; - std::cout << "number_of_steps: " << result_stats.number_of_steps << std::endl; - std::cout << "accepted: " << result_stats.accepted << std::endl; - std::cout << "rejected: " << result_stats.rejected << std::endl; - std::cout << "decompositions: " << result_stats.decompositions << std::endl; - std::cout << "solves: " << result_stats.solves << std::endl; - std::cout << "singular: " << result_stats.singular << std::endl; - std::cout << "total_forcing_time: " << result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; - std::cout << "total_jacobian_time: " << result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_factor_time: " << result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; - - auto jit_result_stats = jit_result_pair.second; - std::cout << "JIT result stats: " << std::endl; - std::cout << "accepted: " << jit_result_stats.accepted << std::endl; - std::cout << "function_calls: " << jit_result_stats.function_calls << std::endl; - std::cout << "jacobian_updates: " << jit_result_stats.jacobian_updates << std::endl; - std::cout << "number_of_steps: " << jit_result_stats.number_of_steps << std::endl; - std::cout << "accepted: " << jit_result_stats.accepted << std::endl; - std::cout << "rejected: " << jit_result_stats.rejected << std::endl; - std::cout << "decompositions: " << jit_result_stats.decompositions << std::endl; - std::cout << "solves: " << jit_result_stats.solves << std::endl; - std::cout << "singular: " << jit_result_stats.singular << std::endl; - std::cout << "total_forcing_time: " << jit_result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; - std::cout << "total_jacobian_time: " << jit_result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_factor_time: " << jit_result_stats.total_linear_factor_time.count() << " nanoseconds" + auto result_tuple = run_solver(solver); + auto jit_result_tuple = run_solver(jit_solver); + + std::cout << "Standard solve time: " << std::get<2>(result_tuple).count() << " nanoseconds" << std::endl; + std::cout << "JIT solve time: " << std::get<2>(jit_result_tuple).count() << " nanoseconds" << std::endl; + + auto result_stats = std::get<1>(result_tuple); + std::cout << "Standard solve stats: " << std::endl; + std::cout << "\taccepted: " << result_stats.accepted << std::endl; + std::cout << "\tfunction_calls: " << result_stats.function_calls << std::endl; + std::cout << "\tjacobian_updates: " << result_stats.jacobian_updates << std::endl; + std::cout << "\tnumber_of_steps: " << result_stats.number_of_steps << std::endl; + std::cout << "\taccepted: " << result_stats.accepted << std::endl; + std::cout << "\trejected: " << result_stats.rejected << std::endl; + std::cout << "\tdecompositions: " << result_stats.decompositions << std::endl; + std::cout << "\tsolves: " << result_stats.solves << std::endl; + std::cout << "\tsingular: " << result_stats.singular << std::endl; + std::cout << "\ttotal_update_state_time: " << result_stats.total_update_state_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_forcing_time: " << result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_jacobian_time: " << result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_linear_factor_time: " << result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl << std::endl; + + auto jit_result_stats = std::get<1>(jit_result_tuple); + std::cout << "JIT solve stats: " << std::endl; + std::cout << "\taccepted: " << jit_result_stats.accepted << std::endl; + std::cout << "\tfunction_calls: " << jit_result_stats.function_calls << std::endl; + std::cout << "\tjacobian_updates: " << jit_result_stats.jacobian_updates << std::endl; + std::cout << "\tnumber_of_steps: " << jit_result_stats.number_of_steps << std::endl; + std::cout << "\taccepted: " << jit_result_stats.accepted << std::endl; + std::cout << "\trejected: " << jit_result_stats.rejected << std::endl; + std::cout << "\tdecompositions: " << jit_result_stats.decompositions << std::endl; + std::cout << "\tsolves: " << jit_result_stats.solves << std::endl; + std::cout << "\tsingular: " << jit_result_stats.singular << std::endl; + std::cout << "\ttotal_update_state_time: " << jit_result_stats.total_update_state_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_forcing_time: " << jit_result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_jacobian_time: " << jit_result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_linear_factor_time: " << jit_result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; - std::cout << "total_linear_solve_time: " << jit_result_stats.total_linear_solve_time.count() << " nanoseconds" + std::cout << "\ttotal_linear_solve_time: " << jit_result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl; - auto result = result_pair.first; - auto jit_result = result_pair.first; + auto result = std::get<0>(result_tuple); + auto jit_result = std::get<0>(jit_result_tuple); for (auto& species : result.variable_names_) { for (int i = 0; i < n_grid_cells; ++i) { - if (result.variables_[i][result.variable_map_[species]] != jit_result.variables_[i][jit_result.variable_map_[species]]) + double a = result.variables_[i][result.variable_map_[species]]; + double b = jit_result.variables_[i][jit_result.variable_map_[species]]; + if ( std::abs(a - b) > 1.0e-5 * (std::abs(a) + std::abs(b)) / 2.0 + 1.0e-30 ) { - std::cout << species << " do not match final concentration" << std::endl; + std::cout << species << " does not match final concentration" << std::endl; } } } diff --git a/test/unit/util/test_matrix_policy.hpp b/test/unit/util/test_matrix_policy.hpp index 666e58a55..36e7def9b 100644 --- a/test/unit/util/test_matrix_policy.hpp +++ b/test/unit/util/test_matrix_policy.hpp @@ -224,10 +224,10 @@ MatrixPolicy testForEach() sum2 += i * 10.3 + j * 100.5 + i * 1.7 + j * 10.2 - i * 19.5 - j * 32.2; } - matrix.ForEach([&](double a, double b) { result += a + b; }, other); + matrix.ForEach([&](double &a, const double &b) { result += a + b; }, other); EXPECT_NEAR(sum, result, 1.0e-5); result = 0.0; - matrix.ForEach([&](double a, double b, double c) { result += a + b - c; }, other, other2); + matrix.ForEach([&](double &a, const double &b, const double &c) { result += a + b - c; }, other, other2); EXPECT_NEAR(sum2, result, 1.0e-5); return matrix; From af9c9163aa7a9a5200b6f0ab074fe6159a6f6cdc Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Sun, 5 Nov 2023 08:45:02 -0800 Subject: [PATCH 220/318] simplify jit tutorial chemical system --- test/tutorial/test_jit_tutorial.cpp | 82 ++++++++++++----------------- 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index 47781137a..e18482cb4 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -29,41 +29,9 @@ auto run_solver(auto& solver) state.conditions_[i].pressure_ = 101319.9; // Pa state.conditions_[i].air_density_ = 1e6; // mol m-3 } - - state.SetCustomRateParameter("EMIS.NO", std::vector(solver.parameters_.number_of_grid_cells_, 1.44e-10)); - state.SetCustomRateParameter("EMIS.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.56e-12)); - state.SetCustomRateParameter("EMIS.CO", std::vector(solver.parameters_.number_of_grid_cells_, 1.96e-09)); - state.SetCustomRateParameter("EMIS.SO2", std::vector(solver.parameters_.number_of_grid_cells_, 1.06e-09)); - state.SetCustomRateParameter("EMIS.FORM", std::vector(solver.parameters_.number_of_grid_cells_, 1.02e-11)); - state.SetCustomRateParameter("EMIS.MEOH", std::vector(solver.parameters_.number_of_grid_cells_, 5.92e-13)); - state.SetCustomRateParameter("EMIS.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 4.25e-12)); - state.SetCustomRateParameter("EMIS.PAR", std::vector(solver.parameters_.number_of_grid_cells_, 4.27e-10)); - state.SetCustomRateParameter("EMIS.ETH", std::vector(solver.parameters_.number_of_grid_cells_, 4.62e-11)); - state.SetCustomRateParameter("EMIS.OLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); - state.SetCustomRateParameter("EMIS.IOLE", std::vector(solver.parameters_.number_of_grid_cells_, 1.49e-11)); - state.SetCustomRateParameter("EMIS.TOL", std::vector(solver.parameters_.number_of_grid_cells_, 1.53e-11)); - state.SetCustomRateParameter("EMIS.XYL", std::vector(solver.parameters_.number_of_grid_cells_, 1.4e-11)); - state.SetCustomRateParameter("EMIS.ISOP", std::vector(solver.parameters_.number_of_grid_cells_, 6.03e-12)); - state.SetCustomRateParameter("PHOTO.NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.00477)); - state.SetCustomRateParameter("PHOTO.O3->O1D", std::vector(solver.parameters_.number_of_grid_cells_, 2.26e-06)); - state.SetCustomRateParameter("PHOTO.O3->O3P", std::vector(solver.parameters_.number_of_grid_cells_, 0.000253)); - state.SetCustomRateParameter("PHOTO.NO3->NO2", std::vector(solver.parameters_.number_of_grid_cells_, 0.117)); - state.SetCustomRateParameter("PHOTO.NO3->NO", std::vector(solver.parameters_.number_of_grid_cells_, 0.0144)); - state.SetCustomRateParameter("PHOTO.HONO", std::vector(solver.parameters_.number_of_grid_cells_, 0.000918)); - state.SetCustomRateParameter("PHOTO.H2O2", std::vector(solver.parameters_.number_of_grid_cells_, 2.59e-06)); - state.SetCustomRateParameter("PHOTO.PNA", std::vector(solver.parameters_.number_of_grid_cells_, 1.89e-06)); - state.SetCustomRateParameter("PHOTO.HNO3", std::vector(solver.parameters_.number_of_grid_cells_, 8.61e-08)); - state.SetCustomRateParameter("PHOTO.NTR", std::vector(solver.parameters_.number_of_grid_cells_, 4.77e-07)); - state.SetCustomRateParameter("PHOTO.ROOH", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); - state.SetCustomRateParameter("PHOTO.MEPX", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); - state.SetCustomRateParameter("PHOTO.FORM->HO2", std::vector(solver.parameters_.number_of_grid_cells_, 7.93e-06)); - state.SetCustomRateParameter("PHOTO.FORM->CO", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-05)); - state.SetCustomRateParameter("PHOTO.ALD2", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); - state.SetCustomRateParameter("PHOTO.PACD", std::vector(solver.parameters_.number_of_grid_cells_, 1.81e-06)); - state.SetCustomRateParameter("PHOTO.ALDX", std::vector(solver.parameters_.number_of_grid_cells_, 2.2e-06)); - state.SetCustomRateParameter("PHOTO.OPEN", std::vector(solver.parameters_.number_of_grid_cells_, 0.000645)); - state.SetCustomRateParameter("PHOTO.MGLY", std::vector(solver.parameters_.number_of_grid_cells_, 7.64e-05)); - state.SetCustomRateParameter("PHOTO.ISPD", std::vector(solver.parameters_.number_of_grid_cells_, 1.98e-09)); + auto foo = Species("Foo"); + std::vector foo_conc(n_grid_cells, 1.0); + state.SetConcentration(foo, foo_conc); // choose a timestep and print the initial state double time_step = 500; // s @@ -105,19 +73,27 @@ auto run_solver(auto& solver) int main(const int argc, const char* argv[]) { - SolverConfig solverConfig; + auto foo = Species{ "Foo" }; + auto bar = Species{ "Bar" }; + auto baz = Species{ "Baz" }; - std::string config_path = "./configs/carbon_bond_5"; - ConfigParseStatus status = solverConfig.ReadAndParse(config_path); - if (status != micm::ConfigParseStatus::Success) - { - throw "Parsing failed"; - } + Phase gas_phase{ std::vector{ foo, bar, baz } }; + + System chemical_system{ SystemParameters{ .gas_phase_ = gas_phase } }; + + Process r1 = Process::create() + .reactants({ foo }) + .products({ Yield(bar, 0.8), Yield(baz, 0.2) }) + .rate_constant(ArrheniusRateConstant({ .A_ = 1.0e-3 })) + .phase(gas_phase); - micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + Process r2 = Process::create() + .reactants({ foo, bar }) + .products({ Yield(baz, 1) }) + .rate_constant(ArrheniusRateConstant({ .A_ = 1.0e-5, .C_ = 110.0 })) + .phase(gas_phase); - auto chemical_system = solver_params.system_; - auto reactions = solver_params.processes_; + std::vector reactions{ r1, r2 }; auto solver_parameters = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(n_grid_cells); @@ -140,6 +116,11 @@ int main(const int argc, const char* argv[]) auto result_tuple = run_solver(solver); auto jit_result_tuple = run_solver(jit_solver); + // Rerun for more fair comparison after assumed improvements to + // branch-prediction during state update + result_tuple = run_solver(solver); + jit_result_tuple = run_solver(jit_solver); + std::cout << "Standard solve time: " << std::get<2>(result_tuple).count() << " nanoseconds" << std::endl; std::cout << "JIT solve time: " << std::get<2>(jit_result_tuple).count() << " nanoseconds" << std::endl; @@ -157,8 +138,10 @@ int main(const int argc, const char* argv[]) std::cout << "\ttotal_update_state_time: " << result_stats.total_update_state_time.count() << " nanoseconds" << std::endl; std::cout << "\ttotal_forcing_time: " << result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; std::cout << "\ttotal_jacobian_time: " << result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; - std::cout << "\ttotal_linear_factor_time: " << result_stats.total_linear_factor_time.count() << " nanoseconds" << std::endl; - std::cout << "\ttotal_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl << std::endl; + std::cout << "\ttotal_linear_factor_time: " << result_stats.total_linear_factor_time.count() << " nanoseconds" + << std::endl; + std::cout << "\ttotal_linear_solve_time: " << result_stats.total_linear_solve_time.count() << " nanoseconds" << std::endl + << std::endl; auto jit_result_stats = std::get<1>(jit_result_tuple); std::cout << "JIT solve stats: " << std::endl; @@ -171,7 +154,8 @@ int main(const int argc, const char* argv[]) std::cout << "\tdecompositions: " << jit_result_stats.decompositions << std::endl; std::cout << "\tsolves: " << jit_result_stats.solves << std::endl; std::cout << "\tsingular: " << jit_result_stats.singular << std::endl; - std::cout << "\ttotal_update_state_time: " << jit_result_stats.total_update_state_time.count() << " nanoseconds" << std::endl; + std::cout << "\ttotal_update_state_time: " << jit_result_stats.total_update_state_time.count() << " nanoseconds" + << std::endl; std::cout << "\ttotal_forcing_time: " << jit_result_stats.total_forcing_time.count() << " nanoseconds" << std::endl; std::cout << "\ttotal_jacobian_time: " << jit_result_stats.total_jacobian_time.count() << " nanoseconds" << std::endl; std::cout << "\ttotal_linear_factor_time: " << jit_result_stats.total_linear_factor_time.count() << " nanoseconds" @@ -188,7 +172,7 @@ int main(const int argc, const char* argv[]) { double a = result.variables_[i][result.variable_map_[species]]; double b = jit_result.variables_[i][jit_result.variable_map_[species]]; - if ( std::abs(a - b) > 1.0e-5 * (std::abs(a) + std::abs(b)) / 2.0 + 1.0e-30 ) + if (std::abs(a - b) > 1.0e-5 * (std::abs(a) + std::abs(b)) / 2.0 + 1.0e-30) { std::cout << species << " does not match final concentration" << std::endl; } From 37e51571dd024e0577bfb4076f4b6666affaeb71 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Sun, 5 Nov 2023 09:49:12 -0800 Subject: [PATCH 221/318] add ability to handle JIT functions with the same name --- include/micm/jit/jit_compiler.hpp | 7 ++++ include/micm/jit/jit_function.hpp | 2 +- test/unit/jit/test_jit_function.cpp | 60 +++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 56daf9929..61eb7054f 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -52,6 +52,8 @@ namespace micm llvm::orc::JITDylib &main_lib_; + unsigned int name_uniquer_{0}; + public: JitCompiler( std::unique_ptr execution_session, @@ -124,6 +126,11 @@ namespace micm return execution_session_->lookup({ &main_lib_ }, mangle_(name.str())); } + std::string UniquifyName(const std::string &base_name) + { + return base_name + std::to_string(name_uniquer_++); + } + private: static llvm::Expected OptimizeModule( llvm::orc::ThreadSafeModule threadsafe_module, diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp index d8178d180..ef643117a 100644 --- a/include/micm/jit/jit_function.hpp +++ b/include/micm/jit/jit_function.hpp @@ -157,7 +157,7 @@ namespace micm JitFunction::JitFunction(JitFunctionBuilder& function_builder) : generated_(false), - name_(function_builder.name_), + name_(function_builder.compiler_->UniquifyName(function_builder.name_)), compiler_(function_builder.compiler_), context_(std::make_unique()), module_(std::make_unique(name_ + " module", *context_)), diff --git a/test/unit/jit/test_jit_function.cpp b/test/unit/jit/test_jit_function.cpp index 186062696..ac2d9c2bb 100644 --- a/test/unit/jit/test_jit_function.cpp +++ b/test/unit/jit/test_jit_function.cpp @@ -379,4 +379,64 @@ TEST(JitFunction, LocalArray) EXPECT_EQ(20, func_ptr(4)); EXPECT_EQ(5, func_ptr(1)); func.exit_on_error_(foo_target.first->remove()); +} + +// This test creates several functions with the same name that +// add a unique number to the function argument and return the sum +TEST(JitFunction, SameNameFunctions) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error] "); + EXPECT_TRUE(false); + } + micm::JitFunction func1 = micm::JitFunction::create(jit.get()) + .name("foobar") + .arguments({ { "foo", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); + llvm::Value *const_val = llvm::ConstantInt::get(*(func1.context_), llvm::APInt(64, 2)); + llvm::Value *ret_val = func1.builder_->CreateNSWAdd(func1.arguments_[0].ptr_, const_val, "add args"); + func1.builder_->CreateRet(ret_val); + + auto func1_target = func1.Generate(); + int32_t (*func1_ptr)(int32_t) = (int32_t(*)(int32_t))(intptr_t)func1_target.second; + + micm::JitFunction func2 = micm::JitFunction::create(jit.get()) + .name("foobar") + .arguments({ { "foo", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); + const_val = llvm::ConstantInt::get(*(func2.context_), llvm::APInt(64, 12)); + ret_val = func2.builder_->CreateNSWAdd(func2.arguments_[0].ptr_, const_val, "add args"); + func2.builder_->CreateRet(ret_val); + + auto func2_target = func2.Generate(); + int32_t (*func2_ptr)(int32_t) = (int32_t(*)(int32_t))(intptr_t)func2_target.second; + + micm::JitFunction func3 = micm::JitFunction::create(jit.get()) + .name("foobar") + .arguments({ { "foo", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); + const_val = llvm::ConstantInt::get(*(func3.context_), llvm::APInt(64, 24)); + ret_val = func3.builder_->CreateNSWAdd(func3.arguments_[0].ptr_, const_val, "add args"); + func3.builder_->CreateRet(ret_val); + + auto func3_target = func3.Generate(); + int32_t (*func3_ptr)(int32_t) = (int32_t(*)(int32_t))(intptr_t)func3_target.second; + + EXPECT_EQ(10, func1_ptr(8)); + EXPECT_EQ(-6, func1_ptr(-8)); + EXPECT_EQ(82, func1_ptr(80)); + + EXPECT_EQ(20, func2_ptr(8)); + EXPECT_EQ(4, func2_ptr(-8)); + EXPECT_EQ(92, func2_ptr(80)); + + EXPECT_EQ(32, func3_ptr(8)); + EXPECT_EQ(16, func3_ptr(-8)); + EXPECT_EQ(104, func3_ptr(80)); + + func1.exit_on_error_(func1_target.first->remove()); + func2.exit_on_error_(func2_target.first->remove()); + func3.exit_on_error_(func3_target.first->remove()); } \ No newline at end of file From 95701167c42ed94f42ea0abf09b906789d5f9722 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Sun, 5 Nov 2023 10:30:33 -0800 Subject: [PATCH 222/318] use random string generator for function names --- include/micm/jit/jit_compiler.hpp | 7 ------- include/micm/jit/jit_function.hpp | 4 +++- include/micm/solver/jit_rosenbrock.hpp | 4 +--- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 61eb7054f..56daf9929 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -52,8 +52,6 @@ namespace micm llvm::orc::JITDylib &main_lib_; - unsigned int name_uniquer_{0}; - public: JitCompiler( std::unique_ptr execution_session, @@ -126,11 +124,6 @@ namespace micm return execution_session_->lookup({ &main_lib_ }, mangle_(name.str())); } - std::string UniquifyName(const std::string &base_name) - { - return base_name + std::to_string(name_uniquer_++); - } - private: static llvm::Expected OptimizeModule( llvm::orc::ThreadSafeModule threadsafe_module, diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp index ef643117a..0cdf8a377 100644 --- a/include/micm/jit/jit_function.hpp +++ b/include/micm/jit/jit_function.hpp @@ -9,6 +9,8 @@ #include #include +#include + #include "jit_compiler.hpp" #include "llvm/IR/Attributes.h" #include "llvm/IR/BasicBlock.h" @@ -157,7 +159,7 @@ namespace micm JitFunction::JitFunction(JitFunctionBuilder& function_builder) : generated_(false), - name_(function_builder.compiler_->UniquifyName(function_builder.name_)), + name_(function_builder.name_ + generate_random_string()), compiler_(function_builder.compiler_), context_(std::make_unique()), module_(std::make_unique(name_ + " module", *context_)), diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index a8a307f05..a651d144a 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -126,11 +126,9 @@ namespace micm std::size_t n_cells = jacobian.GroupVectorSize(); std::size_t number_of_nonzero_jacobian_elements = jacobian.AsVector().size(); - std::string function_name = "alpha_minus_jacobian_" + generate_random_string(); - // Create the JitFunction with the modified name JitFunction func = JitFunction::create(compiler_) - .name(function_name) + .name("alpha_minus_jacobian") .arguments({ { "jacobian", JitType::DoublePtr }, { "alpha", JitType::Double } }) .return_type(JitType::Void); From f3d9b96bdcf8bc05bc4a9955f24d1029be0acdc8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 6 Nov 2023 07:51:19 -0800 Subject: [PATCH 223/318] Auto-format code changes (#342) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/jit/jit_compiler.hpp | 1 - include/micm/jit/jit_function.hpp | 1 - include/micm/solver/jit_rosenbrock.hpp | 3 +- include/micm/solver/rosenbrock.hpp | 3 +- include/micm/util/random_string.hpp | 2 +- .../regression/RosenbrockChapman/jit_util.hpp | 70 +++++++++++++----- test/unit/jit/test_jit_function.cpp | 18 ++--- test/unit/solver/test_jit_rosenbrock.cpp | 74 +++++++++---------- test/unit/util/test_matrix_policy.hpp | 6 +- 9 files changed, 100 insertions(+), 78 deletions(-) diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 56daf9929..5c2180879 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -29,7 +29,6 @@ #include "llvm/Support/TargetSelect.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/InstCombine/InstCombine.h" -#include "llvm/Transforms/IPO.h" #include "llvm/Transforms/Scalar.h" #include "llvm/Transforms/Scalar/GVN.h" #include "llvm/Transforms/Utils.h" diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp index 0cdf8a377..1a71a965e 100644 --- a/include/micm/jit/jit_function.hpp +++ b/include/micm/jit/jit_function.hpp @@ -8,7 +8,6 @@ #include #include #include - #include #include "jit_compiler.hpp" diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index a651d144a..633fca4d8 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -78,7 +78,8 @@ namespace micm [&](const SparseMatrixPolicy& matrix, double initial_value) -> LinearSolverPolicy { return LinearSolverPolicy{ compiler, matrix, initial_value }; }, - [&](const std::vector& processes, const std::map& variable_map) -> ProcessSetPolicy { + [&](const std::vector& processes, + const std::map& variable_map) -> ProcessSetPolicy { return ProcessSetPolicy{ compiler, processes, variable_map }; }), compiler_(compiler) diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 5fa83e440..9322180aa 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -148,7 +148,8 @@ namespace micm const std::vector& processes, const RosenbrockSolverParameters& parameters, const std::function, double)> create_linear_solver, - const std::function&, const std::map&)> create_process_set); + const std::function&, const std::map&)> + create_process_set); virtual ~RosenbrockSolver() = default; diff --git a/include/micm/util/random_string.hpp b/include/micm/util/random_string.hpp index c48e7a1aa..b3f011107 100644 --- a/include/micm/util/random_string.hpp +++ b/include/micm/util/random_string.hpp @@ -3,9 +3,9 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include #include #include +#include namespace micm { diff --git a/test/regression/RosenbrockChapman/jit_util.hpp b/test/regression/RosenbrockChapman/jit_util.hpp index b1ed1770f..c1da846d5 100644 --- a/test/regression/RosenbrockChapman/jit_util.hpp +++ b/test/regression/RosenbrockChapman/jit_util.hpp @@ -2,9 +2,15 @@ #include "util.hpp" -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> -micm::JitRosenbrockSolver getTwoStageMultiCellJitChapmanSolver( - const size_t number_of_grid_cells) +template< + template + class MatrixPolicy, + template + class SparseMatrixPolicy, + class LinearSolverPolicy, + class ProcessSetPolicy> +micm::JitRosenbrockSolver +getTwoStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); @@ -15,16 +21,22 @@ micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> -micm::JitRosenbrockSolver getThreeStageMultiCellJitChapmanSolver( - const size_t number_of_grid_cells) +template< + template + class MatrixPolicy, + template + class SparseMatrixPolicy, + class LinearSolverPolicy, + class ProcessSetPolicy> +micm::JitRosenbrockSolver +getThreeStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); @@ -35,16 +47,22 @@ micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> -micm::JitRosenbrockSolver getFourStageMultiCellJitChapmanSolver( - const size_t number_of_grid_cells) +template< + template + class MatrixPolicy, + template + class SparseMatrixPolicy, + class LinearSolverPolicy, + class ProcessSetPolicy> +micm::JitRosenbrockSolver +getFourStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); @@ -55,16 +73,22 @@ micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> -micm::JitRosenbrockSolver getFourStageDAMultiCellJitChapmanSolver( - const size_t number_of_grid_cells) +template< + template + class MatrixPolicy, + template + class SparseMatrixPolicy, + class LinearSolverPolicy, + class ProcessSetPolicy> +micm::JitRosenbrockSolver +getFourStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); @@ -75,16 +99,22 @@ micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); } -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class ProcessSetPolicy> -micm::JitRosenbrockSolver getSixStageDAMultiCellJitChapmanSolver( - const size_t number_of_grid_cells) +template< + template + class MatrixPolicy, + template + class SparseMatrixPolicy, + class LinearSolverPolicy, + class ProcessSetPolicy> +micm::JitRosenbrockSolver +getSixStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) { micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); @@ -95,7 +125,7 @@ micm::JitRosenbrockSolver( + return micm::JitRosenbrockSolver( jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), diff --git a/test/unit/jit/test_jit_function.cpp b/test/unit/jit/test_jit_function.cpp index ac2d9c2bb..26b0bec7f 100644 --- a/test/unit/jit/test_jit_function.cpp +++ b/test/unit/jit/test_jit_function.cpp @@ -392,9 +392,9 @@ TEST(JitFunction, SameNameFunctions) EXPECT_TRUE(false); } micm::JitFunction func1 = micm::JitFunction::create(jit.get()) - .name("foobar") - .arguments({ { "foo", micm::JitType::Int32 } }) - .return_type(micm::JitType::Int32); + .name("foobar") + .arguments({ { "foo", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); llvm::Value *const_val = llvm::ConstantInt::get(*(func1.context_), llvm::APInt(64, 2)); llvm::Value *ret_val = func1.builder_->CreateNSWAdd(func1.arguments_[0].ptr_, const_val, "add args"); func1.builder_->CreateRet(ret_val); @@ -403,9 +403,9 @@ TEST(JitFunction, SameNameFunctions) int32_t (*func1_ptr)(int32_t) = (int32_t(*)(int32_t))(intptr_t)func1_target.second; micm::JitFunction func2 = micm::JitFunction::create(jit.get()) - .name("foobar") - .arguments({ { "foo", micm::JitType::Int32 } }) - .return_type(micm::JitType::Int32); + .name("foobar") + .arguments({ { "foo", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); const_val = llvm::ConstantInt::get(*(func2.context_), llvm::APInt(64, 12)); ret_val = func2.builder_->CreateNSWAdd(func2.arguments_[0].ptr_, const_val, "add args"); func2.builder_->CreateRet(ret_val); @@ -414,9 +414,9 @@ TEST(JitFunction, SameNameFunctions) int32_t (*func2_ptr)(int32_t) = (int32_t(*)(int32_t))(intptr_t)func2_target.second; micm::JitFunction func3 = micm::JitFunction::create(jit.get()) - .name("foobar") - .arguments({ { "foo", micm::JitType::Int32 } }) - .return_type(micm::JitType::Int32); + .name("foobar") + .arguments({ { "foo", micm::JitType::Int32 } }) + .return_type(micm::JitType::Int32); const_val = llvm::ConstantInt::get(*(func3.context_), llvm::APInt(64, 24)); ret_val = func3.builder_->CreateNSWAdd(func3.arguments_[0].ptr_, const_val, "add args"); func3.builder_->CreateRet(ret_val); diff --git a/test/unit/solver/test_jit_rosenbrock.cpp b/test/unit/solver/test_jit_rosenbrock.cpp index b69a0c3b2..3d27db0e4 100644 --- a/test/unit/solver/test_jit_rosenbrock.cpp +++ b/test/unit/solver/test_jit_rosenbrock.cpp @@ -11,7 +11,11 @@ #include template class MatrixPolicy, template class SparseMatrixPolicy> -micm::JitRosenbrockSolver, micm::JitProcessSet> +micm::JitRosenbrockSolver< + MatrixPolicy, + SparseMatrixPolicy, + micm::JitLinearSolver, + micm::JitProcessSet> getSolver(std::shared_ptr jit) { // ---- foo bar baz quz quuz @@ -44,12 +48,15 @@ getSolver(std::shared_ptr jit) micm::Process r3 = micm::Process::create().reactants({ quz }).products({}).phase(gas_phase).rate_constant( micm::ArrheniusRateConstant({ .A_ = 3.5e-6 })); - return micm:: - JitRosenbrockSolver, micm::JitProcessSet>( - jit, - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, false)); + return micm::JitRosenbrockSolver< + MatrixPolicy, + SparseMatrixPolicy, + micm::JitLinearSolver, + micm::JitProcessSet>( + jit, + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, false)); } template @@ -180,40 +187,25 @@ TEST(JitRosenbrockSolver, MultipleInstances) auto solver_parameters = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(); micm::JitRosenbrockSolver< - Group1VectorMatrix, - Group1SparseVectorMatrix, - micm::JitLinearSolver<1, Group1SparseVectorMatrix>, - micm::JitProcessSet<1> - > solver1( - jit.get(), - chemical_system, - reactions, - solver_parameters - ); + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1>> + solver1(jit.get(), chemical_system, reactions, solver_parameters); micm::JitRosenbrockSolver< - Group1VectorMatrix, - Group1SparseVectorMatrix, - micm::JitLinearSolver<1, Group1SparseVectorMatrix>, - micm::JitProcessSet<1> - > solver2( - jit.get(), - chemical_system, - reactions, - solver_parameters - ); + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1>> + solver2(jit.get(), chemical_system, reactions, solver_parameters); micm::JitRosenbrockSolver< - Group1VectorMatrix, - Group1SparseVectorMatrix, - micm::JitLinearSolver<1, Group1SparseVectorMatrix>, - micm::JitProcessSet<1> - > solver3( - jit.get(), - chemical_system, - reactions, - solver_parameters - ); - - run_solver(solver1); - run_solver(solver2); - run_solver(solver3); + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1>> + solver3(jit.get(), chemical_system, reactions, solver_parameters); + + run_solver(solver1); + run_solver(solver2); + run_solver(solver3); } diff --git a/test/unit/util/test_matrix_policy.hpp b/test/unit/util/test_matrix_policy.hpp index 36e7def9b..ba7b20dc6 100644 --- a/test/unit/util/test_matrix_policy.hpp +++ b/test/unit/util/test_matrix_policy.hpp @@ -14,7 +14,7 @@ MatrixPolicy testSmallMatrix() EXPECT_EQ(matrix[0][0], 41.2); EXPECT_EQ(matrix[2][4], 102.3); - std::vector& data = matrix.AsVector(); + std::vector &data = matrix.AsVector(); EXPECT_GE(data.size(), 3); @@ -36,7 +36,7 @@ const MatrixPolicy testSmallConstMatrix() EXPECT_EQ(const_matrix[0][0], 41.2); EXPECT_EQ(const_matrix[2][4], 102.3); - const std::vector& data = const_matrix.AsVector(); + const std::vector &data = const_matrix.AsVector(); EXPECT_GE(data.size(), 3); @@ -240,7 +240,7 @@ MatrixPolicy testSetScalar() matrix = 2.0; - for (auto& elem : matrix.AsVector()) + for (auto &elem : matrix.AsVector()) { EXPECT_EQ(elem, 2.0); } From 074a29e0e6cb7275c2bea8577de5760af44c8dde Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 6 Nov 2023 08:54:21 -0800 Subject: [PATCH 224/318] Update README.md Update README example for new version of MICM --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7cd4a6db1..4836956df 100644 --- a/README.md +++ b/README.md @@ -158,7 +158,7 @@ int main(const int argc, const char *argv[]) To build and run the example using GNU (assuming the default install location): ``` -g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.1.0/include -std=c++20 +g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.2.0/include -std=c++20 ./foo_chem ``` From 1effd61d78c80358c1ae0a0af5eaca6401950b71 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 6 Nov 2023 09:47:44 -0800 Subject: [PATCH 225/318] add build instructions to JIT tutorial --- docs/source/user_guide/jit.rst | 18 +++++++++--------- test/tutorial/test_jit_tutorial.cpp | 1 - 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst index f91a28753..b5bf8fed5 100644 --- a/docs/source/user_guide/jit.rst +++ b/docs/source/user_guide/jit.rst @@ -41,19 +41,19 @@ the appropriate tab below and be on your way! Otherwise, stick around for a line .. tab-set:: - .. tab-item:: OpenAtmos Configuration reading - - .. raw:: html - - + .. tab-item:: JIT-compiled Rosenbrock Solver .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp +To build and run the example using GNU (assuming the default install location), copy and +paste the example code into a file named ``foo_jit_chem.cpp`` and run: + +.. code:: bash + + g++ -o foo_jit_chem foo_jit_chem.cpp -I/usr/local/micm-3.2.0/include -std=c++20 `llvm-config --cxxflags --ldflags --system-libs --libs support core orcjit native irreader` -std=c++20 -fexceptions + ./foo_jit_chem + Line-by-line explanation ------------------------ diff --git a/test/tutorial/test_jit_tutorial.cpp b/test/tutorial/test_jit_tutorial.cpp index e18482cb4..6bfe32032 100644 --- a/test/tutorial/test_jit_tutorial.cpp +++ b/test/tutorial/test_jit_tutorial.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include From 5def946e343fb4ebae8d62d948bac08e418b2f3b Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 6 Nov 2023 09:51:31 -0800 Subject: [PATCH 226/318] update build instructions to JIT tutorial --- .devcontainer/devcontainer.json | 2 +- docs/source/user_guide/jit.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index eccf8125f..6f3312dcb 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -6,5 +6,5 @@ ], "settings": { }, - "postCreateCommand": "cd /workspaces/micm && mkdir build && cd build && cmake -D CMAKE_BUILD_TYPE=debug -D ENABLE_CLANG_TIDY:BOOL=FALSE -D ENABLE_LLVM:BOOL=TRUE -D ENABLE_MEMCHECK:BOOL=TRUE .. && make install -j 2" + "postCreateCommand": "cd /workspaces/micm && mkdir build && cd build && cmake -D CMAKE_BUILD_TYPE=release -D ENABLE_CLANG_TIDY:BOOL=FALSE -D ENABLE_LLVM:BOOL=TRUE -D ENABLE_MEMCHECK:BOOL=TRUE .. && make install -j 2" } diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst index b5bf8fed5..d9abc1678 100644 --- a/docs/source/user_guide/jit.rst +++ b/docs/source/user_guide/jit.rst @@ -51,7 +51,7 @@ paste the example code into a file named ``foo_jit_chem.cpp`` and run: .. code:: bash - g++ -o foo_jit_chem foo_jit_chem.cpp -I/usr/local/micm-3.2.0/include -std=c++20 `llvm-config --cxxflags --ldflags --system-libs --libs support core orcjit native irreader` -std=c++20 -fexceptions + g++ -o foo_jit_chem foo_jit_chem.cpp -I/usr/local/micm-3.2.0/include `llvm-config --cxxflags --ldflags --system-libs --libs support core orcjit native irreader` -std=c++20 -fexceptions ./foo_jit_chem Line-by-line explanation From 173467800a77a741c2bc8ad31d961a06a2cbf02f Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 6 Nov 2023 17:00:04 -0700 Subject: [PATCH 227/318] adding jit process set in openmp example (#344) --- test/unit/openmp/test_openmp_jit_solver.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/unit/openmp/test_openmp_jit_solver.cpp b/test/unit/openmp/test_openmp_jit_solver.cpp index 0044cea35..c9cd1219f 100644 --- a/test/unit/openmp/test_openmp_jit_solver.cpp +++ b/test/unit/openmp/test_openmp_jit_solver.cpp @@ -36,8 +36,12 @@ TEST(OpenMP, JITOneSolverManyStates) std::vector> results(n_threads); auto jit{ micm::JitCompiler::create() }; - JitRosenbrockSolver> solver( - jit.get(), chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters()); + JitRosenbrockSolver< + Group1VectorMatrix, + Group1SparseVectorMatrix, + JitLinearSolver<1, Group1SparseVectorMatrix>, + micm::JitProcessSet<1>> + solver(jit.get(), chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters()); #pragma omp parallel num_threads(n_threads) { From 2557bed849d5336abaf3a9da363c4db063135999 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Tue, 7 Nov 2023 07:06:50 -0800 Subject: [PATCH 228/318] update line numbers in JIT tutorial --- docs/source/user_guide/jit.rst | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst index d9abc1678..a0a380685 100644 --- a/docs/source/user_guide/jit.rst +++ b/docs/source/user_guide/jit.rst @@ -9,12 +9,12 @@ acheive a numerically stable solution, and cache misses, among others. This tutorial focuses on alleviating the cache misses. A popular method for handling cache misses is to pre-compute the indices. This method, which may be referred to as ahead-of-time (AOT) compilation, is used in applications such as KPP :cite:`Damian2002`. -Pre-computed methods require code preprocessors and preclude runtime configurable software, which is a goal of micm. +Pre-computed methods require code preprocessors and preclude runtime configurable software, which is a goal of MICM. MICM uses just-in-time (JIT) compiled functions built with `LLVM JIT `_ libraries to supply runtime-configurable chemistry to avoid cache misses in important chemistry functions. -Up until now, a :cpp:class:`micm::RosenbrockSolver` has been used. This is a special class in micm which builds all +Up until now, a :cpp:class:`micm::RosenbrockSolver` has been used. This is a special class in MICM which builds all of the componenets needed to solve chemistry in memory, including the forcing function and the jacobian. MICM also provides a :cpp:class:`micm::JitRosenbrockSolver` which builds and compiles several important chemistry functions at runtime. @@ -32,8 +32,9 @@ So what does this gain me? -------------------------- Runtime configuraiton of chemical mechanisms that are *fast*. Let's compare the speed of :cpp:class:`micm::RosenbrockSolver` -and the :cpp:class:`micm::JitRosenbrockSolver` classes applied to the same problem. This time we'll use the carbon-bond 5 -mechanism :cite:`Yarwood2005`, which is an update to the carbon bond mechanism from :cite:`Gery1989`. +and the :cpp:class:`micm::JitRosenbrockSolver` classes applied to the same problem. We will use the simple +fictitous chemical system from previous examples for simplicity, but feel free to try out more complex +mechanisms to see the effects of JIT compiling on compute time in more realistic cases. If you're looking for a copy and paste, choose the appropriate tab below and be on your way! Otherwise, stick around for a line by line explanation. @@ -57,12 +58,11 @@ paste the example code into a file named ``foo_jit_chem.cpp`` and run: Line-by-line explanation ------------------------ -Starting with the header files, we need headers for timing, outpu, reading the configuration, -and of course for both types of solvers. +Starting with the header files, we need headers for timing, output, and of course for both types of solvers. .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp - :lines: 1-7 + :lines: 1-5 Next, we use our namespace, define our number of gridcells (1 for now), and some partial template specializations. We are using our custom vectorized matrix, which @@ -71,24 +71,23 @@ grid cells to be solved simultaneously. .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp - :lines: 14-18 + :lines: 7-16 Now, all at once, is the function which runs either type of solver. We set all species -concentrations to 1 :math:`\mathrm{mol\ m^-3}` and set the rate paramters for all of the -photolysis and emissions reactions. Additionally, we are collecting all of the solver -stats across all solving timesteps +concentrations to 1 :math:`\mathrm{mol\ m^-3}`. +Additionally, we are collecting all of the solver stats across all solving timesteps .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp - :lines: 20-96 + :lines: 18-71 Finally, the main function which reads the configuration and initializes the jit solver. .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp - :lines: 101-117 + :lines: 73-113 -The only additionall step here is to make an instance of the :cpp:class:`micm::JitCompiler` +The only additional step here is to make an instance of the :cpp:class:`micm::JitCompiler` and pass it as a shared pointer to the :cpp:class:`micm::JitRosenbrockSolver`. We also are using our vectorized matrix for both solvers. The :cpp:class:`micm::JitRosenbrockSolver` only works with the vectorized matrix whereas the :cpp:class:`micm::RosenbrockSolver` works with a regular matrix. @@ -97,14 +96,14 @@ time here. .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp - :lines: 119-140 + :lines: 103-113 Finally, we run both solvers, output their cumulative stats, and compare their results. .. literalinclude:: ../../../test/tutorial/test_jit_tutorial.cpp :language: cpp - :lines: 142-198 + :lines: 115-181 The output will be similar to this: From 9a4d75616ed25198cb30a669660d5805eee93b6b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 7 Nov 2023 17:16:12 -0700 Subject: [PATCH 229/318] Develop 272 ts1 (#340) * add initial ts1 example and test * add remaining unmapped reactions from TS1 * remove examples tests when JSON support is not available --------- Co-authored-by: Matt Dawson --- .dockerignore | 1 + examples/TS1/config.json | 6 + examples/TS1/reactions.json | 7130 +++++++++++++++++ examples/TS1/species.json | 1281 +++ include/micm/configure/solver_config.hpp | 54 +- include/micm/process/process_set.hpp | 4 + test/CMakeLists.txt | 3 + test/examples/CMakeLists.txt | 10 + test/examples/test_TS1.cpp | 216 + test/unit/configure/process/CMakeLists.txt | 2 +- .../process/test_user_defined_config.cpp | 83 + .../contains_nonstandard_key/config.json | 1 + .../contains_nonstandard_key/reactions.json | 17 + .../contains_nonstandard_key/species.json | 20 + .../contains_nonstandard_key/tolerance.json | 8 + .../missing_MUSICA_name/config.json | 1 + .../missing_MUSICA_name/reactions.json | 21 + .../missing_MUSICA_name/species.json | 16 + .../missing_MUSICA_name/tolerance.json | 8 + .../user_defined/missing_products/config.json | 1 + .../missing_products/reactions.json | 18 + .../missing_products/species.json | 16 + .../missing_products/tolerance.json | 8 + .../missing_reactants/config.json | 1 + .../missing_reactants/reactions.json | 18 + .../missing_reactants/species.json | 16 + .../missing_reactants/tolerance.json | 8 + .../nonstandard_product_coef/config.json | 1 + .../nonstandard_product_coef/reactions.json | 21 + .../nonstandard_product_coef/species.json | 20 + .../nonstandard_product_coef/tolerance.json | 8 + .../nonstandard_reactant_coef/config.json | 1 + .../nonstandard_reactant_coef/reactions.json | 21 + .../nonstandard_reactant_coef/species.json | 20 + .../nonstandard_reactant_coef/tolerance.json | 8 + .../process/user_defined/valid/config.json | 1 + .../process/user_defined/valid/reactions.json | 33 + .../process/user_defined/valid/species.json | 20 + .../process/user_defined/valid/tolerance.json | 8 + 39 files changed, 9127 insertions(+), 3 deletions(-) create mode 100644 examples/TS1/config.json create mode 100644 examples/TS1/reactions.json create mode 100644 examples/TS1/species.json create mode 100644 test/examples/CMakeLists.txt create mode 100644 test/examples/test_TS1.cpp create mode 100644 test/unit/configure/process/test_user_defined_config.cpp create mode 100644 test/unit/unit_configs/process/user_defined/contains_nonstandard_key/config.json create mode 100644 test/unit/unit_configs/process/user_defined/contains_nonstandard_key/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/contains_nonstandard_key/species.json create mode 100644 test/unit/unit_configs/process/user_defined/contains_nonstandard_key/tolerance.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_MUSICA_name/config.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_MUSICA_name/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_MUSICA_name/species.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_MUSICA_name/tolerance.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_products/config.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_products/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_products/species.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_products/tolerance.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_reactants/config.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_reactants/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_reactants/species.json create mode 100644 test/unit/unit_configs/process/user_defined/missing_reactants/tolerance.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_product_coef/config.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_product_coef/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_product_coef/species.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_product_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/config.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/species.json create mode 100644 test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/tolerance.json create mode 100644 test/unit/unit_configs/process/user_defined/valid/config.json create mode 100644 test/unit/unit_configs/process/user_defined/valid/reactions.json create mode 100644 test/unit/unit_configs/process/user_defined/valid/species.json create mode 100644 test/unit/unit_configs/process/user_defined/valid/tolerance.json diff --git a/.dockerignore b/.dockerignore index 496c56379..810bb6789 100644 --- a/.dockerignore +++ b/.dockerignore @@ -8,6 +8,7 @@ !libs/ !test/ !etc/ +!examples/ !cmake/ !packaging !include/ diff --git a/examples/TS1/config.json b/examples/TS1/config.json new file mode 100644 index 000000000..04d0ef289 --- /dev/null +++ b/examples/TS1/config.json @@ -0,0 +1,6 @@ +{ + "camp-files": [ + "species.json", + "reactions.json" + ] +} diff --git a/examples/TS1/reactions.json b/examples/TS1/reactions.json new file mode 100644 index 000000000..fb2bc1c43 --- /dev/null +++ b/examples/TS1/reactions.json @@ -0,0 +1,7130 @@ +{ + "__comments": [ "This mechanism may contain refactored reactions based on custom", + "rate constant functions in mo_usrrxt.F90 in the CAM source code.", + "As this file could change at any time, it is important that you", + "do an md5 checksum of mo_usrrxt.F90 from the version of CAM you", + "are using. The value should be: 9783938d6c5977f1c3603cadfe254987" ], + "camp-data": [ + { + "name": "MZ327_TS1.2_20230307", + "type": "MECHANISM", + "reactions": [ + { + "type": "TROE", + "k0_A": 5.5e-30, + "kinf_A": 8.3e-13, + "N": -2, + "reactants": { + "C2H2": { }, + "OH": { } + }, + "products": { + "GLYOXAL": { "yield": 0.65 }, + "OH": { "yield": 0.65 }, + "HCOOH": { "yield": 0.35 }, + "HO2": { "yield": 0.35 }, + "CO": { "yield": 0.35 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "Ea": -6.90325e-21, + "reactants": { + "CH3O2": { }, + "TERPO2": { } + }, + "products": { + "TERPROD1": { }, + "CH2O": { "yield": 0.95 }, + "CH3OH": { "yield": 0.25 }, + "HO2": { }, + "CH3COCH3": { "yield": 0.025 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "C3H7OOH": { }, + "OH": { } + }, + "products": { + "H2O": { }, + "C3H7O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jterpnit->,jch3ooh", + "reactants": { + "TERPNIT": { } + }, + "products": { + "TERPROD1": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.45e-12, + "Ea": -3.03743e-21, + "reactants": { + "N": { }, + "NO2": { } + }, + "products": { + "N2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.4e-11, + "Ea": 2.20904e-20, + "reactants": { + "O": { }, + "CH2O": { } + }, + "products": { + "HO2": { }, + "OH": { }, + "CO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "TOLUO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.1364 }, + "SOAG1": { "yield": 0.0101 }, + "SOAG2": { "yield": 0.0763 }, + "SOAG3": { "yield": 0.2157 }, + "SOAG4": { "yield": 0.0738 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jo2_b=userdefined,", + "reactants": { + "O2": { } + }, + "products": { + "O": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-12, + "Ea": 2.44375e-20, + "reactants": { + "OH": { }, + "HCFC142B": { } + }, + "products": { + "CL": { }, + "COF2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmgly", + "reactants": { + "CH3COCHO": { } + }, + "products": { + "CH3CO3": { }, + "CO": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.17e-10, + "reactants": { + "CFC114": { }, + "O1D": { } + }, + "products": { + "CL": { "yield": 2 }, + "COF2": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jxylenooh->,jch3ooh", + "reactants": { + "XYLENOOH": { } + }, + "products": { + "OH": { }, + "HO2": { }, + "GLYOXAL": { "yield": 0.34 }, + "CH3COCHO": { "yield": 0.54 }, + "BIGALD1": { "yield": 0.06 }, + "BIGALD2": { "yield": 0.2 }, + "BIGALD3": { "yield": 0.15 }, + "BIGALD4": { "yield": 0.21 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.1e-12, + "Ea": -3.72775e-21, + "reactants": { + "NO": { }, + "CH3CO3": { } + }, + "products": { + "CH3O2": { }, + "CO2": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "reactants": { + "HONITR": { }, + "OH": { } + }, + "products": { + "ONITR": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.12e-13, + "Ea": -1.79484e-20, + "reactants": { + "ISOPO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.0031 }, + "SOAG1": { "yield": 0.0035 }, + "SOAG2": { "yield": 0.0003 }, + "SOAG3": { "yield": 0.0271 }, + "SOAG4": { "yield": 0.0474 } + } + }, + { + "type": "TROE", + "k0_A": 1.07767, + "k0_B": -5.6, + "k0_C": -14000, + "kinf_A": 1.03323e+17, + "kinf_C": -14000, + "N": 1.5, + "reactants": { + "PAN": { } + }, + "products": { + "CH3CO3": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": -6.90325e-21, + "reactants": { + "CH3CO3": { "qty": 2 } + }, + "products": { + "CH3O2": { "yield": 2 }, + "CO2": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch4_b", + "reactants": { + "CH4": { } + }, + "products": { + "H2": { "yield": 1.44 }, + "CH2O": { "yield": 0.18 }, + "O": { "yield": 0.18 }, + "OH": { "yield": 0.33 }, + "H": { "yield": 0.33 }, + "CO2": { "yield": 0.44 }, + "CO": { "yield": 0.38 }, + "H2O": { "yield": 0.05 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.63e-10, + "Ea": -8.28389e-22, + "reactants": { + "H2O": { }, + "O1D": { } + }, + "products": { + "OH": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 6.0e-13, + "Ea": -3.17549e-21, + "reactants": { + "OH": { }, + "CLO": { } + }, + "products": { + "HCL": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-11, + "Ea": -6.07486e-21, + "reactants": { + "OH": { }, + "MTERP": { } + }, + "products": { + "MTERP": { }, + "OH": { }, + "MTERPO2VBS": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-10, + "reactants": { + "CH3BR": { }, + "O1D": { } + }, + "products": { + "BR": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jterp2ooh->,jch3ooh", + "reactants": { + "TERP2OOH": { } + }, + "products": { + "OH": { }, + "CH2O": { "yield": 0.375 }, + "CH3COCH3": { "yield": 0.3 }, + "CO": { "yield": 0.25 }, + "CO2": { }, + "TERPROD2": { }, + "HO2": { }, + "GLYALD": { "yield": 0.25 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.34e-11, + "reactants": { + "SVOC": { }, + "OH": { } + }, + "products": { + "OH": { }, + "SOAG0": { "yield": 0.5931 }, + "SOAG1": { "yield": 0.1534 }, + "SOAG2": { "yield": 0.0459 }, + "SOAG3": { "yield": 0.0085 }, + "SOAG4": { "yield": 0.0128 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "XYLENOOH": { }, + "OH": { } + }, + "products": { + "XYLENO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.9e-11, + "reactants": { + "HCL": { }, + "O1D": { } + }, + "products": { + "CL": { }, + "OH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbrono2_a", + "reactants": { + "BRONO2": { } + }, + "products": { + "BR": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.7e-11, + "reactants": { + "SO": { }, + "BRO": { } + }, + "products": { + "SO2": { }, + "BR": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "BENZO2": { } + }, + "products": { + "NO2": { }, + "GLYOXAL": { }, + "BIGALD1": { "yield": 0.5 }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "TOLO2": { }, + "NO": { } + }, + "products": { + "NO2": { }, + "GLYOXAL": { "yield": 0.6 }, + "CH3COCHO": { "yield": 0.4 }, + "HO2": { }, + "BIGALD1": { "yield": 0.2 }, + "BIGALD2": { "yield": 0.2 }, + "BIGALD3": { "yield": 0.2 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-10, + "reactants": { + "BCARY": { }, + "OH": { } + }, + "products": { + "BCARY": { }, + "OH": { }, + "BCARYO2VBS": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbigald4->,.006*jno2", + "reactants": { + "BIGALD4": { } + }, + "products": { + "HO2": { }, + "CO": { }, + "CH3COCHO": { }, + "CH3CO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.75e-13, + "Ea": 5.5226e-22, + "reactants": { + "CH3O2": { }, + "C3H7O2": { } + }, + "products": { + "CH2O": { }, + "HO2": { }, + "CH3COCH3": { "yield": 0.82 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.65e-11, + "reactants": { + "O1D": { }, + "HCFC22": { } + }, + "products": { + "CL": { }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-12, + "Ea": 2.3471e-21, + "reactants": { + "MEK": { }, + "OH": { } + }, + "products": { + "MEKO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.38889e+16, + "Ea": 8.40401e-20, + "reactants": { + "CL2O2": { }, + "M": { } + }, + "products": { + "CLO": { "yield": 2 }, + "M": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "TOLOOH": { } + }, + "products": { + "TOLO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa3_a2->,.0004*jno2", + "reactants": { + "soa3_a2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 2.14e-11, + "reactants": { + "COF2": { }, + "O1D": { } + }, + "products": { + "F": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.54e-11, + "Ea": -5.66066e-21, + "reactants": { + "ISOP": { }, + "OH": { } + }, + "products": { + "ISOPAO2": { "yield": 0.6 }, + "ISOPBO2": { "yield": 0.4 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.29e-7, + "reactants": { + "E90": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 6.3e-16, + "Ea": 8.00776e-21, + "reactants": { + "O3": { }, + "MTERP": { } + }, + "products": { + "MTERP": { }, + "O3": { }, + "SOAG0": { "yield": 0.0508 }, + "SOAG1": { "yield": 0.1149 }, + "SOAG2": { "yield": 0.0348 }, + "SOAG3": { "yield": 0.0554 }, + "SOAG4": { "yield": 0.1278 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbro", + "reactants": { + "BRO": { } + }, + "products": { + "BR": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "Ea": -6.90325e-21, + "reactants": { + "CH3O2": { }, + "MCO3": { } + }, + "products": { + "CH2O": { "yield": 2 }, + "HO2": { }, + "CO2": { }, + "CH3CO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jtepomuc->,.10*jno2", + "reactants": { + "TEPOMUC": { } + }, + "products": { + "CH3CO3": { "yield": 0.5 }, + "HO2": { }, + "CO": { "yield": 1.5 } + } + }, + { + "type": "TROE", + "k0_A": 9.7e-29, + "k0_B": -5.6, + "kinf_A": 9.3e-12, + "N": 1.5, + "reactants": { + "MDIALO2": { }, + "NO2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.96e-12, + "Ea": 1.65678e-20, + "reactants": { + "CH3CL": { }, + "OH": { } + }, + "products": { + "CL": { }, + "H2O": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcof2", + "reactants": { + "COF2": { } + }, + "products": { + "F": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch2o_b", + "reactants": { + "CH2O": { } + }, + "products": { + "CO": { }, + "H2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbrono2_b", + "reactants": { + "BRONO2": { } + }, + "products": { + "BRO": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.4e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "ISOPBO2": { } + }, + "products": { + "HYDRALD": { "yield": 0.87 }, + "ISOPNITB": { "yield": 0.08 }, + "NO2": { "yield": 0.92 }, + "HO2": { "yield": 0.92 }, + "GLYOXAL": { "yield": 0.05 }, + "GLYALD": { "yield": 0.05 }, + "CH3COCHO": { "yield": 0.05 }, + "HYAC": { "yield": 0.05 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jno=userdefined,", + "reactants": { + "NO": { } + }, + "products": { + "N": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "Ea": -6.90325e-21, + "reactants": { + "CH3O2": { }, + "CH3CO3": { } + }, + "products": { + "CH3O2": { "yield": 0.9 }, + "CH2O": { }, + "HO2": { "yield": 0.9 }, + "CO2": { "yield": 0.9 }, + "CH3COOH": { "yield": 0.1 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.5e-15, + "Ea": 2.89936e-20, + "reactants": { + "O3": { }, + "MACR": { } + }, + "products": { + "CH2O": { "yield": 0.12 }, + "OH": { "yield": 0.24 }, + "CO": { "yield": 0.65 }, + "CH3CO3": { "yield": 0.1 }, + "CH3COCHO": { "yield": 0.88 }, + "HCOOH": { "yield": 0.33 }, + "HO2": { "yield": 0.14 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "ALKOOH": { } + }, + "products": { + "ALKO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.63e-12, + "Ea": -4.83227e-21, + "reactants": { + "OH": { }, + "CH3CHO": { } + }, + "products": { + "CH3CO3": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.75e-13, + "Ea": -1.79484e-20, + "reactants": { + "BCARYO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.2202 }, + "SOAG1": { "yield": 0.2067 }, + "SOAG2": { "yield": 0.0653 }, + "SOAG3": { "yield": 0.1284 }, + "SOAG4": { "yield": 0.114 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-12, + "Ea": -6.76518e-21, + "reactants": { + "NO3": { }, + "MTERP": { } + }, + "products": { + "NTERPO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-12, + "Ea": 4.34904e-20, + "reactants": { + "N": { }, + "O2": { } + }, + "products": { + "NO": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -3.65872e-21, + "reactants": { + "HOCH2OO": { }, + "NO": { } + }, + "products": { + "HCOOH": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jh2o_b", + "reactants": { + "H2O": { } + }, + "products": { + "H2": { }, + "O1D": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jc3h7ooh->,jch3ooh", + "reactants": { + "C3H7OOH": { } + }, + "products": { + "CH3COCH3": { "yield": 0.82 }, + "OH": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcfc113", + "reactants": { + "CFC113": { } + }, + "products": { + "CL": { "yield": 2 }, + "COFCL": { }, + "COF2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jccl4", + "reactants": { + "CCL4": { } + }, + "products": { + "CL": { "yield": 4 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.8e-11, + "reactants": { + "SO": { }, + "CLO": { } + }, + "products": { + "SO2": { }, + "CL": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jnoa->,jch2o_a", + "reactants": { + "NOA": { } + }, + "products": { + "NO2": { }, + "CH2O": { }, + "CH3CO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.57e-10, + "reactants": { + "CH2BR2": { }, + "O1D": { } + }, + "products": { + "BR": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jh2so4", + "reactants": { + "H2SO4": { } + }, + "products": { + "SO3": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.4e-13, + "Ea": -1.14594e-20, + "reactants": { + "OH": { }, + "CH3COCHO": { } + }, + "products": { + "CH3CO3": { }, + "CO": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-10, + "reactants": { + "O1D": { }, + "COFCL": { } + }, + "products": { + "F": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.64e-12, + "Ea": 2.09859e-20, + "reactants": { + "CH3CCL3": { }, + "OH": { } + }, + "products": { + "H2O": { }, + "CL": { "yield": 3 } + } + }, + { + "type": "TROE", + "k0_A": 6.1e-33, + "k0_B": -1.5, + "kinf_A": 9.8e-15, + "Fc": 0.8, + "N": -4.6, + "reactants": { + "OH": { }, + "HCN": { } + }, + "products": { + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.794e-10, + "reactants": { + "O1D": { }, + "HCFC141B": { } + }, + "products": { + "CL": { }, + "COFCL": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmacr_b", + "reactants": { + "MACR": { } + }, + "products": { + "HO2": { "yield": 0.66 }, + "CO": { "yield": 1.34 } + } + }, + { + "type": "ARRHENIUS", + "A": 9.0e-12, + "reactants": { + "CH4": { }, + "O1D": { } + }, + "products": { + "CH2O": { }, + "H2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "C3H7O2": { }, + "HO2": { } + }, + "products": { + "C3H7OOH": { }, + "O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jnterpooh->,jch3ooh", + "reactants": { + "NTERPOOH": { } + }, + "products": { + "TERPROD1": { }, + "NO2": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.54e-11, + "Ea": -5.66066e-21, + "reactants": { + "ISOP": { }, + "OH": { } + }, + "products": { + "ISOP": { }, + "OH": { }, + "ISOPO2VBS": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jrooh->,jch3ooh", + "reactants": { + "ROOH": { } + }, + "products": { + "CH3CO3": { }, + "CH2O": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.7e-11, + "reactants": { + "OH": { }, + "TERPROD1": { } + }, + "products": { + "TERP2O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.45e-12, + "Ea": -3.03743e-21, + "reactants": { + "N": { }, + "NO2": { } + }, + "products": { + "NO": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhbr", + "reactants": { + "HBR": { } + }, + "products": { + "BR": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-10, + "Ea": 6.48905e-21, + "reactants": { + "H": { }, + "O3": { } + }, + "products": { + "OH": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-13, + "Ea": -5.5226e-21, + "reactants": { + "ISOPNO3": { }, + "CH3O2": { } + }, + "products": { + "NC4CHO": { "yield": 0.8 }, + "HO2": { "yield": 1.2 }, + "CH2O": { "yield": 0.8 }, + "CH3OH": { "yield": 0.2 }, + "NC4CH2OH": { "yield": 0.2 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": -1.43587e-20, + "reactants": { + "MDIALO2": { }, + "HO2": { } + }, + "products": { + "OH": { "yield": 0.4 }, + "HO2": { "yield": 0.33 }, + "CH3COCHO": { "yield": 0.07 }, + "CO": { "yield": 0.14 }, + "CH3O2": { "yield": 0.07 }, + "GLYOXAL": { "yield": 0.07 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jpooh->,jch3ooh", + "reactants": { + "POOH": { } + }, + "products": { + "CH3CHO": { }, + "CH2O": { }, + "HO2": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-13, + "Ea": -5.5226e-21, + "reactants": { + "CH3O2": { }, + "ISOPBO2": { } + }, + "products": { + "CH3OH": { "yield": 0.25 }, + "HO2": { }, + "CH2O": { "yield": 0.75 }, + "HYDRALD": { "yield": 0.75 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.1e-11, + "Ea": 3.86582e-21, + "reactants": { + "OH": { }, + "DMS": { } + }, + "products": { + "SO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa2_a2->,.0004*jno2", + "reactants": { + "soa2_a2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "HOCH2OO": { }, + "HO2": { } + }, + "products": { + "HCOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-12, + "Ea": -6.76518e-21, + "reactants": { + "NO3": { }, + "MTERP": { } + }, + "products": { + "MTERP": { }, + "NO3": { }, + "SOAG3": { "yield": 0.17493 }, + "SOAG4": { "yield": 0.59019 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.66e-12, + "Ea": 1.40826e-20, + "reactants": { + "C2H6": { }, + "OH": { } + }, + "products": { + "C2H5O2": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.15e-11, + "reactants": { + "GLYOXAL": { }, + "OH": { } + }, + "products": { + "HO2": { }, + "CO": { }, + "CO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "C6H5O2": { }, + "HO2": { } + }, + "products": { + "C6H5OOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-11, + "Ea": -6.07486e-21, + "reactants": { + "OH": { }, + "MTERP": { } + }, + "products": { + "TERPO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.52e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "XOOH": { } + }, + "products": { + "XO2": { "yield": 0.5 }, + "OH": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.0e-12, + "Ea": 2.84414e-20, + "reactants": { + "O": { }, + "O3": { } + }, + "products": { + "O2": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-11, + "reactants": { + "OH": { }, + "IEPOX": { } + }, + "products": { + "XO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.2e-11, + "Ea": 9.66454e-22, + "reactants": { + "C2H6": { }, + "CL": { } + }, + "products": { + "HCL": { }, + "C2H5O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.4e-14, + "Ea": -1.20116e-20, + "reactants": { + "ALKO2": { }, + "NO": { } + }, + "products": { + "ALKNIT": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "BZOO": { }, + "NO": { } + }, + "products": { + "BZALD": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-11, + "Ea": -2.48517e-21, + "reactants": { + "OH": { }, + "O": { } + }, + "products": { + "H": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-13, + "Ea": -7.17937e-21, + "reactants": { + "NO3": { }, + "DMS": { } + }, + "products": { + "SO2": { }, + "HNO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-12, + "Ea": -7.31744e-21, + "reactants": { + "MCO3": { "qty": 2 } + }, + "products": { + "CO2": { "yield": 2 }, + "CH2O": { "yield": 2 }, + "CH3CO3": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.8e-12, + "Ea": -1.65678e-21, + "reactants": { + "NO": { }, + "ENEO2": { } + }, + "products": { + "CH3CHO": { }, + "CH2O": { "yield": 0.5 }, + "CH3COCH3": { "yield": 0.5 }, + "HO2": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.0e-34, + "B": -2.4, + "reactants": { + "O2": { }, + "M": { }, + "O": { } + }, + "products": { + "O3": { }, + "M": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.4e-12, + "Ea": -4.00388e-21, + "reactants": { + "NO": { }, + "CLO": { } + }, + "products": { + "NO2": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.0e-13, + "Ea": -9.66454e-21, + "reactants": { + "XO2": { }, + "HO2": { } + }, + "products": { + "XOOH": { } + } + }, + { + "type": "TROE", + "k0_A": 2.9e-31, + "k0_B": -4.1, + "kinf_A": 1.7e-12, + "N": -0.2, + "reactants": { + "SO2": { }, + "OH": { } + }, + "products": { + "SO3": { }, + "HO2": { } + } + }, + { + "type": "TROE", + "k0_A": 1.8e-30, + "k0_B": -3, + "kinf_A": 2.8e-11, + "N": 0, + "reactants": { + "OH": { }, + "NO2": { } + }, + "products": { + "HNO3": { } + } + }, + { + "type": "TROE", + "k0_A": 1.8e-31, + "k0_B": -3.4, + "kinf_A": 1.5e-11, + "N": 1.9, + "reactants": { + "CLO": { }, + "NO2": { } + }, + "products": { + "CLONO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhyac", + "reactants": { + "HYAC": { } + }, + "products": { + "CH3CO3": { }, + "HO2": { }, + "CH2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-11, + "Ea": -2.7613e-21, + "reactants": { + "O": { }, + "HO2": { } + }, + "products": { + "OH": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-12, + "Ea": -4.85988e-21, + "reactants": { + "TOLUENE": { }, + "OH": { } + }, + "products": { + "TOLUENE": { }, + "OH": { }, + "TOLUO2VBS": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.6e-12, + "Ea": -4.97034e-21, + "reactants": { + "OH": { }, + "MACR": { } + }, + "products": { + "MACRO2": { "yield": 0.5 }, + "H2O": { "yield": 0.5 }, + "MCO3": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.204e-10, + "reactants": { + "CFC12": { }, + "O1D": { } + }, + "products": { + "CL": { "yield": 2 }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-11, + "Ea": -3.17549e-21, + "reactants": { + "O": { }, + "BRO": { } + }, + "products": { + "BR": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.9e-12, + "reactants": { + "H": { }, + "HO2": { } + }, + "products": { + "H2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.5e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "HBR": { } + }, + "products": { + "BR": { }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhcfc142b", + "reactants": { + "HCFC142B": { } + }, + "products": { + "CL": { }, + "COF2": { } + } + }, + { + "type": "TROE", + "k0_A": 8.0e-27, + "k0_B": -3.5, + "kinf_A": 3.0e-11, + "Fc": 0.5, + "N": 0, + "reactants": { + "OH": { }, + "C3H6": { } + }, + "products": { + "PO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-10, + "reactants": { + "BCARY": { }, + "OH": { } + }, + "products": { + "TERPO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "reactants": { + "MACRO2": { }, + "CH3CO3": { } + }, + "products": { + "CH3COCHO": { "yield": 0.25 }, + "CH3O2": { }, + "CO": { "yield": 0.22 }, + "HO2": { "yield": 0.47 }, + "GLYALD": { "yield": 0.53 }, + "HYAC": { "yield": 0.22 }, + "CH2O": { "yield": 0.25 }, + "CH3CO3": { "yield": 0.53 } + } + }, + { + "type": "TROE", + "k0_A": 0.000413793, + "k0_B": -3, + "k0_C": -10840, + "kinf_A": 2.75862e+14, + "kinf_C": -10840, + "N": -0.1, + "reactants": { + "N2O5": { } + }, + "products": { + "NO2": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-10, + "reactants": { + "O1D": { }, + "H2": { } + }, + "products": { + "H": { }, + "OH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jh2o2", + "reactants": { + "H2O2": { } + }, + "products": { + "OH": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e-13, + "Ea": 3.14788e-20, + "reactants": { + "SO": { }, + "O2": { } + }, + "products": { + "SO2": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "C6H5OOH": { } + }, + "products": { + "C6H5O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-13, + "reactants": { + "O": { }, + "HOCL": { } + }, + "products": { + "CLO": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.8e-13, + "reactants": { + "PHENO": { }, + "O3": { } + }, + "products": { + "C6H5O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "joclo", + "reactants": { + "OCLO": { } + }, + "products": { + "O": { }, + "CLO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "BENZO2VBS": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.0097 }, + "SOAG1": { "yield": 0.0034 }, + "SOAG2": { "yield": 0.1579 }, + "SOAG3": { "yield": 0.0059 }, + "SOAG4": { "yield": 0.0536 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "PHENO2": { }, + "NO": { } + }, + "products": { + "HO2": { }, + "GLYOXAL": { "yield": 0.7 }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "reactants": { + "SO": { }, + "NO2": { } + }, + "products": { + "SO2": { }, + "NO": { } + } + }, + { + "type": "TROE", + "k0_A": 1.9e-32, + "k0_B": -3.6, + "kinf_A": 3.7e-12, + "N": 1.6, + "reactants": { + "CLO": { "qty": 2 } + }, + "products": { + "CL2O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa5_a2->,.0004*jno2", + "reactants": { + "soa5_a2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-12, + "Ea": -4.00388e-21, + "reactants": { + "NO": { }, + "ACBZO2": { } + }, + "products": { + "C6H5O2": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "Ea": -3.72775e-21, + "reactants": { + "CL": { }, + "HO2": { } + }, + "products": { + "HCL": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "PO2": { }, + "HO2": { } + }, + "products": { + "POOH": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "PHENO2": { }, + "HO2": { } + }, + "products": { + "PHENOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": -1.43587e-20, + "reactants": { + "MCO3": { }, + "HO2": { } + }, + "products": { + "O3": { "yield": 0.15 }, + "CH3COOH": { "yield": 0.15 }, + "CH3COOOH": { "yield": 0.4 }, + "OH": { "yield": 0.45 }, + "CO2": { "yield": 0.45 }, + "CH2O": { "yield": 0.45 }, + "CH3CO3": { "yield": 0.45 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "C2H5O2": { } + }, + "products": { + "CH3CHO": { }, + "HO2": { }, + "NO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmacr_a", + "reactants": { + "MACR": { } + }, + "products": { + "HO2": { "yield": 1.34 }, + "MCO3": { "yield": 0.66 }, + "CH2O": { "yield": 1.34 }, + "CH3CO3": { "yield": 1.34 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbenzooh->,jch3ooh", + "reactants": { + "BENZOOH": { } + }, + "products": { + "OH": { }, + "GLYOXAL": { }, + "BIGALD1": { "yield": 0.5 }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.07e-10, + "reactants": { + "O1D": { }, + "CFC11": { } + }, + "products": { + "CL": { "yield": 2 }, + "COFCL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-13, + "reactants": { + "CH3O2": { }, + "C2H5O2": { } + }, + "products": { + "CH2O": { "yield": 0.7 }, + "CH3CHO": { "yield": 0.8 }, + "HO2": { }, + "CH3OH": { "yield": 0.3 }, + "C2H5OH": { "yield": 0.2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jn2o5_b", + "reactants": { + "N2O5": { } + }, + "products": { + "NO": { }, + "O": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.0e-13, + "Ea": -9.66454e-21, + "reactants": { + "ISOPBO2": { }, + "HO2": { } + }, + "products": { + "ISOPOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.8e-12, + "Ea": 2.48517e-20, + "reactants": { + "OH": { }, + "H2": { } + }, + "products": { + "H2O": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e-12, + "reactants": { + "H": { }, + "HO2": { } + }, + "products": { + "H2O": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.45e-12, + "Ea": 2.45065e-20, + "reactants": { + "CH4": { }, + "OH": { } + }, + "products": { + "CH3O2": { }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jso", + "reactants": { + "SO": { } + }, + "products": { + "S": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": -4.83227e-21, + "reactants": { + "ISOPO2VBS": { }, + "NO": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.0003 }, + "SOAG1": { "yield": 0.0003 }, + "SOAG2": { "yield": 0.0073 }, + "SOAG3": { "yield": 0.0057 }, + "SOAG4": { "yield": 0.0623 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.8e-11, + "Ea": -3.45162e-21, + "reactants": { + "OH": { }, + "HO2": { } + }, + "products": { + "H2O": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.1e-12, + "reactants": { + "PHENO": { }, + "NO2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.5e-12, + "Ea": -3.17549e-21, + "reactants": { + "BRO": { "qty": 2 } + }, + "products": { + "BR": { "yield": 2 }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.0e-11, + "reactants": { + "OH": { }, + "NC4CH2OH": { } + }, + "products": { + "GLYALD": { }, + "NOA": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jisopnooh->,jch3ooh", + "reactants": { + "ISOPNOOH": { } + }, + "products": { + "NO2": { }, + "HO2": { }, + "ISOPOOH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jalkooh->,jch3ooh", + "reactants": { + "ALKOOH": { } + }, + "products": { + "CH3CHO": { "yield": 0.4 }, + "CH2O": { "yield": 0.1 }, + "CH3COCH3": { "yield": 0.25 }, + "HO2": { "yield": 0.9 }, + "MEK": { "yield": 0.8 }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.4e-12, + "Ea": -3.72775e-21, + "reactants": { + "OH": { }, + "CLO": { } + }, + "products": { + "CL": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "NTERPO2": { } + }, + "products": { + "TERPNIT": { "yield": 0.2 }, + "NO2": { "yield": 1.6 }, + "TERPROD1": { "yield": 0.8 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-12, + "reactants": { + "NO3": { }, + "TERPROD1": { } + }, + "products": { + "TERP2O2": { "yield": 0.5 }, + "NTERPO2": { "yield": 0.5 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa1_a1->,.0004*jno2", + "reactants": { + "soa1_a1": { } + }, + "products": { + + } + }, + { + "type": "TROE", + "k0_A": 8.6e-29, + "k0_B": -3.1, + "kinf_A": 9.0e-12, + "Fc": 0.48, + "N": 0.85, + "reactants": { + "C2H4": { }, + "OH": { } + }, + "products": { + "EO2": { } + } + }, + { + "type": "TROE", + "k0_A": 5.3e-32, + "k0_B": -1.8, + "kinf_A": 9.5e-11, + "N": -0.4, + "reactants": { + "O2": { }, + "H": { } + }, + "products": { + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-12, + "reactants": { + "S": { }, + "O2": { } + }, + "products": { + "SO": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-14, + "Ea": 3.63111e-20, + "reactants": { + "C2H4": { }, + "O3": { } + }, + "products": { + "CO": { "yield": 0.63 }, + "OH": { "yield": 0.13 }, + "HO2": { "yield": 0.13 }, + "HCOOH": { "yield": 0.37 }, + "CH2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.63e-7, + "reactants": { + "ST80_25": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-12, + "Ea": 4.55614e-21, + "reactants": { + "CLONO2": { }, + "OH": { } + }, + "products": { + "HOCL": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.1e-11, + "Ea": 3.03743e-20, + "reactants": { + "O": { }, + "OCS": { } + }, + "products": { + "SO": { }, + "CO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.0e-13, + "Ea": -9.66454e-21, + "reactants": { + "ISOPNO3": { }, + "HO2": { } + }, + "products": { + "ISOPNOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.13e-12, + "Ea": -6.24053e-21, + "reactants": { + "OH": { }, + "MVK": { } + }, + "products": { + "MACRO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jeooh->,jch3ooh", + "reactants": { + "EOOH": { } + }, + "products": { + "EO": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "reactants": { + "ISOPBO2": { }, + "CH3CO3": { } + }, + "products": { + "HYDRALD": { }, + "CH3O2": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbrcl", + "reactants": { + "BRCL": { } + }, + "products": { + "BR": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "C2H5O2": { }, + "HO2": { } + }, + "products": { + "C2H5OOH": { }, + "O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jisopooh->,jch3ooh", + "reactants": { + "ISOPOOH": { } + }, + "products": { + "MVK": { "yield": 0.7 }, + "MACR": { "yield": 0.3 }, + "OH": { }, + "CH2O": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-10, + "reactants": { + "H2402": { }, + "O1D": { } + }, + "products": { + "BR": { "yield": 2 }, + "COF2": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.03e-12, + "Ea": 6.15769e-21, + "reactants": { + "ISOP": { }, + "NO3": { } + }, + "products": { + "ISOP": { }, + "NO3": { }, + "SOAG3": { "yield": 0.059024 }, + "SOAG4": { "yield": 0.025024 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "CH3OOH": { } + }, + "products": { + "CH3O2": { "yield": 0.7 }, + "OH": { "yield": 0.3 }, + "CH2O": { "yield": 0.3 }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jc6h5ooh->,jch3ooh", + "reactants": { + "C6H5OOH": { } + }, + "products": { + "PHENO": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.1e-12, + "Ea": -2.89936e-21, + "reactants": { + "O": { }, + "NO2": { } + }, + "products": { + "NO": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "MEKOOH": { }, + "OH": { } + }, + "products": { + "MEKO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.3e-12, + "Ea": -4.97034e-21, + "reactants": { + "NO": { }, + "MCO3": { } + }, + "products": { + "NO2": { }, + "CH2O": { }, + "CH3CO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcfc114", + "reactants": { + "CFC114": { } + }, + "products": { + "CL": { "yield": 2 }, + "COF2": { "yield": 2 } + } + }, + { + "type": "TROE", + "k0_A": 9.7e-29, + "k0_B": -5.6, + "kinf_A": 9.3e-12, + "N": 1.5, + "reactants": { + "MALO2": { }, + "NO2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 3.44e-12, + "Ea": -3.58969e-21, + "reactants": { + "NO": { }, + "HO2": { } + }, + "products": { + "NO2": { }, + "OH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jho2no2_b", + "reactants": { + "HO2NO2": { } + }, + "products": { + "NO2": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.25e-12, + "Ea": 2.20904e-20, + "reactants": { + "OH": { }, + "HCFC141B": { } + }, + "products": { + "CL": { }, + "COFCL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-12, + "Ea": -3.58969e-21, + "reactants": { + "BRO": { }, + "CLO": { } + }, + "products": { + "BR": { }, + "CL": { }, + "O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jpan", + "reactants": { + "PAN": { } + }, + "products": { + "CH3CO3": { "yield": 0.6 }, + "NO2": { "yield": 0.6 }, + "CH3O2": { "yield": 0.4 }, + "NO3": { "yield": 0.4 }, + "CO2": { "yield": 0.4 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.0e-11, + "reactants": { + "OH": { }, + "ISOPNITA": { } + }, + "products": { + "HYAC": { "yield": 0.7 }, + "GLYALD": { "yield": 0.7 }, + "NO2": { "yield": 0.7 }, + "CH2O": { "yield": 0.3 }, + "HONITR": { "yield": 0.3 }, + "HO2": { "yield": 0.3 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "PO2": { } + }, + "products": { + "CH3CHO": { }, + "CH2O": { }, + "HO2": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "BENZO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.0023 }, + "SOAG1": { "yield": 0.0008 }, + "SOAG2": { "yield": 0.0843 }, + "SOAG3": { "yield": 0.0443 }, + "SOAG4": { "yield": 0.1621 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jo3_b", + "reactants": { + "O3": { } + }, + "products": { + "O": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-14, + "Ea": 6.76518e-21, + "reactants": { + "O3": { }, + "HO2": { } + }, + "products": { + "OH": { }, + "O2": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcf2cl2", + "reactants": { + "CFC12": { } + }, + "products": { + "CL": { "yield": 2 }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.62e-10, + "reactants": { + "CHBR3": { }, + "O1D": { } + }, + "products": { + "BR": { "yield": 3 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jno3_b", + "reactants": { + "NO3": { } + }, + "products": { + "NO": { }, + "O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbigald2->,.20*jno2", + "reactants": { + "BIGALD2": { } + }, + "products": { + "HO2": { "yield": 0.6 }, + "DICARBO2": { "yield": 0.6 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-13, + "Ea": -4.97034e-21, + "reactants": { + "NO": { }, + "MACRO2": { } + }, + "products": { + "HONITR": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.5e-11, + "reactants": { + "O1D": { }, + "CF3BR": { } + }, + "products": { + "BR": { }, + "F": { }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "BENZOOH": { }, + "OH": { } + }, + "products": { + "BENZO2": { } + } + }, + { + "type": "TROE", + "k0_A": 9.7e-29, + "k0_B": -5.6, + "kinf_A": 9.3e-12, + "N": 1.5, + "reactants": { + "ACBZO2": { }, + "NO2": { } + }, + "products": { + "PBZNIT": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "TERPO2": { } + }, + "products": { + "TERPNIT": { "yield": 0.2 }, + "NO2": { "yield": 0.8 }, + "CH2O": { "yield": 0.32 }, + "CH3COCH3": { "yield": 0.04 }, + "TERPROD1": { "yield": 0.8 }, + "HO2": { "yield": 0.8 } + } + }, + { + "type": "ARRHENIUS", + "A": 6.6e-11, + "reactants": { + "OH": { }, + "S": { } + }, + "products": { + "SO": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-12, + "reactants": { + "CH3COOOH": { }, + "OH": { } + }, + "products": { + "CH3CO3": { "yield": 0.5 }, + "CH2O": { "yield": 0.5 }, + "CO2": { "yield": 0.5 }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-11, + "Ea": -7.59357e-22, + "reactants": { + "O1D": { }, + "O2": { } + }, + "products": { + "O": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-14, + "Ea": -6.35099e-21, + "reactants": { + "HNO3": { }, + "OH": { } + }, + "products": { + "NO3": { }, + "H2O": { } + } + }, + { + "type": "TROE", + "k0_A": 6.5e-34, + "k0_C": 1335, + "kinf_A": 2.7e-17, + "kinf_C": 2199, + "Fc": 1, + "reactants": { + "HNO3": { }, + "OH": { } + }, + "products": { + "NO3": { }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jho2no2_a", + "reactants": { + "HO2NO2": { } + }, + "products": { + "OH": { }, + "NO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch3br", + "reactants": { + "CH3BR": { } + }, + "products": { + "BR": { }, + "CH3O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -4.00388e-21, + "reactants": { + "CLO": { }, + "HO2": { } + }, + "products": { + "O2": { }, + "HOCL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-12, + "Ea": -4.00388e-21, + "reactants": { + "NO": { }, + "MDIALO2": { } + }, + "products": { + "NO2": { }, + "HO2": { "yield": 0.83 }, + "CH3COCHO": { "yield": 0.17 }, + "CO": { "yield": 0.35 }, + "CH3O2": { "yield": 0.17 }, + "GLYOXAL": { "yield": 0.17 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "reactants": { + "H2O": { }, + "F": { } + }, + "products": { + "HF": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-12, + "Ea": 2.56801e-20, + "reactants": { + "NO3": { }, + "CH3COCHO": { } + }, + "products": { + "HNO3": { }, + "CO": { }, + "CH3CO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jclono2_b", + "reactants": { + "CLONO2": { } + }, + "products": { + "CLO": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.1e-11, + "Ea": -1.38065e-21, + "reactants": { + "NO": { }, + "N": { } + }, + "products": { + "N2": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": 4.76324e-21, + "reactants": { + "CH3OH": { }, + "OH": { } + }, + "products": { + "HO2": { }, + "CH2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-13, + "Ea": -5.5226e-21, + "reactants": { + "CH3O2": { }, + "XO2": { } + }, + "products": { + "CH3OH": { "yield": 0.3 }, + "HO2": { "yield": 0.8 }, + "CH2O": { "yield": 0.8 }, + "CO": { "yield": 0.2 }, + "GLYOXAL": { "yield": 0.1 }, + "CH3COCHO": { "yield": 0.1 }, + "HYAC": { "yield": 0.1 }, + "GLYALD": { "yield": 0.1 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "PHENOOH": { } + }, + "products": { + "PHENO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "reactants": { + "ISOPNO3": { }, + "CH3CO3": { } + }, + "products": { + "NC4CHO": { }, + "CH3O2": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jglyoxal->,jmgly", + "reactants": { + "GLYOXAL": { } + }, + "products": { + "CO": { "yield": 2 }, + "HO2": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jo3_a", + "reactants": { + "O3": { } + }, + "products": { + "O1D": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "XYLENO2": { } + }, + "products": { + "NO2": { }, + "HO2": { }, + "GLYOXAL": { "yield": 0.34 }, + "CH3COCHO": { "yield": 0.54 }, + "BIGALD1": { "yield": 0.06 }, + "BIGALD2": { "yield": 0.2 }, + "BIGALD3": { "yield": 0.15 }, + "BIGALD4": { "yield": 0.21 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "XYLOLOOH": { } + }, + "products": { + "XYLOLO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-12, + "reactants": { + "OH": { }, + "HYAC": { } + }, + "products": { + "CH3COCHO": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.0e-13, + "Ea": 2.84138e-20, + "reactants": { + "NO3": { }, + "CH2O": { } + }, + "products": { + "CO": { }, + "HO2": { }, + "HNO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-12, + "Ea": -4.00388e-21, + "reactants": { + "MALO2": { }, + "NO": { } + }, + "products": { + "GLYOXAL": { "yield": 0.4 }, + "HO2": { "yield": 0.4 }, + "CO": { "yield": 0.4 }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "ALKO2": { }, + "HO2": { } + }, + "products": { + "ALKOOH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jn2o5_a", + "reactants": { + "N2O5": { } + }, + "products": { + "NO2": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-11, + "reactants": { + "OH": { }, + "TERPNIT": { } + }, + "products": { + "NO2": { }, + "TERPROD1": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhpald->,.006*jno2", + "reactants": { + "HPALD": { } + }, + "products": { + "BIGALD3": { }, + "OH": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e+12, + "Ea": 9.66454e-20, + "reactants": { + "HOCH2OO": { } + }, + "products": { + "CH2O": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.1e-13, + "Ea": -4.00388e-21, + "reactants": { + "BRO": { }, + "CLO": { } + }, + "products": { + "BRCL": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-13, + "Ea": -5.5226e-21, + "reactants": { + "ISOPAO2": { }, + "CH3O2": { } + }, + "products": { + "CH3OH": { "yield": 0.25 }, + "HO2": { }, + "CH2O": { "yield": 1.5 }, + "MACR": { "yield": 0.31 }, + "MVK": { "yield": 0.44 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbzooh->,jch3ooh", + "reactants": { + "BZOOH": { } + }, + "products": { + "BZALD": { }, + "OH": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-12, + "Ea": 2.7613e-20, + "reactants": { + "O": { }, + "H2O2": { } + }, + "products": { + "OH": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-14, + "reactants": { + "EO": { }, + "O2": { } + }, + "products": { + "GLYALD": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.64e-11, + "Ea": -2.7613e-22, + "reactants": { + "N2O": { }, + "O1D": { } + }, + "products": { + "N2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.86e-11, + "Ea": -2.41614e-21, + "reactants": { + "OH": { }, + "HPALD": { } + }, + "products": { + "XO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcl2o2", + "reactants": { + "CL2O2": { } + }, + "products": { + "CL": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-10, + "Ea": 5.93679e-21, + "reactants": { + "HOBR": { }, + "O": { } + }, + "products": { + "BRO": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.7e-15, + "Ea": -8.62906e-21, + "reactants": { + "CH2O": { }, + "HO2": { } + }, + "products": { + "HOCH2OO": { } + } + }, + { + "type": "TROE", + "k0_A": 5.2e-30, + "k0_B": -2.4, + "kinf_A": 2.2e-10, + "N": 0.7, + "reactants": { + "C2H2": { }, + "CL": { } + }, + "products": { + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-12, + "reactants": { + "ISOPNO3": { }, + "NO3": { } + }, + "products": { + "NC4CHO": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.34e-11, + "reactants": { + "IVOC": { }, + "OH": { } + }, + "products": { + "OH": { }, + "IVOCO2VBS": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jalknit->,jch3ooh", + "reactants": { + "ALKNIT": { } + }, + "products": { + "NO2": { }, + "CH3CHO": { "yield": 0.4 }, + "CH2O": { "yield": 0.1 }, + "CH3COCH3": { "yield": 0.25 }, + "HO2": { }, + "MEK": { "yield": 0.8 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-12, + "reactants": { + "NO3": { }, + "HO2": { } + }, + "products": { + "OH": { }, + "NO2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.03e-11, + "Ea": 1.51871e-20, + "reactants": { + "CH3CL": { }, + "CL": { } + }, + "products": { + "HO2": { }, + "CO": { }, + "HCL": { "yield": 2 } + } + }, + { + "type": "TROE", + "k0_A": 9.7e-29, + "k0_B": -5.6, + "kinf_A": 9.3e-12, + "N": 1.5, + "reactants": { + "MCO3": { }, + "NO2": { } + }, + "products": { + "MPAN": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.0e-13, + "Ea": 4.97034e-21, + "reactants": { + "CHBR3": { }, + "OH": { } + }, + "products": { + "BR": { "yield": 3 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-10, + "reactants": { + "O1D": { }, + "HCFC142B": { } + }, + "products": { + "CL": { }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-12, + "Ea": 1.58775e-21, + "reactants": { + "CH3O2": { }, + "CLO": { } + }, + "products": { + "CL": { }, + "HO2": { }, + "CH2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-13, + "Ea": 1.89149e-20, + "reactants": { + "CLO": { "qty": 2 } + }, + "products": { + "CL": { }, + "OCLO": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbigald->,0.2*jno2", + "reactants": { + "BIGALD": { } + }, + "products": { + "CO": { "yield": 0.45 }, + "GLYOXAL": { "yield": 0.13 }, + "HO2": { "yield": 0.56 }, + "CH3CO3": { "yield": 0.13 }, + "CH3COCHO": { "yield": 0.18 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.6e-11, + "Ea": 5.17743e-21, + "reactants": { + "CL": { }, + "HO2": { } + }, + "products": { + "OH": { }, + "CLO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "C3H7O2": { } + }, + "products": { + "CH3COCH3": { "yield": 0.82 }, + "NO2": { }, + "HO2": { }, + "CH3CHO": { "yield": 0.27 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa5_a1->,.0004*jno2", + "reactants": { + "soa5_a1": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 6.9e-12, + "Ea": 3.17549e-21, + "reactants": { + "C2H5OH": { }, + "OH": { } + }, + "products": { + "HO2": { }, + "CH3CHO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-11, + "reactants": { + "O3": { }, + "S": { } + }, + "products": { + "SO": { }, + "O2": { } + } + }, + { + "type": "TROE", + "k0_A": 7.3e-29, + "k0_B": -4.1, + "kinf_A": 9.5e-12, + "N": 1.6, + "reactants": { + "CH3CO3": { }, + "NO2": { } + }, + "products": { + "PAN": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.52e-11, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "ISOPOOH": { } + }, + "products": { + "XO2": { "yield": 0.4 }, + "IEPOX": { "yield": 0.6 }, + "OH": { "yield": 0.6 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": -4.14195e-21, + "reactants": { + "NO": { }, + "RO2": { } + }, + "products": { + "CH3CO3": { }, + "CH2O": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "BZOOH": { } + }, + "products": { + "BZOO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-10, + "reactants": { + "O3": { }, + "O1D": { } + }, + "products": { + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": -1.43587e-20, + "reactants": { + "MALO2": { }, + "HO2": { } + }, + "products": { + "GLYOXAL": { "yield": 0.16 }, + "HO2": { "yield": 0.16 }, + "CO": { "yield": 0.16 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.31e-7, + "reactants": { + "NH_50": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 2.15e-11, + "Ea": -1.51871e-21, + "reactants": { + "O1D": { }, + "N2": { } + }, + "products": { + "O": { }, + "N2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-11, + "reactants": { + "OH": { }, + "XYLENES": { } + }, + "products": { + "XYLOL": { "yield": 0.15 }, + "TEPOMUC": { "yield": 0.23 }, + "BZOO": { "yield": 0.06 }, + "XYLENO2": { "yield": 0.56 }, + "HO2": { "yield": 0.38 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.2e-11, + "reactants": { + "NO3": { }, + "OH": { } + }, + "products": { + "HO2": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "IVOCO2VBS": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.1056 }, + "SOAG1": { "yield": 0.1026 }, + "SOAG2": { "yield": 0.0521 }, + "SOAG3": { "yield": 0.0143 }, + "SOAG4": { "yield": 0.0166 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": -4.97034e-21, + "reactants": { + "BCARYO2VBS": { }, + "NO": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.1279 }, + "SOAG1": { "yield": 0.1792 }, + "SOAG2": { "yield": 0.0676 }, + "SOAG3": { "yield": 0.079 }, + "SOAG4": { "yield": 0.1254 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmvk", + "reactants": { + "MVK": { } + }, + "products": { + "C3H6": { "yield": 0.7 }, + "CO": { "yield": 0.7 }, + "CH3O2": { "yield": 0.3 }, + "CH3CO3": { "yield": 0.3 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "EO2": { } + }, + "products": { + "CH2O": { "yield": 0.5 }, + "HO2": { "yield": 0.25 }, + "EO": { "yield": 0.75 }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-11, + "reactants": { + "O1D": { }, + "HBR": { } + }, + "products": { + "BRO": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.8e-11, + "Ea": -1.17355e-21, + "reactants": { + "O": { }, + "CLO": { } + }, + "products": { + "CL": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.1e-12, + "Ea": 1.75342e-20, + "reactants": { + "CH4": { }, + "CL": { } + }, + "products": { + "CH3O2": { }, + "HCL": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jso3", + "reactants": { + "SO3": { } + }, + "products": { + "SO2": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.3e-12, + "Ea": 1.10452e-20, + "reactants": { + "CH2BR2": { }, + "CL": { } + }, + "products": { + "BR": { "yield": 2 }, + "HCL": { } + } + }, + { + "type": "TROE", + "k0_A": 8.0e-27, + "k0_B": -3.5, + "kinf_A": 3.0e-11, + "Fc": 0.5, + "N": 0, + "reactants": { + "OH": { }, + "MPAN": { } + }, + "products": { + "HYAC": { "yield": 0.5 }, + "NO3": { "yield": 0.5 }, + "CH2O": { "yield": 0.5 }, + "HO2": { "yield": 0.5 }, + "CO2": { "yield": 0.5 } + } + }, + { + "type": "TROE", + "k0_A": 1.07767, + "k0_B": -5.6, + "k0_C": -14000, + "kinf_A": 1.03323e+17, + "kinf_C": -14000, + "N": 1.5, + "reactants": { + "PBZNIT": { } + }, + "products": { + "ACBZO2": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-12, + "Ea": 6.90325e-21, + "reactants": { + "OH": { }, + "HOCL": { } + }, + "products": { + "H2O": { }, + "CLO": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jchbr3", + "reactants": { + "CHBR3": { } + }, + "products": { + "BR": { "yield": 3 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-12, + "Ea": 2.62323e-20, + "reactants": { + "NO3": { }, + "CH3CHO": { } + }, + "products": { + "CH3CO3": { }, + "HNO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jterprd2->,jch3cho", + "reactants": { + "TERPROD2": { } + }, + "products": { + "RO2": { "yield": 0.15 }, + "CH2O": { "yield": 0.68 }, + "CO2": { "yield": 0.8 }, + "CH3COCH3": { "yield": 0.5 }, + "CH3CO3": { "yield": 0.65 }, + "HO2": { "yield": 1.2 }, + "CO": { "yield": 1.7 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.0e-13, + "Ea": -9.66454e-21, + "reactants": { + "MACRO2": { }, + "HO2": { } + }, + "products": { + "MACROOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.8e-12, + "Ea": 4.28001e-21, + "reactants": { + "BR": { }, + "HO2": { } + }, + "products": { + "HBR": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": -4.97034e-21, + "reactants": { + "NO": { }, + "MACRO2": { } + }, + "products": { + "NO2": { }, + "HO2": { "yield": 0.47 }, + "CH2O": { "yield": 0.25 }, + "GLYALD": { "yield": 0.53 }, + "CH3COCHO": { "yield": 0.25 }, + "CH3CO3": { "yield": 0.53 }, + "HYAC": { "yield": 0.22 }, + "CO": { "yield": 0.22 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.05e-14, + "Ea": 2.7613e-20, + "reactants": { + "ISOP": { }, + "O3": { } + }, + "products": { + "ISOP": { }, + "O3": { }, + "SOAG3": { "yield": 0.0033 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmek->,jacet", + "reactants": { + "MEK": { } + }, + "products": { + "CH3CO3": { }, + "C2H5O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jacet", + "reactants": { + "CH3COCH3": { } + }, + "products": { + "CH3CO3": { }, + "CH3O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-12, + "reactants": { + "SO": { }, + "OCLO": { } + }, + "products": { + "SO2": { }, + "CLO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": -1.43587e-20, + "reactants": { + "ACBZO2": { }, + "HO2": { } + }, + "products": { + "C6H5O2": { "yield": 0.4 }, + "OH": { "yield": 0.4 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "XYLEO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.1677 }, + "SOAG1": { "yield": 0.0174 }, + "SOAG2": { "yield": 0.086 }, + "SOAG3": { "yield": 0.0512 }, + "SOAG4": { "yield": 0.1598 } + } + }, + { + "type": "ARRHENIUS", + "A": 5.9e-12, + "Ea": -3.10646e-21, + "reactants": { + "BZALD": { }, + "OH": { } + }, + "products": { + "ACBZO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e-10, + "Ea": 3.58969e-21, + "reactants": { + "CH4": { }, + "F": { } + }, + "products": { + "HF": { }, + "CH3O2": { } + } + }, + { + "type": "TROE", + "k0_A": 9.0e-32, + "k0_B": -1.5, + "kinf_A": 3.0e-11, + "N": 0, + "reactants": { + "NO": { }, + "O": { } + }, + "products": { + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.31e-6, + "reactants": { + "NH_5": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "reactants": { + "ISOPAO2": { }, + "CH3CO3": { } + }, + "products": { + "CH3O2": { }, + "HO2": { }, + "CH2O": { }, + "MACR": { "yield": 0.39 }, + "MVK": { "yield": 0.61 }, + "CO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.82e-11, + "Ea": 2.7613e-20, + "reactants": { + "CH3COCH3": { }, + "OH": { } + }, + "products": { + "RO2": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.33e-13, + "reactants": { + "CH3COCH3": { }, + "OH": { } + }, + "products": { + "RO2": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.5e-13, + "Ea": -8.42196e-21, + "reactants": { + "HO2NO2": { }, + "OH": { } + }, + "products": { + "H2O": { }, + "NO2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-11, + "reactants": { + "NO3": { }, + "O": { } + }, + "products": { + "NO2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.7e-12, + "reactants": { + "ALKO2": { }, + "NO": { } + }, + "products": { + "CH3CHO": { "yield": 0.4 }, + "CH2O": { "yield": 0.1 }, + "CH3COCH3": { "yield": 0.25 }, + "HO2": { }, + "MEK": { "yield": 0.8 }, + "NO2": { } + } + }, + { + "type": "TROE", + "k0_A": 1.9e-31, + "k0_B": -3.4, + "kinf_A": 4.0e-12, + "N": 0.3, + "reactants": { + "NO2": { }, + "HO2": { } + }, + "products": { + "HO2NO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcf2clbr", + "reactants": { + "CF2CLBR": { } + }, + "products": { + "BR": { }, + "CL": { }, + "COF2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jh2o_c", + "reactants": { + "H2O": { } + }, + "products": { + "H": { "yield": 2 }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.0e-11, + "reactants": { + "ISOPNITB": { }, + "OH": { } + }, + "products": { + "HYAC": { "yield": 0.5 }, + "GLYALD": { "yield": 0.5 }, + "NOA": { "yield": 0.5 }, + "HO2": { }, + "HONITR": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.4e-11, + "reactants": { + "OH": { }, + "XYLOL": { } + }, + "products": { + "XYLOLO2": { "yield": 0.3 }, + "HO2": { "yield": 0.63 }, + "PHENO": { "yield": 0.07 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-11, + "Ea": -4.55614e-21, + "reactants": { + "SO": { }, + "OH": { } + }, + "products": { + "SO2": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.6e-12, + "Ea": -7.31744e-21, + "reactants": { + "MCO3": { }, + "CH3CO3": { } + }, + "products": { + "CO2": { "yield": 2 }, + "CH3O2": { }, + "CH2O": { }, + "CH3CO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jxylolooh->,jch3ooh", + "reactants": { + "XYLOLOOH": { } + }, + "products": { + "OH": { }, + "GLYOXAL": { "yield": 0.17 }, + "CH3COCHO": { "yield": 0.51 }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jso2", + "reactants": { + "SO2": { } + }, + "products": { + "SO": { }, + "O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmekooh->,jch3ooh", + "reactants": { + "MEKOOH": { } + }, + "products": { + "OH": { }, + "CH3CO3": { }, + "CH3CHO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-11, + "reactants": { + "OH": { }, + "XYLENES": { } + }, + "products": { + "XYLENES": { }, + "OH": { }, + "XYLEO2VBS": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.4e-12, + "Ea": 1.79484e-21, + "reactants": { + "HOCL": { }, + "CL": { } + }, + "products": { + "HCL": { }, + "CLO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.7e-11, + "reactants": { + "CRESOL": { }, + "OH": { } + }, + "products": { + "PHENO2": { "yield": 0.2 }, + "HO2": { "yield": 0.73 }, + "PHENO": { "yield": 0.07 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.15e-14, + "Ea": -1.2702e-20, + "reactants": { + "OH": { }, + "CH3COOH": { } + }, + "products": { + "CH3O2": { }, + "CO2": { }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jphenooh->,jch3ooh", + "reactants": { + "PHENOOH": { } + }, + "products": { + "OH": { }, + "HO2": { }, + "GLYOXAL": { "yield": 0.7 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-12, + "reactants": { + "OH": { "qty": 2 } + }, + "products": { + "H2O": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-11, + "Ea": 3.38259e-20, + "reactants": { + "CLO": { "qty": 2 } + }, + "products": { + "CL": { "yield": 2 }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-12, + "Ea": -8.83615e-21, + "reactants": { + "XO2": { }, + "CH3CO3": { } + }, + "products": { + "CO": { "yield": 0.25 }, + "CH2O": { "yield": 0.25 }, + "GLYOXAL": { "yield": 0.25 }, + "CH3O2": { }, + "HO2": { }, + "CH3COCHO": { "yield": 0.25 }, + "HYAC": { "yield": 0.25 }, + "GLYALD": { "yield": 0.25 }, + "CO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-12, + "reactants": { + "NO3": { }, + "XO2": { } + }, + "products": { + "NO2": { }, + "HO2": { }, + "CO": { "yield": 0.5 }, + "HYAC": { "yield": 0.25 }, + "GLYOXAL": { "yield": 0.25 }, + "CH3COCHO": { "yield": 0.25 }, + "GLYALD": { "yield": 0.25 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "TERP2O2": { }, + "NO": { } + }, + "products": { + "ONITR": { "yield": 0.1 }, + "NO2": { "yield": 0.9 }, + "CH2O": { "yield": 0.34 }, + "CH3COCH3": { "yield": 0.27 }, + "CO": { "yield": 0.225 }, + "CO2": { "yield": 0.9 }, + "TERPROD2": { "yield": 0.9 }, + "HO2": { "yield": 0.9 }, + "GLYALD": { "yield": 0.225 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.42e-12, + "Ea": 1.58775e-20, + "reactants": { + "CH3BR": { }, + "OH": { } + }, + "products": { + "BR": { }, + "H2O": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa4_a1->,.0004*jno2", + "reactants": { + "soa4_a1": { } + }, + "products": { + + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jno2", + "reactants": { + "NO2": { } + }, + "products": { + "NO": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-11, + "reactants": { + "OH": { }, + "GLYALD": { } + }, + "products": { + "HO2": { }, + "GLYOXAL": { "yield": 0.2 }, + "CH2O": { "yield": 0.8 }, + "CO2": { "yield": 0.8 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "NTERPO2": { }, + "HO2": { } + }, + "products": { + "NTERPOOH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbigald3->,.20*jno2", + "reactants": { + "BIGALD3": { } + }, + "products": { + "HO2": { "yield": 0.6 }, + "CO": { "yield": 0.6 }, + "MDIALO2": { "yield": 0.6 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "POOH": { } + }, + "products": { + "PO2": { "yield": 0.5 }, + "OH": { "yield": 0.5 }, + "HYAC": { "yield": 0.5 }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.75e-11, + "reactants": { + "O1D": { }, + "CF2CLBR": { } + }, + "products": { + "CL": { }, + "BR": { }, + "COF2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch2br2", + "reactants": { + "CH2BR2": { } + }, + "products": { + "BR": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhf", + "reactants": { + "HF": { } + }, + "products": { + "H": { }, + "F": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbepomuc->,.10*jno2", + "reactants": { + "BEPOMUC": { } + }, + "products": { + "BIGALD1": { }, + "HO2": { "yield": 1.5 }, + "CO": { "yield": 1.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.6e-13, + "Ea": -9.66454e-21, + "reactants": { + "RO2": { }, + "HO2": { } + }, + "products": { + "ROOH": { "yield": 0.85 }, + "OH": { "yield": 0.15 }, + "CH2O": { "yield": 0.15 }, + "CH3CO3": { "yield": 0.15 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-12, + "reactants": { + "NO3": { }, + "MACRO2": { } + }, + "products": { + "NO2": { }, + "HO2": { "yield": 0.47 }, + "CH2O": { "yield": 0.25 }, + "CH3COCHO": { "yield": 0.25 }, + "CO": { "yield": 0.22 }, + "GLYALD": { "yield": 0.53 }, + "HYAC": { "yield": 0.22 }, + "CH3CO3": { "yield": 0.53 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "Ea": -6.90325e-21, + "reactants": { + "CH3O2": { }, + "NTERPO2": { } + }, + "products": { + "TERPNIT": { "yield": 0.5 }, + "CH2O": { "yield": 0.75 }, + "CH3OH": { "yield": 0.25 }, + "HO2": { "yield": 0.5 }, + "TERPROD1": { "yield": 0.5 }, + "NO2": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-11, + "Ea": -2.7613e-21, + "reactants": { + "MACROOH": { }, + "OH": { } + }, + "products": { + "MCO3": { "yield": 0.5 }, + "MACRO2": { "yield": 0.2 }, + "OH": { "yield": 0.1 }, + "HO2": { "yield": 0.2 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "MEKO2": { }, + "HO2": { } + }, + "products": { + "MEKOOH": { "yield": 0.8 }, + "OH": { "yield": 0.2 }, + "CH3CHO": { "yield": 0.2 }, + "CH3CO3": { "yield": 0.2 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.4e-12, + "Ea": 1.51871e-20, + "reactants": { + "SO": { }, + "O3": { } + }, + "products": { + "SO2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-12, + "Ea": 2.66465e-21, + "reactants": { + "BENZENE": { }, + "OH": { } + }, + "products": { + "BENZENE": { }, + "OH": { }, + "BENZO2VBS": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.0e-14, + "reactants": { + "PAN": { }, + "OH": { } + }, + "products": { + "CH2O": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "TERPO2": { }, + "HO2": { } + }, + "products": { + "TERPOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-12, + "reactants": { + "NO3": { }, + "MCO3": { } + }, + "products": { + "NO2": { }, + "CH2O": { }, + "CH3CO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jn2o", + "reactants": { + "N2O": { } + }, + "products": { + "O1D": { }, + "N2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch2o_a", + "reactants": { + "CH2O": { } + }, + "products": { + "CO": { }, + "H": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-11, + "Ea": 2.7613e-21, + "reactants": { + "O3": { }, + "CL": { } + }, + "products": { + "CLO": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-11, + "Ea": -1.72581e-21, + "reactants": { + "NO3": { }, + "NO": { } + }, + "products": { + "NO2": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "EO2": { }, + "HO2": { } + }, + "products": { + "EOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.8e-13, + "Ea": 1.44968e-20, + "reactants": { + "CH3CN": { }, + "OH": { } + }, + "products": { + "HO2": { } + } + }, + { + "type": "TROE", + "k0_A": 9.04762e-5, + "k0_B": -3.4, + "k0_C": -10900, + "kinf_A": 1.90476e+15, + "kinf_C": -10900, + "N": 0.3, + "reactants": { + "HO2NO2": { } + }, + "products": { + "HO2": { }, + "NO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhcfc22", + "reactants": { + "HCFC22": { } + }, + "products": { + "CL": { }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-12, + "Ea": 1.29781e-20, + "reactants": { + "OH": { }, + "O3": { } + }, + "products": { + "HO2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-13, + "Ea": 3.38259e-20, + "reactants": { + "O3": { }, + "NO2": { } + }, + "products": { + "NO3": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.05e-11, + "Ea": 3.13407e-20, + "reactants": { + "CL": { }, + "H2": { } + }, + "products": { + "HCL": { }, + "H": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jnc4cho->,jch2o_a", + "reactants": { + "NC4CHO": { } + }, + "products": { + "BIGALD3": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.5e-15, + "Ea": 2.62323e-20, + "reactants": { + "O3": { }, + "C3H6": { } + }, + "products": { + "CH2O": { "yield": 0.5 }, + "HCOOH": { "yield": 0.12 }, + "CH3COOH": { "yield": 0.12 }, + "CH3CHO": { "yield": 0.5 }, + "CO": { "yield": 0.56 }, + "CH3O2": { "yield": 0.28 }, + "CH4": { "yield": 0.1 }, + "CO2": { "yield": 0.2 }, + "HO2": { "yield": 0.28 }, + "OH": { "yield": 0.36 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhonitr->,jch2o_a", + "reactants": { + "HONITR": { } + }, + "products": { + "NO2": { }, + "HO2": { "yield": 0.67 }, + "CH3CHO": { "yield": 0.33 }, + "CH2O": { "yield": 0.33 }, + "CO": { "yield": 0.33 }, + "GLYALD": { "yield": 0.33 }, + "CH3CO3": { "yield": 0.33 }, + "HYAC": { "yield": 0.17 }, + "CH3COCH3": { "yield": 0.17 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "C6H5O2": { }, + "NO": { } + }, + "products": { + "PHENO": { }, + "NO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jxooh->,jch3ooh", + "reactants": { + "XOOH": { } + }, + "products": { + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-12, + "reactants": { + "NO3": { }, + "ISOPBO2": { } + }, + "products": { + "NO2": { }, + "HYDRALD": { "yield": 0.95 }, + "HO2": { }, + "GLYOXAL": { "yield": 0.05 }, + "GLYALD": { "yield": 0.05 }, + "CH3COCHO": { "yield": 0.05 }, + "HYAC": { "yield": 0.05 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.26e-11, + "Ea": -2.7613e-22, + "reactants": { + "N2O": { }, + "O1D": { } + }, + "products": { + "NO": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-10, + "reactants": { + "OH": { }, + "NC4CHO": { } + }, + "products": { + "GLYOXAL": { }, + "NOA": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch4_a", + "reactants": { + "CH4": { } + }, + "products": { + "H": { }, + "CH3O2": { } + } + }, + { + "type": "TROE", + "k0_A": 5.2e-31, + "k0_B": -3.2, + "kinf_A": 6.9e-12, + "N": 2.9, + "reactants": { + "BRO": { }, + "NO2": { } + }, + "products": { + "BRONO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhobr", + "reactants": { + "HOBR": { } + }, + "products": { + "BR": { }, + "OH": { } + } + }, + { + "type": "TROE", + "k0_A": 1.6e-29, + "k0_B": -3.3, + "kinf_A": 3.1e-10, + "reactants": { + "C2H4": { }, + "CL": { } + }, + "products": { + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": -4.97034e-21, + "reactants": { + "NO": { }, + "MTERPO2VBS": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.0245 }, + "SOAG1": { "yield": 0.0082 }, + "SOAG2": { "yield": 0.0772 }, + "SOAG3": { "yield": 0.0332 }, + "SOAG4": { "yield": 0.13 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "XYLEO2VBS": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.0063 }, + "SOAG1": { "yield": 0.0237 }, + "SOAG2": { "yield": 0.0025 }, + "SOAG3": { "yield": 0.011 }, + "SOAG4": { "yield": 0.1185 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jno3_a", + "reactants": { + "NO3": { } + }, + "products": { + "NO2": { }, + "O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jh2o_a", + "reactants": { + "H2O": { } + }, + "products": { + "OH": { }, + "H": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch3co3h->,0.28*jh2o2", + "reactants": { + "CH3COOOH": { } + }, + "products": { + "CH3O2": { }, + "OH": { }, + "CO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-11, + "Ea": -3.45162e-21, + "reactants": { + "OH": { }, + "BRO": { } + }, + "products": { + "BR": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.8e-12, + "Ea": -3.58969e-21, + "reactants": { + "NO": { }, + "BRO": { } + }, + "products": { + "BR": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-13, + "Ea": -6.35099e-21, + "reactants": { + "HO2": { "qty": 2 } + }, + "products": { + "H2O2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.1e-33, + "Ea": -1.2702e-20, + "reactants": { + "HO2": { "qty": 2 }, + "M": { } + }, + "products": { + "H2O2": { }, + "O2": { }, + "M": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-34, + "Ea": -3.67253e-20, + "reactants": { + "HO2": { "qty": 2 }, + "H2O": { } + }, + "products": { + "H2O2": { }, + "O2": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.94e-54, + "Ea": -4.30762e-20, + "reactants": { + "HO2": { "qty": 2 }, + "M": { }, + "H2O": { } + }, + "products": { + "H2O2": { }, + "O2": { }, + "M": { }, + "H2O": { } + } + }, + { + "type": "TROE", + "k0_A": 6.9e-31, + "k0_B": -1, + "kinf_A": 2.6e-11, + "N": 0, + "reactants": { + "OH": { "qty": 2 } + }, + "products": { + "H2O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jonitr->,jch3cho", + "reactants": { + "ONITR": { } + }, + "products": { + "NO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jterpooh->,jch3ooh", + "reactants": { + "TERPOOH": { } + }, + "products": { + "CH2O": { "yield": 0.4 }, + "CH3COCH3": { "yield": 0.05 }, + "TERPROD1": { }, + "HO2": { }, + "OH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcl2", + "reactants": { + "CL2": { } + }, + "products": { + "CL": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.6e-13, + "Ea": 1.59603e-20, + "reactants": { + "NO3": { }, + "C3H6": { } + }, + "products": { + "NOA": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-11, + "reactants": { + "BCARY": { }, + "NO3": { } + }, + "products": { + "NTERPO2": { } + } + }, + { + "type": "TROE", + "k0_A": 3.57e-43, + "k0_C": 7810, + "kinf_A": 3.09091e-12, + "kinf_C": 350, + "Fc": 1, + "reactants": { + "OH": { }, + "DMS": { } + }, + "products": { + "SO2": { "yield": 0.5 }, + "HO2": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-14, + "reactants": { + "BCARY": { }, + "O3": { } + }, + "products": { + "TERPROD1": { "yield": 0.33 }, + "TERPROD2": { "yield": 0.3 }, + "OH": { "yield": 0.63 }, + "HO2": { "yield": 0.57 }, + "CO": { "yield": 0.23 }, + "CO2": { "yield": 0.27 }, + "CH3COCH3": { "yield": 0.52 }, + "CH2O": { "yield": 0.34 }, + "BIGALD": { "yield": 0.1 }, + "HCOOH": { "yield": 0.05 }, + "BIGALK": { "yield": 0.05 }, + "CH3CO3": { "yield": 0.06 }, + "RO2": { "yield": 0.06 } + } + }, + { + "type": "ARRHENIUS", + "A": 9.2e-13, + "Ea": 2.15381e-20, + "reactants": { + "OH": { }, + "HCFC22": { } + }, + "products": { + "H2O": { }, + "CL": { }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-10, + "reactants": { + "O3": { }, + "O1D": { } + }, + "products": { + "O2": { }, + "O": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch3cho", + "reactants": { + "CH3CHO": { } + }, + "products": { + "CH3O2": { }, + "CO": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "C2H5OOH": { }, + "OH": { } + }, + "products": { + "C2H5O2": { "yield": 0.5 }, + "CH3CHO": { "yield": 0.5 }, + "OH": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 6.8e-14, + "reactants": { + "C2H5O2": { "qty": 2 } + }, + "products": { + "CH3CHO": { "yield": 1.6 }, + "HO2": { "yield": 1.2 }, + "C2H5OH": { "yield": 0.4 } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-11, + "reactants": { + "OH": { }, + "N": { } + }, + "products": { + "NO": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": -4.97034e-21, + "reactants": { + "NO": { }, + "XO2": { } + }, + "products": { + "NO2": { }, + "HO2": { }, + "CO": { "yield": 0.25 }, + "CH2O": { "yield": 0.25 }, + "GLYOXAL": { "yield": 0.25 }, + "CH3COCHO": { "yield": 0.25 }, + "HYAC": { "yield": 0.25 }, + "GLYALD": { "yield": 0.25 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.644e-11, + "reactants": { + "CFC115": { }, + "O1D": { } + }, + "products": { + "CL": { }, + "F": { }, + "COF2": { "yield": 2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jbigald1->,.14*jno2", + "reactants": { + "BIGALD1": { } + }, + "products": { + "MALO2": { "yield": 0.6 }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhno3", + "reactants": { + "HNO3": { } + }, + "products": { + "NO2": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": -1.43587e-20, + "reactants": { + "CH3CO3": { }, + "HO2": { } + }, + "products": { + "CH3COOOH": { "yield": 0.4 }, + "CH3COOH": { "yield": 0.15 }, + "O3": { "yield": 0.15 }, + "OH": { "yield": 0.45 }, + "CH3O2": { "yield": 0.45 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jc2h5ooh->,jch3ooh", + "reactants": { + "C2H5OOH": { } + }, + "products": { + "CH3CHO": { }, + "HO2": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.7e-13, + "Ea": -1.68439e-20, + "reactants": { + "OH": { }, + "PHENOL": { } + }, + "products": { + "PHENO2": { "yield": 0.14 }, + "HO2": { "yield": 0.8 }, + "PHENO": { "yield": 0.06 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.08e-10, + "Ea": -1.44968e-21, + "reactants": { + "HCN": { }, + "O1D": { } + }, + "products": { + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.0e-11, + "reactants": { + "ISOPNOOH": { }, + "OH": { } + }, + "products": { + "NOA": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": -3.03743e-21, + "reactants": { + "N": { }, + "NO2": { } + }, + "products": { + "N2O": { }, + "O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jco2", + "reactants": { + "CO2": { } + }, + "products": { + "CO": { }, + "O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.0e-11, + "reactants": { + "O1D": { }, + "HBR": { } + }, + "products": { + "BR": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.0e-12, + "Ea": -5.5226e-21, + "reactants": { + "HNO3": { }, + "F": { } + }, + "products": { + "HF": { }, + "NO3": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcfc115", + "reactants": { + "CFC115": { } + }, + "products": { + "CL": { }, + "F": { }, + "COF2": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 9.19e-12, + "Ea": 8.69809e-21, + "reactants": { + "OH": { }, + "C3H8": { } + }, + "products": { + "C3H7O2": { }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhcfc141b", + "reactants": { + "HCFC141B": { } + }, + "products": { + "CL": { }, + "COFCL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-11, + "reactants": { + "CH4": { }, + "O1D": { } + }, + "products": { + "CH2O": { }, + "H": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jterprd1->,jch3cho", + "reactants": { + "TERPROD1": { } + }, + "products": { + "HO2": { }, + "CO": { }, + "TERPROD2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jmpan->,jpan", + "reactants": { + "MPAN": { } + }, + "products": { + "MCO3": { }, + "NO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jclono2_a", + "reactants": { + "CLONO2": { } + }, + "products": { + "CL": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-11, + "Ea": 4.55614e-20, + "reactants": { + "HCL": { }, + "O": { } + }, + "products": { + "CL": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.0e-13, + "reactants": { + "HCOOH": { }, + "OH": { } + }, + "products": { + "HO2": { }, + "CO2": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-11, + "Ea": 1.10452e-20, + "reactants": { + "BR": { }, + "CH2O": { } + }, + "products": { + "HBR": { }, + "HO2": { }, + "CO": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa3_a1->,.0004*jno2", + "reactants": { + "soa3_a1": { } + }, + "products": { + + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa4_a2->,.0004*jno2", + "reactants": { + "soa4_a2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 4.5e-12, + "Ea": -6.35099e-21, + "reactants": { + "BRO": { }, + "HO2": { } + }, + "products": { + "HOBR": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.1e-13, + "Ea": -1.03549e-20, + "reactants": { + "CH3O2": { }, + "HO2": { } + }, + "products": { + "CH3OOH": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": -4.97034e-21, + "reactants": { + "ISOPNO3": { }, + "NO": { } + }, + "products": { + "NC4CHO": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcf3br", + "reactants": { + "CF3BR": { } + }, + "products": { + "BR": { }, + "F": { }, + "COF2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.76e-34, + "Ea": -9.94067e-21, + "reactants": { + "O": { "qty": 2 }, + "M": { } + }, + "products": { + "O2": { }, + "M": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.3e-16, + "Ea": 8.00776e-21, + "reactants": { + "O3": { }, + "MTERP": { } + }, + "products": { + "TERPROD1": { "yield": 0.33 }, + "TERPROD2": { "yield": 0.3 }, + "OH": { "yield": 0.63 }, + "HO2": { "yield": 0.57 }, + "CO": { "yield": 0.23 }, + "CO2": { "yield": 0.27 }, + "CH3COCH3": { "yield": 0.52 }, + "CH2O": { "yield": 0.34 }, + "BIGALD": { "yield": 0.1 }, + "HCOOH": { "yield": 0.05 }, + "BIGALK": { "yield": 0.05 }, + "CH3CO3": { "yield": 0.06 }, + "RO2": { "yield": 0.06 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.1e-11, + "Ea": 1.35304e-20, + "reactants": { + "CL": { }, + "H2O2": { } + }, + "products": { + "HCL": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.7e-13, + "reactants": { + "OH": { }, + "NOA": { } + }, + "products": { + "NO2": { }, + "CH3COCHO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.2e-14, + "Ea": 1.47729e-20, + "reactants": { + "OH": { }, + "OCS": { } + }, + "products": { + "SO2": { }, + "CO": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-11, + "reactants": { + "OH": { }, + "TERPOOH": { } + }, + "products": { + "TERPO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-12, + "reactants": { + "BIGALK": { }, + "OH": { } + }, + "products": { + "ALKO2": { } + } + }, + { + "type": "TROE", + "k0_A": 9.7e-29, + "k0_B": -5.6, + "kinf_A": 9.3e-12, + "N": 1.5, + "reactants": { + "DICARBO2": { }, + "NO2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e-11, + "Ea": 1.07691e-20, + "reactants": { + "BR": { }, + "O3": { } + }, + "products": { + "BRO": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-12, + "Ea": 2.66465e-21, + "reactants": { + "BENZENE": { }, + "OH": { } + }, + "products": { + "PHENOL": { "yield": 0.53 }, + "BEPOMUC": { "yield": 0.12 }, + "HO2": { "yield": 0.65 }, + "BENZO2": { "yield": 0.35 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch3cl", + "reactants": { + "CH3CL": { } + }, + "products": { + "CL": { }, + "CH3O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhcl", + "reactants": { + "HCL": { } + }, + "products": { + "H": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-13, + "Ea": -5.5226e-21, + "reactants": { + "CH3O2": { }, + "MACRO2": { } + }, + "products": { + "HO2": { "yield": 0.73 }, + "CH2O": { "yield": 0.88 }, + "CO": { "yield": 0.11 }, + "CH3COCHO": { "yield": 0.24 }, + "GLYALD": { "yield": 0.26 }, + "CH3CO3": { "yield": 0.26 }, + "CH3OH": { "yield": 0.25 }, + "HYAC": { "yield": 0.23 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.0e-13, + "Ea": -9.66454e-21, + "reactants": { + "ISOPAO2": { }, + "HO2": { } + }, + "products": { + "ISOPOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.5e-41, + "Ea": -9.02944e-20, + "reactants": { + "H2O": { }, + "SO3": { } + }, + "products": { + "H2SO4": { }, + "H2O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jtolooh->,jch3ooh", + "reactants": { + "TOLOOH": { } + }, + "products": { + "OH": { }, + "GLYOXAL": { "yield": 0.6 }, + "CH3COCHO": { "yield": 0.4 }, + "HO2": { }, + "BIGALD1": { "yield": 0.2 }, + "BIGALD2": { "yield": 0.2 }, + "BIGALD3": { "yield": 0.2 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch3ccl3", + "reactants": { + "CH3CCL3": { } + }, + "products": { + "CL": { "yield": 3 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-11, + "reactants": { + "NTERPOOH": { }, + "OH": { } + }, + "products": { + "NTERPO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.8e-12, + "Ea": -4.14195e-21, + "reactants": { + "CH3O2": { }, + "NO": { } + }, + "products": { + "CH2O": { }, + "NO2": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-12, + "reactants": { + "ISOPAO2": { }, + "NO3": { } + }, + "products": { + "NO2": { }, + "MACR": { "yield": 0.4 }, + "MVK": { "yield": 0.6 }, + "CH2O": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "BZOO": { }, + "HO2": { } + }, + "products": { + "BZOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "TOLUO2VBS": { } + }, + "products": { + "NO": { }, + "SOAG0": { "yield": 0.0154 }, + "SOAG1": { "yield": 0.0452 }, + "SOAG2": { "yield": 0.0966 }, + "SOAG3": { "yield": 0.0073 }, + "SOAG4": { "yield": 0.238 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-13, + "Ea": -1.79484e-20, + "reactants": { + "MTERPO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.0508 }, + "SOAG1": { "yield": 0.1149 }, + "SOAG2": { "yield": 0.0348 }, + "SOAG3": { "yield": 0.0554 }, + "SOAG4": { "yield": 0.1278 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": -2.48517e-21, + "reactants": { + "NO": { }, + "MEKO2": { } + }, + "products": { + "CH3CO3": { }, + "CH3CHO": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "XYLENO2": { }, + "HO2": { } + }, + "products": { + "XYLENOOH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa2_a1->,.0004*jno2", + "reactants": { + "soa2_a1": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-12, + "Ea": -4.00388e-21, + "reactants": { + "NO": { }, + "DICARBO2": { } + }, + "products": { + "NO2": { }, + "HO2": { "yield": 0.17 }, + "CH3COCHO": { "yield": 0.17 }, + "CO": { "yield": 0.17 }, + "CH3O2": { "yield": 0.83 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jglyald", + "reactants": { + "GLYALD": { } + }, + "products": { + "HO2": { "yield": 2 }, + "CO": { }, + "CH2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "TERP2O2": { }, + "HO2": { } + }, + "products": { + "TERP2OOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-12, + "reactants": { + "HCL": { }, + "O1D": { } + }, + "products": { + "CLO": { }, + "H": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jocs", + "reactants": { + "OCS": { } + }, + "products": { + "S": { }, + "CO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-14, + "reactants": { + "BCARY": { }, + "O3": { } + }, + "products": { + "BCARY": { }, + "O3": { }, + "SOAG0": { "yield": 0.2202 }, + "SOAG1": { "yield": 0.2067 }, + "SOAG2": { "yield": 0.0653 }, + "SOAG3": { "yield": 0.1284 }, + "SOAG4": { "yield": 0.114 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsf6", + "reactants": { + "SF6": { } + }, + "products": { + "sink": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.1e-13, + "Ea": -6.90325e-21, + "reactants": { + "CH3O2": { }, + "RO2": { } + }, + "products": { + "CH3CO3": { "yield": 0.3 }, + "CH2O": { "yield": 0.8 }, + "HO2": { "yield": 0.3 }, + "HYAC": { "yield": 0.2 }, + "CH3COCHO": { "yield": 0.5 }, + "CH3OH": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "XYLOLO2": { }, + "HO2": { } + }, + "products": { + "XYLOLOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-11, + "reactants": { + "BCARY": { }, + "NO3": { } + }, + "products": { + "BCARY": { }, + "NO3": { }, + "SOAG3": { "yield": 0.17493 }, + "SOAG4": { "yield": 0.59019 } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jhocl", + "reactants": { + "HOCL": { } + }, + "products": { + "OH": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.607e-10, + "reactants": { + "O1D": { }, + "CCL4": { } + }, + "products": { + "CL": { "yield": 4 } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": -1.43587e-20, + "reactants": { + "DICARBO2": { }, + "HO2": { } + }, + "products": { + "OH": { "yield": 0.4 }, + "HO2": { "yield": 0.07 }, + "CH3COCHO": { "yield": 0.07 }, + "CO": { "yield": 0.07 }, + "CH3O2": { "yield": 0.33 } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": -2.7613e-21, + "reactants": { + "OH": { }, + "ROOH": { } + }, + "products": { + "RO2": { }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e+11, + "Ea": 5.72969e-20, + "reactants": { + "EO": { } + }, + "products": { + "CH2O": { "yield": 2 }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.05e-14, + "Ea": 2.7613e-20, + "reactants": { + "ISOP": { }, + "O3": { } + }, + "products": { + "MACR": { "yield": 0.3 }, + "MVK": { "yield": 0.2 }, + "HCOOH": { "yield": 0.11 }, + "CO": { "yield": 0.62 }, + "OH": { "yield": 0.32 }, + "HO2": { "yield": 0.37 }, + "CH2O": { "yield": 0.91 }, + "CH3CO3": { "yield": 0.08 }, + "C3H6": { "yield": 0.13 }, + "CH3O2": { "yield": 0.05 } + } + }, + { + "type": "ARRHENIUS", + "A": 5.1e-14, + "Ea": -9.5679e-21, + "reactants": { + "NO": { }, + "ENEO2": { } + }, + "products": { + "HONITR": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jch3ooh", + "reactants": { + "CH3OOH": { } + }, + "products": { + "CH2O": { }, + "H": { }, + "OH": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jclo", + "reactants": { + "CLO": { } + }, + "products": { + "CL": { }, + "O": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcofcl", + "reactants": { + "COFCL": { } + }, + "products": { + "F": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.03e-12, + "Ea": 6.15769e-21, + "reactants": { + "ISOP": { }, + "NO3": { } + }, + "products": { + "ISOPNO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.31e-10, + "reactants": { + "CH4": { }, + "O1D": { } + }, + "products": { + "CH3O2": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-13, + "reactants": { + "BIGENE": { }, + "NO3": { } + }, + "products": { + "NO2": { }, + "CH3CHO": { }, + "CH2O": { "yield": 0.5 }, + "CH3COCH3": { "yield": 0.5 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-12, + "reactants": { + "NO3": { }, + "NTERPO2": { } + }, + "products": { + "NO2": { "yield": 2 }, + "TERPROD1": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.34e-8, + "reactants": { + "NH4": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e+9, + "Ea": 1.14594e-19, + "reactants": { + "ISOPBO2": { } + }, + "products": { + "HPALD": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "BENZO2": { }, + "HO2": { } + }, + "products": { + "BENZOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e-12, + "reactants": { + "OH": { }, + "ALKNIT": { } + }, + "products": { + "CH2O": { "yield": 0.4 }, + "CH3CHO": { "yield": 0.8 }, + "CH3COCH3": { "yield": 0.8 }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.46e-11, + "Ea": 1.43587e-20, + "reactants": { + "CH3BR": { }, + "CL": { } + }, + "products": { + "HCL": { }, + "HO2": { }, + "BR": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.4e-11, + "reactants": { + "OH": { }, + "TERPROD2": { } + }, + "products": { + "RO2": { "yield": 0.15 }, + "CH2O": { "yield": 0.68 }, + "CO2": { "yield": 1.8 }, + "CH3COCH3": { "yield": 0.5 }, + "CH3CO3": { "yield": 0.65 }, + "HO2": { "yield": 0.2 }, + "CO": { "yield": 0.7 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": -5.03937e-21, + "reactants": { + "NO": { }, + "XYLOLO2": { } + }, + "products": { + "HO2": { }, + "NO2": { }, + "GLYOXAL": { "yield": 0.17 }, + "CH3COCHO": { "yield": 0.51 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.86e-11, + "Ea": -2.41614e-21, + "reactants": { + "OH": { }, + "HYDRALD": { } + }, + "products": { + "XO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "Ea": -6.90325e-21, + "reactants": { + "TERP2O2": { }, + "CH3O2": { } + }, + "products": { + "TERPROD2": { }, + "CH2O": { "yield": 0.93 }, + "CH3OH": { "yield": 0.25 }, + "HO2": { }, + "CO2": { "yield": 0.5 }, + "CO": { "yield": 0.125 }, + "GLYALD": { "yield": 0.125 }, + "CH3COCH3": { "yield": 0.15 } + } + }, + { + "type": "ARRHENIUS", + "A": 5.5e-12, + "Ea": -1.72581e-21, + "reactants": { + "OH": { }, + "CH2O": { } + }, + "products": { + "CO": { }, + "H2O": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-11, + "Ea": -2.9684e-21, + "reactants": { + "O": { }, + "BRONO2": { } + }, + "products": { + "BRO": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 8.5e-16, + "Ea": 2.09859e-20, + "reactants": { + "O3": { }, + "MVK": { } + }, + "products": { + "CH2O": { "yield": 0.6 }, + "CO": { "yield": 0.56 }, + "CH3CHO": { "yield": 0.1 }, + "CO2": { "yield": 0.1 }, + "CH3CO3": { "yield": 0.28 }, + "CH3COCHO": { "yield": 0.5 }, + "HO2": { "yield": 0.28 }, + "OH": { "yield": 0.36 }, + "HCOOH": { "yield": 0.12 } + } + }, + { + "type": "ARRHENIUS", + "A": 8.1e-11, + "Ea": 4.14195e-22, + "reactants": { + "CL": { }, + "CH2O": { } + }, + "products": { + "HCL": { }, + "HO2": { }, + "CO": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jo2_a=userdefined,", + "reactants": { + "O2": { } + }, + "products": { + "O": { }, + "O1D": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.0e-13, + "Ea": 5.85395e-21, + "reactants": { + "CH3O2": { "qty": 2 } + }, + "products": { + "CH2O": { "yield": 2 }, + "HO2": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-12, + "Ea": -4.85988e-21, + "reactants": { + "TOLUENE": { }, + "OH": { } + }, + "products": { + "CRESOL": { "yield": 0.18 }, + "TEPOMUC": { "yield": 0.1 }, + "BZOO": { "yield": 0.07 }, + "TOLO2": { "yield": 0.65 }, + "HO2": { "yield": 0.28 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.0e-12, + "Ea": 2.19523e-20, + "reactants": { + "CLO": { "qty": 2 } + }, + "products": { + "CL2": { }, + "O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "IVOCO2VBS": { }, + "HO2": { } + }, + "products": { + "HO2": { }, + "SOAG0": { "yield": 0.2381 }, + "SOAG1": { "yield": 0.1308 }, + "SOAG2": { "yield": 0.0348 }, + "SOAG3": { "yield": 0.0076 }, + "SOAG4": { "yield": 0.0113 } + } + }, + { + "type": "TROE", + "k0_A": 1.07767, + "k0_B": -5.6, + "k0_C": -14000, + "kinf_A": 1.03323e+17, + "kinf_C": -14000, + "N": 1.5, + "reactants": { + "MPAN": { } + }, + "products": { + "MCO3": { }, + "NO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.0e-12, + "Ea": 2.07097e-20, + "reactants": { + "NO": { }, + "O3": { } + }, + "products": { + "NO2": { }, + "O2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jh2402", + "reactants": { + "H2402": { } + }, + "products": { + "BR": { "yield": 2 }, + "COF2": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-11, + "reactants": { + "TERP2OOH": { }, + "OH": { } + }, + "products": { + "TERP2O2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 9.5e-13, + "Ea": -7.59357e-21, + "reactants": { + "BRO": { }, + "CLO": { } + }, + "products": { + "BR": { }, + "OCLO": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.0e-12, + "Ea": 1.15975e-20, + "reactants": { + "CH2BR2": { }, + "OH": { } + }, + "products": { + "BR": { "yield": 2 }, + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-12, + "reactants": { + "OH": { }, + "H2O2": { } + }, + "products": { + "H2O": { }, + "HO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.2e-11, + "reactants": { + "H": { }, + "HO2": { } + }, + "products": { + "OH": { "yield": 2 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-12, + "Ea": 3.45162e-21, + "reactants": { + "HCL": { }, + "OH": { } + }, + "products": { + "H2O": { }, + "CL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 2.088e-10, + "reactants": { + "CFC113": { }, + "O1D": { } + }, + "products": { + "CL": { "yield": 2 }, + "COFCL": { }, + "COF2": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jsoa1_a2->,.0004*jno2", + "reactants": { + "soa1_a2": { } + }, + "products": { + + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-14, + "Ea": -9.74738e-21, + "reactants": { + "CH3O2": { "qty": 2 } + }, + "products": { + "CH2O": { }, + "CH3OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-10, + "Ea": 6.90325e-21, + "reactants": { + "F": { }, + "H2": { } + }, + "products": { + "HF": { }, + "H": { } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-12, + "Ea": 9.80261e-21, + "reactants": { + "NH3": { }, + "OH": { } + }, + "products": { + "H2O": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.85e-12, + "Ea": 1.17355e-20, + "reactants": { + "CHBR3": { }, + "CL": { } + }, + "products": { + "BR": { "yield": 3 }, + "HCL": { } + } + }, + { + "type": "ARRHENIUS", + "A": 3.6e-12, + "Ea": 1.15975e-20, + "reactants": { + "CLONO2": { }, + "O": { } + }, + "products": { + "CLO": { }, + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 4.4e-12, + "Ea": -2.48517e-21, + "reactants": { + "ISOPAO2": { }, + "NO": { } + }, + "products": { + "ISOPNITA": { "yield": 0.08 }, + "NO2": { "yield": 0.92 }, + "MACR": { "yield": 0.36 }, + "MVK": { "yield": 0.56 }, + "CH2O": { "yield": 0.92 }, + "HO2": { "yield": 0.92 } + } + }, + { + "type": "ARRHENIUS", + "A": 1.6e-11, + "Ea": 6.30957e-20, + "reactants": { + "O": { }, + "H2": { } + }, + "products": { + "OH": { }, + "H": { } + } + }, + { + "type": "TROE", + "k0_A": 2.5e-31, + "k0_B": -1.8, + "kinf_A": 2.2e-11, + "N": 0.7, + "reactants": { + "NO2": { }, + "O": { } + }, + "products": { + "NO3": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.8e-12, + "Ea": 2.07097e-20, + "reactants": { + "O": { }, + "HBR": { } + }, + "products": { + "BR": { }, + "OH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 5.4e-11, + "reactants": { + "BIGENE": { }, + "OH": { } + }, + "products": { + "ENEO2": { } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": -9.66454e-21, + "reactants": { + "TOLO2": { }, + "HO2": { } + }, + "products": { + "TOLOOH": { } + } + }, + { + "type": "ARRHENIUS", + "A": 6.5e-12, + "Ea": -1.86388e-21, + "reactants": { + "CLONO2": { }, + "CL": { } + }, + "products": { + "CL2": { }, + "NO3": { } + } + }, + { + "type": "TROE", + "k0_A": 2.4e-30, + "k0_B": -3, + "kinf_A": 1.6e-12, + "N": -0.1, + "reactants": { + "NO3": { }, + "NO2": { } + }, + "products": { + "N2O5": { } + } + }, + { + "type": "PHOTOLYSIS", + "MUSICA name": "jcfcl3", + "reactants": { + "CFC11": { } + }, + "products": { + "CL": { "yield": 2 }, + "COFCL": { } + } + } + ] + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het17", + "reactants": { + "HOBR": { }, + "HCL": { } + }, + "products": { + "BRCL": { }, + "H2O": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het14", + "reactants": { + "BRONO2": { } + }, + "products": { + "HOBR": { }, + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het5", + "reactants": { + "HCL": { }, + "HOCL": { } + }, + "products": { + "CL2": { }, + "H2O": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het10", + "reactants": { + "HCL": { }, + "HOCL": { } + }, + "products": { + "CL2": { }, + "H2O": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het3", + "reactants": { + "BRONO2": { } + }, + "products": { + "HOBR": { }, + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_NO2_aer", + "reaction probability": 8.0e-6, + "gas-phase reactant": "NO2", + "gas-phase products": { + "OH": { "yield": 0.5 }, + "NO": { "yield": 0.5 }, + "HNO3": { "yield": 0.5 } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het2", + "reactants": { + "CLONO2": { } + }, + "products": { + "HOCL": { }, + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het11", + "reactants": { + "BRONO2": { } + }, + "products": { + "HOBR": { }, + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_HO2_aer", + "reaction probability": 0.02, + "gas-phase reactant": "HO2", + "gas-phase products": { + "H2O": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_NO3_aer", + "reaction probability": 0.002, + "gas-phase reactant": "NO3", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het8", + "reactants": { + "CLONO2": { } + }, + "products": { + "HOCL": { }, + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_ISOPNITA_aer", + "reaction probability": 0.005, + "gas-phase reactant": "ISOPNITA", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_ISOPNITB_aer", + "reaction probability": 0.005, + "gas-phase reactant": "ISOPNITB", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het16", + "reactants": { + "HCL": { }, + "HOCL": { } + }, + "products": { + "CL2": { }, + "H2O": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_GLYOXAL_aer", + "reaction probability": 2.0e-4, + "gas-phase reactant": "GLYOXAL", + "gas-phase products": { + "SOAG0": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het13", + "reactants": { + "CLONO2": { } + }, + "products": { + "HOCL": { }, + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_ONITR_aer", + "reaction probability": 0.005, + "gas-phase reactant": "ONITR", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_N2O5_aer", + "reaction probability": 0.02, + "gas-phase reactant": "N2O5", + "gas-phase products": { + "HNO3": { "yield": 2 } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het9", + "reactants": { + "CLONO2": { }, + "HCL": { } + }, + "products": { + "CL2": { }, + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het1", + "reactants": { + "N2O5": { } + }, + "products": { + "HNO3": { "yield": 2 } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het6", + "reactants": { + "HOBR": { }, + "HCL": { } + }, + "products": { + "BRCL": { }, + "H2O": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_HONITR_aer", + "reaction probability": 0.005, + "gas-phase reactant": "HONITR", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het12", + "reactants": { + "N2O5": { } + }, + "products": { + "HNO3": { "yield": 2 } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_NC4CHO_aer", + "reaction probability": 0.02, + "gas-phase reactant": "NC4CHO", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het7", + "reactants": { + "N2O5": { } + }, + "products": { + "HNO3": { "yield": 2 } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het15", + "reactants": { + "CLONO2": { }, + "HCL": { } + }, + "products": { + "CL2": { }, + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_TERPNIT_aer", + "reaction probability": 0.01, + "gas-phase reactant": "TERPNIT", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "SURFACE", + "MUSICA name": "usr_NTERPOOH_aer", + "reaction probability": 0.01, + "gas-phase reactant": "NTERPOOH", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "usr_CO_OH; non-standard algorithm", + "reactants": { + "OH": { }, + "CO": { } + }, + "products": { + "CO2": { }, + "HO2": { } + }, + "MUSICA name": "k_co_oh_jpl19" + }, + { + "type": "SURFACE", + "MUSICA name": "usr_NC4CH2OH_aer", + "reaction probability": 0.005, + "gas-phase reactant": "NC4CH2OH", + "gas-phase products": { + "HNO3": { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "het4", + "reactants": { + "CLONO2": { }, + "HCL": { } + }, + "products": { + "CL2": { }, + "HNO3": { } + } + } + ] +} diff --git a/examples/TS1/species.json b/examples/TS1/species.json new file mode 100644 index 000000000..6d7e014fc --- /dev/null +++ b/examples/TS1/species.json @@ -0,0 +1,1281 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + }, + { + "name": "ALKNIT", + "type": "CHEM_SPEC", + "__description": "standard alkyl nitrate from BIGALK+OH chemistry", + "molecular weight [kg mol-1]": 0.133141 + }, + { + "name": "BZOOH", + "type": "CHEM_SPEC", + "__description": "hydroperoxide from toluene oxidation", + "molecular weight [kg mol-1]": 0.124135 + }, + { + "name": "C6H5OOH", + "type": "CHEM_SPEC", + "__description": "phenyl hydroperoxide", + "molecular weight [kg mol-1]": 0.110109 + }, + { + "name": "COF2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "O2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "COFCL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "HF", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "F", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "BENZO2", + "type": "CHEM_SPEC", + "__description": "bicyclic peroxy radical from OH + benzene", + "molecular weight [kg mol-1]": 0.159115 + }, + { + "name": "BZOO", + "type": "CHEM_SPEC", + "__description": "peroxy radical from toluene oxidation", + "molecular weight [kg mol-1]": 0.123128 + }, + { + "name": "N2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "E90", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "NH_5", + "type": "CHEM_SPEC", + "__description": "idealized tracer with 5 day loss rate", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "NH_50", + "type": "CHEM_SPEC", + "__description": "idealized tracer with 50-day loss rate", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "ST80_25", + "type": "CHEM_SPEC", + "__description": "Stratospheric loss tracer", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "PAN", + "type": "CHEM_SPEC", + "__description": "peroxy acetyl nitrate", + "molecular weight [kg mol-1]": 0.121048 + }, + { + "name": "MVK", + "type": "CHEM_SPEC", + "__description": "methyl vinyl ketone", + "molecular weight [kg mol-1]": 0.0700878 + }, + { + "name": "MACROOH", + "type": "CHEM_SPEC", + "__description": "peroxide from methacrolein", + "molecular weight [kg mol-1]": 0.120101 + }, + { + "name": "SOAG0", + "type": "CHEM_SPEC", + "__description": "SOA gas-phase precursor VBS bin 0", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "SOAG1", + "type": "CHEM_SPEC", + "__description": "SOA gas-phase precursor VBS bin 1", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "SOAG2", + "type": "CHEM_SPEC", + "__description": "SOA gas-phase precursor VBS bin 2", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "SOAG3", + "type": "CHEM_SPEC", + "__description": "SOA gas-phase precursor VBS bin 3", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "SOAG4", + "type": "CHEM_SPEC", + "__description": "SOA gas-phase precursor VBS bin 4", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa4_a1", + "type": "CHEM_SPEC", + "__description": "SOA bin 4, MAM accumulation mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa5_a1", + "type": "CHEM_SPEC", + "__description": "SOA bin 5, MAM accumulation mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa5_a2", + "type": "CHEM_SPEC", + "__description": "SOA bin 5, MAM Aitken mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa3_a1", + "type": "CHEM_SPEC", + "__description": "SOA bin 3, MAM accumulation mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa2_a1", + "type": "CHEM_SPEC", + "__description": "SOA bin 2, MAM accumulation mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa1_a1", + "type": "CHEM_SPEC", + "__description": "SOA bin 1, MAM accumulation mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa1_a2", + "type": "CHEM_SPEC", + "__description": "SOA bin 1, MAM Aitken mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa2_a2", + "type": "CHEM_SPEC", + "__description": "SOA bin 2, MAM Aitken mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa3_a2", + "type": "CHEM_SPEC", + "__description": "SOA bin 3, MAM Aitken mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "soa4_a2", + "type": "CHEM_SPEC", + "__description": "SOA bin 4, MAM Aitken mode", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "ISOPNITB", + "type": "CHEM_SPEC", + "__description": "1,4-hydroxynitrate from OH+isoprene chemistry", + "molecular weight [kg mol-1]": 0.147126, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "SO3", + "type": "CHEM_SPEC", + "__description": "sulfur trioxide", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "OCS", + "type": "CHEM_SPEC", + "__description": "carbonyl sulfide", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "SO", + "type": "CHEM_SPEC", + "__description": "sulfur monoxide", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "S", + "type": "CHEM_SPEC", + "__description": "atomic sulfur", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "BCARYO2VBS", + "type": "CHEM_SPEC", + "__description": "BCARY oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "SF6", + "type": "CHEM_SPEC", + "__description": "sulfur hexafluoride", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "sink", + "type": "CHEM_SPEC", + "__description": "sink for sulfur hexaflouride", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "H", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0010074 + }, + { + "name": "MEK", + "type": "CHEM_SPEC", + "__description": "methyl ethyl ketone", + "molecular weight [kg mol-1]": 0.0721026 + }, + { + "name": "MTERP", + "type": "CHEM_SPEC", + "__description": "lumped monoterpenes", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "N2O5", + "type": "CHEM_SPEC", + "__description": "dinitrogen pentoxide", + "molecular weight [kg mol-1]": 0.10801, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "HCN", + "type": "CHEM_SPEC", + "__description": "hydrogen cyanide", + "molecular weight [kg mol-1]": 0.0270251 + }, + { + "name": "SVOC", + "type": "CHEM_SPEC", + "__description": "semi-volatile organic precursor of VBS SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "ISOPNO3", + "type": "CHEM_SPEC", + "__description": "peroxy radical from isoprene NO3 oxidation", + "molecular weight [kg mol-1]": 0.162118 + }, + { + "name": "RO2", + "type": "CHEM_SPEC", + "__description": "peroxy radical from acetone", + "molecular weight [kg mol-1]": 0.0890682 + }, + { + "name": "PHENO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.175114 + }, + { + "name": "BENZO2VBS", + "type": "CHEM_SPEC", + "__description": "benzene oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "IVOC", + "type": "CHEM_SPEC", + "__description": "intermediate volatility organic precursor of VBS SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "TERPNIT", + "type": "CHEM_SPEC", + "__description": "mostly hydroxynitrates from OH+terpene chemistry", + "molecular weight [kg mol-1]": 0.21524, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "ISOPO2VBS", + "type": "CHEM_SPEC", + "__description": "isoprene oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "IVOCO2VBS", + "type": "CHEM_SPEC", + "__description": "IVOC oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "HNO3", + "type": "CHEM_SPEC", + "__description": "nitric acid", + "molecular weight [kg mol-1]": 0.0630123 + }, + { + "name": "ACBZO2", + "type": "CHEM_SPEC", + "__description": "acylperoxy radical from benzaldehyde", + "molecular weight [kg mol-1]": 0.137112 + }, + { + "name": "CH3COOOH", + "type": "CHEM_SPEC", + "__description": "peracetic acid", + "molecular weight [kg mol-1]": 0.0760498 + }, + { + "name": "SO2", + "type": "CHEM_SPEC", + "__description": "sulfur dioxide", + "molecular weight [kg mol-1]": 0.0640648 + }, + { + "name": "MTERPO2VBS", + "type": "CHEM_SPEC", + "__description": "MTERP oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "CH4", + "type": "CHEM_SPEC", + "__description": "methane", + "molecular weight [kg mol-1]": 0.0160406 + }, + { + "name": "CH3CL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0504859 + }, + { + "name": "CH3CO3", + "type": "CHEM_SPEC", + "__description": "acetylperoxy radical", + "molecular weight [kg mol-1]": 0.0750424 + }, + { + "name": "C6H5O2", + "type": "CHEM_SPEC", + "__description": "phenylperoxy radical", + "molecular weight [kg mol-1]": 0.109102 + }, + { + "name": "TERPROD1", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.168227 + }, + { + "name": "HYAC", + "type": "CHEM_SPEC", + "__description": "hydroxyacetone", + "molecular weight [kg mol-1]": 0.0740762 + }, + { + "name": "HPALD", + "type": "CHEM_SPEC", + "__description": "hydroperoxyaldehyde", + "molecular weight [kg mol-1]": 0.116112 + }, + { + "name": "TOLUO2VBS", + "type": "CHEM_SPEC", + "__description": "toluene oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "H2O", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0180142 + }, + { + "name": "NO2", + "type": "CHEM_SPEC", + "__description": "nitrogen dioxide", + "molecular weight [kg mol-1]": 0.0460055, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "EOOH", + "type": "CHEM_SPEC", + "__description": "hydroxyhydroperoxide from OH + ethene chemistry", + "molecular weight [kg mol-1]": 0.0780646 + }, + { + "name": "NTERPOOH", + "type": "CHEM_SPEC", + "__description": "nitrooxy-hydroperoxide from NO3+terpene chemistry", + "molecular weight [kg mol-1]": 0.23124, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "XYLEO2VBS", + "type": "CHEM_SPEC", + "__description": "xylenes oxidation proxy for NOx-dependent VBS-SOA", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "CCL4", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.153822 + }, + { + "name": "CF2CLBR", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.165365 + }, + { + "name": "CF3BR", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.14891 + }, + { + "name": "CFC11", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.137368 + }, + { + "name": "CFC113", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.187375 + }, + { + "name": "CFC114", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.170921 + }, + { + "name": "CFC115", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.154467 + }, + { + "name": "CFC12", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.120913 + }, + { + "name": "CH2BR2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.173834 + }, + { + "name": "CH3BR", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0949372 + }, + { + "name": "CH3CCL3", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.133402 + }, + { + "name": "NO", + "type": "CHEM_SPEC", + "__description": "nitric oxide", + "molecular weight [kg mol-1]": 0.0300061 + }, + { + "name": "BR", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.079904 + }, + { + "name": "BRCL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.115357 + }, + { + "name": "BRO", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0959034 + }, + { + "name": "BRONO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.141909 + }, + { + "name": "CL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0354527 + }, + { + "name": "CL2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0709054 + }, + { + "name": "CL2O2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.102904 + }, + { + "name": "CLO", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0514521 + }, + { + "name": "CLONO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0974576 + }, + { + "name": "HCOOH", + "type": "CHEM_SPEC", + "__description": "formic acid", + "molecular weight [kg mol-1]": 0.0460246 + }, + { + "name": "HBR", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0809114 + }, + { + "name": "HOBR", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0969108 + }, + { + "name": "HOCL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0524595 + }, + { + "name": "N", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0140067 + }, + { + "name": "BIGENE", + "type": "CHEM_SPEC", + "__description": "lumped alkenes C>3", + "molecular weight [kg mol-1]": 0.0561032 + }, + { + "name": "C2H4", + "type": "CHEM_SPEC", + "__description": "ethene", + "molecular weight [kg mol-1]": 0.0280516 + }, + { + "name": "C2H5O2", + "type": "CHEM_SPEC", + "__description": "ethylperoxy radical", + "molecular weight [kg mol-1]": 0.0610578 + }, + { + "name": "CH3COCHO", + "type": "CHEM_SPEC", + "__description": "methyl glyoxal", + "molecular weight [kg mol-1]": 0.0720614 + }, + { + "name": "CH3COCH3", + "type": "CHEM_SPEC", + "__description": "acetone", + "molecular weight [kg mol-1]": 0.0580768 + }, + { + "name": "O", + "type": "CHEM_SPEC", + "__description": "ground state atomic oxygen", + "molecular weight [kg mol-1]": 0.0159994 + }, + { + "name": "OCLO", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0674515 + }, + { + "name": "O1D", + "type": "CHEM_SPEC", + "__description": "excited state atomic oxygen", + "molecular weight [kg mol-1]": 0.0159994 + }, + { + "name": "PHENO", + "type": "CHEM_SPEC", + "__description": "phenoxy radical", + "molecular weight [kg mol-1]": 0.159115 + }, + { + "name": "HCFC141B", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.116948 + }, + { + "name": "HCFC142B", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.100494 + }, + { + "name": "HCFC22", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0864679 + }, + { + "name": "DMS", + "type": "CHEM_SPEC", + "__description": "dimethyl sulfide", + "molecular weight [kg mol-1]": 0.0621324 + }, + { + "name": "C2H5OH", + "type": "CHEM_SPEC", + "__description": "ethanol", + "molecular weight [kg mol-1]": 0.0460658 + }, + { + "name": "HCL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0364601 + }, + { + "name": "BEPOMUC", + "type": "CHEM_SPEC", + "__description": "unsaturated dialdehydic epoxide from OH + benzene", + "molecular weight [kg mol-1]": 0.126109 + }, + { + "name": "CHBR3", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.25273 + }, + { + "name": "H2402", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.259824 + }, + { + "name": "CO2", + "type": "CHEM_SPEC", + "__description": "carbon dioxide", + "molecular weight [kg mol-1]": 0.0440098 + }, + { + "name": "BZALD", + "type": "CHEM_SPEC", + "__description": "benzaldehyde", + "molecular weight [kg mol-1]": 0.106121 + }, + { + "name": "BENZENE", + "type": "CHEM_SPEC", + "__description": "benzene", + "molecular weight [kg mol-1]": 0.0781104 + }, + { + "name": "C3H7O2", + "type": "CHEM_SPEC", + "__description": "propylperoxy radical", + "molecular weight [kg mol-1]": 0.0750836 + }, + { + "name": "CH3O2", + "type": "CHEM_SPEC", + "__description": "methylperoxy radical", + "molecular weight [kg mol-1]": 0.047032 + }, + { + "name": "BCARY", + "type": "CHEM_SPEC", + "__description": "beta-caryophyllene", + "molecular weight [kg mol-1]": 0.204343 + }, + { + "name": "BIGALD", + "type": "CHEM_SPEC", + "__description": "lumped aldehyde from terpene ozonolysis", + "molecular weight [kg mol-1]": 0.0980982 + }, + { + "name": "BIGALD2", + "type": "CHEM_SPEC", + "__description": "4-oxy-2-pentenal, a product of aromatic oxidation", + "molecular weight [kg mol-1]": 0.0980982 + }, + { + "name": "BIGALD3", + "type": "CHEM_SPEC", + "__description": "2-methyl butenedial, a product of aromatic oxidation", + "molecular weight [kg mol-1]": 0.0980982 + }, + { + "name": "BIGALD4", + "type": "CHEM_SPEC", + "__description": "2-methyl-4-oxo-2-pentenal, a product of aromatic oxidation", + "molecular weight [kg mol-1]": 0.112124 + }, + { + "name": "BIGALK", + "type": "CHEM_SPEC", + "__description": "lumped alkanes C>3", + "molecular weight [kg mol-1]": 0.0721438 + }, + { + "name": "H2O2", + "type": "CHEM_SPEC", + "__description": "hydrogen peroxide", + "molecular weight [kg mol-1]": 0.0340136 + }, + { + "name": "C2H5OOH", + "type": "CHEM_SPEC", + "__description": "ethyl hydroperoxide", + "molecular weight [kg mol-1]": 0.0620652 + }, + { + "name": "C2H6", + "type": "CHEM_SPEC", + "__description": "ethane", + "molecular weight [kg mol-1]": 0.0300664 + }, + { + "name": "C3H8", + "type": "CHEM_SPEC", + "__description": "propane", + "molecular weight [kg mol-1]": 0.0440922 + }, + { + "name": "C3H6", + "type": "CHEM_SPEC", + "__description": "propene", + "molecular weight [kg mol-1]": 0.0420774 + }, + { + "name": "CH2O", + "type": "CHEM_SPEC", + "__description": "formaldehyde", + "molecular weight [kg mol-1]": 0.0300252 + }, + { + "name": "CH3CN", + "type": "CHEM_SPEC", + "__description": "acetonitrile", + "molecular weight [kg mol-1]": 0.0410509 + }, + { + "name": "C2H2", + "type": "CHEM_SPEC", + "__description": "ethyne", + "molecular weight [kg mol-1]": 0.0260368 + }, + { + "name": "CH3OH", + "type": "CHEM_SPEC", + "__description": "methanol", + "molecular weight [kg mol-1]": 0.03204 + }, + { + "name": "CH3OOH", + "type": "CHEM_SPEC", + "__description": "methyl hydroperoxide", + "molecular weight [kg mol-1]": 0.0480394 + }, + { + "name": "CRESOL", + "type": "CHEM_SPEC", + "__description": "lumped cresols (hydroxymethylbenzenes)", + "molecular weight [kg mol-1]": 0.108136 + }, + { + "name": "ENEO2", + "type": "CHEM_SPEC", + "__description": "lumped hydroxyperoxy radical from OH + large alkenes", + "molecular weight [kg mol-1]": 0.105109 + }, + { + "name": "MACRO2", + "type": "CHEM_SPEC", + "__description": "peroxy radical from methacrolein oxidation", + "molecular weight [kg mol-1]": 0.119093 + }, + { + "name": "ISOPAO2", + "type": "CHEM_SPEC", + "__description": "1,2-isomer of isoprene peroxy radical", + "molecular weight [kg mol-1]": 0.11712 + }, + { + "name": "MALO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.115064 + }, + { + "name": "ISOPBO2", + "type": "CHEM_SPEC", + "__description": "1,4-isomer of isoprene peroxy radical", + "molecular weight [kg mol-1]": 0.11712 + }, + { + "name": "MCO3", + "type": "CHEM_SPEC", + "__description": "peroxy radical from OH abstraction reaction with MACR", + "molecular weight [kg mol-1]": 0.101079 + }, + { + "name": "MDIALO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.117079 + }, + { + "name": "MEKO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.103094 + }, + { + "name": "EO2", + "type": "CHEM_SPEC", + "__description": "hydroxyperoxy radical from OH + ethene chemistry", + "molecular weight [kg mol-1]": 0.0770572 + }, + { + "name": "EO", + "type": "CHEM_SPEC", + "__description": "hydroxyalkoxy radical from OH + ethene chemistry", + "molecular weight [kg mol-1]": 0.0610578 + }, + { + "name": "GLYOXAL", + "type": "CHEM_SPEC", + "__description": "glyoxal", + "molecular weight [kg mol-1]": 0.0580356, + "diffusion coefficient [m2 s-1]": 1e300 + }, + { + "name": "MPAN", + "type": "CHEM_SPEC", + "__description": "methacrylol peroxynitrate", + "molecular weight [kg mol-1]": 0.147085 + }, + { + "name": "NC4CH2OH", + "type": "CHEM_SPEC", + "__description": "nitrooxy-alcohol from NO3+isoprene chemistry", + "molecular weight [kg mol-1]": 0.147126, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "ISOPOOH", + "type": "CHEM_SPEC", + "__description": "unsaturated hydroxyhydroperoxide", + "molecular weight [kg mol-1]": 0.118127 + }, + { + "name": "GLYALD", + "type": "CHEM_SPEC", + "__description": "glycolaldehyde", + "molecular weight [kg mol-1]": 0.0600504 + }, + { + "name": "HO2", + "type": "CHEM_SPEC", + "__description": "hydroperoxyl radical", + "molecular weight [kg mol-1]": 0.0330062, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "HOCH2OO", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0630314 + }, + { + "name": "H2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0020148 + }, + { + "name": "HYDRALD", + "type": "CHEM_SPEC", + "__description": "lumped unsturated hydroxycarbonyl", + "molecular weight [kg mol-1]": 0.100113 + }, + { + "name": "ISOP", + "type": "CHEM_SPEC", + "__description": "isoprene", + "molecular weight [kg mol-1]": 0.0681142 + }, + { + "name": "NTERPO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.230232 + }, + { + "name": "TOLO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.173141 + }, + { + "name": "TERP2O2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.199219 + }, + { + "name": "XYLENO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.187166 + }, + { + "name": "TERPO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.185234 + }, + { + "name": "XYLOLO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.203166 + }, + { + "name": "PBZNIT", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.183118 + }, + { + "name": "XYLENES", + "type": "CHEM_SPEC", + "__description": "lumped xylenes", + "molecular weight [kg mol-1]": 0.106162 + }, + { + "name": "PO2", + "type": "CHEM_SPEC", + "__description": "propene peroxy radical", + "molecular weight [kg mol-1]": 0.091083 + }, + { + "name": "TOLUENE", + "type": "CHEM_SPEC", + "__description": "toluene", + "molecular weight [kg mol-1]": 0.0921362 + }, + { + "name": "XO2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.149119 + }, + { + "name": "XOOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.150126 + }, + { + "name": "TERPROD2", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.154201 + }, + { + "name": "MEKOOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.104101 + }, + { + "name": "MACR", + "type": "CHEM_SPEC", + "__description": "methacrolein", + "molecular weight [kg mol-1]": 0.0700878 + }, + { + "name": "HONITR", + "type": "CHEM_SPEC", + "__description": "lumped hydroxynitrates from various compounds (formula updated 2017-04-06)", + "molecular weight [kg mol-1]": 0.1331, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "ISOPNITA", + "type": "CHEM_SPEC", + "__description": "1,2-hydroxynitrate from OH+isoprene chemistry", + "molecular weight [kg mol-1]": 0.147126, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "ISOPNOOH", + "type": "CHEM_SPEC", + "__description": "nitroxy-hydroperoxide from NO3+isoprene chemistry", + "molecular weight [kg mol-1]": 0.163125 + }, + { + "name": "IEPOX", + "type": "CHEM_SPEC", + "__description": "isoprene epoxide", + "molecular weight [kg mol-1]": 0.118127 + }, + { + "name": "ONITR", + "type": "CHEM_SPEC", + "__description": "lumped hydroxynitrates (formula updated 2017-04-06)", + "molecular weight [kg mol-1]": 0.147126, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "H2SO4", + "type": "CHEM_SPEC", + "__description": "sulfuric acid", + "molecular weight [kg mol-1]": 0.0980784 + }, + { + "name": "N2O", + "type": "CHEM_SPEC", + "__description": "nitrous oxide", + "molecular weight [kg mol-1]": 0.0440129 + }, + { + "name": "NO3", + "type": "CHEM_SPEC", + "__description": "nitrate radical", + "molecular weight [kg mol-1]": 0.0620049, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "OH", + "type": "CHEM_SPEC", + "__description": "hydroxyl radical", + "molecular weight [kg mol-1]": 0.0170068 + }, + { + "name": "PHENOOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.176122 + }, + { + "name": "PHENOL", + "type": "CHEM_SPEC", + "__description": "phenol, product of benzene chemistry", + "molecular weight [kg mol-1]": 0.0941098 + }, + { + "name": "XYLOL", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.122161 + }, + { + "name": "ROOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0900756 + }, + { + "name": "O3", + "type": "CHEM_SPEC", + "__description": "ozone", + "molecular weight [kg mol-1]": 0.0479982 + }, + { + "name": "TERPOOH", + "type": "CHEM_SPEC", + "__description": "Hydroxy hydroperoxide from terpene 0 double bonds", + "molecular weight [kg mol-1]": 0.186241 + }, + { + "name": "TOLOOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.174148 + }, + { + "name": "XYLENOOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.188174 + }, + { + "name": "XYLOLOOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.204173 + }, + { + "name": "CO", + "type": "CHEM_SPEC", + "__description": "carbon monoxide", + "molecular weight [kg mol-1]": 0.0280104 + }, + { + "name": "CH3COOH", + "type": "CHEM_SPEC", + "__description": "acetic acid", + "molecular weight [kg mol-1]": 0.0600504 + }, + { + "name": "CH3CHO", + "type": "CHEM_SPEC", + "__description": "acetaldehyde", + "molecular weight [kg mol-1]": 0.044051 + }, + { + "name": "BIGALD1", + "type": "CHEM_SPEC", + "__description": "butenedial, a product of aromatic oxidation", + "molecular weight [kg mol-1]": 0.0840724 + }, + { + "name": "ALKOOH", + "type": "CHEM_SPEC", + "__description": "lumped alkane peroxide", + "molecular weight [kg mol-1]": 0.104143 + }, + { + "name": "DICARBO2", + "type": "CHEM_SPEC", + "__description": "acylperoxy radical formed from aromatic oxidation, via unsaturated dicarbonyl chemistry", + "molecular weight [kg mol-1]": 0.12909 + }, + { + "name": "BENZOOH", + "type": "CHEM_SPEC", + "__description": "bicyclic hydroperoxide from OH + benzene", + "molecular weight [kg mol-1]": 0.160122 + }, + { + "name": "ALKO2", + "type": "CHEM_SPEC", + "__description": "lumped alkane peroxy radical from BIGALK", + "molecular weight [kg mol-1]": 0.103135 + }, + { + "name": "HO2NO2", + "type": "CHEM_SPEC", + "__description": "pernitric acid", + "molecular weight [kg mol-1]": 0.0790117 + }, + { + "name": "C3H7OOH", + "type": "CHEM_SPEC", + "__description": "propyl hydrogen peroxide", + "molecular weight [kg mol-1]": 0.076091 + }, + { + "name": "NH3", + "type": "CHEM_SPEC", + "__description": "ammonia", + "molecular weight [kg mol-1]": 0.017031 + }, + { + "name": "TERP2OOH", + "type": "CHEM_SPEC", + "__description": "2nd generation terpene hydroperoxide", + "molecular weight [kg mol-1]": 0.200226 + }, + { + "name": "POOH", + "type": "CHEM_SPEC", + "__description": "", + "molecular weight [kg mol-1]": 0.0920904 + }, + { + "name": "NOA", + "type": "CHEM_SPEC", + "__description": "nitrooxyacetone, largely from NO3+propene chemistry", + "molecular weight [kg mol-1]": 0.119074 + }, + { + "name": "NC4CHO", + "type": "CHEM_SPEC", + "__description": "nitrooxy-aldehyde from NO3+isoprene chemistry", + "molecular weight [kg mol-1]": 0.145111, + "diffusion coefficient [m2 s-1]": 1000.0 + }, + { + "name": "TEPOMUC", + "type": "CHEM_SPEC", + "__description": "toluene, xylenes product", + "molecular weight [kg mol-1]": 0.140134 + }, + { + "name": "NH4", + "type": "CHEM_SPEC", + "__description": "Ammonium", + "molecular weight [kg mol-1]": 0 + }, + { + "name": "M", + "type": "CHEM_SPEC", + "__description": "third-body species" + } + + ] +} diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index ddf79c85c..a67e85808 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -326,6 +326,10 @@ namespace micm { status = ParseSurface(object); } + else if (type == "USER_DEFINED") + { + status = ParseUserDefined(object); + } else { status = ConfigParseStatus::UnknownKey; @@ -884,6 +888,41 @@ namespace micm return ConfigParseStatus::Success; } + ConfigParseStatus ParseUserDefined(const json& object) + { + const std::string REACTANTS = "reactants"; + const std::string PRODUCTS = "products"; + const std::string MUSICA_NAME = "MUSICA name"; + + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + if (status != ConfigParseStatus::Success) + { + return status; + } + + auto reactants = ParseReactants(object[REACTANTS]); + auto products = ParseProducts(object[PRODUCTS]); + + if (reactants.first != ConfigParseStatus::Success) + { + return reactants.first; + } + if (products.first != ConfigParseStatus::Success) + { + return products.first; + } + + std::string name = "USER." + object[MUSICA_NAME].get(); + + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + + std::unique_ptr rate_ptr = + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); + + return ConfigParseStatus::Success; + } + ConfigParseStatus ParseSurface(const json& object) { const std::string REACTANTS = "gas-phase reactant"; @@ -937,7 +976,7 @@ namespace micm processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; - } + } /// @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 @@ -986,6 +1025,16 @@ namespace micm // 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; } @@ -1002,7 +1051,8 @@ namespace micm { if (!key.starts_with("__")) { - std::cerr << "non-standard key " << key << std::endl; + std::cerr << "Non-standard key '" << key <<"' found in object" << object << std::endl; + return ConfigParseStatus::ContainsNonStandardKey; } } diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index ff3b0c0a6..60309713e 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -90,6 +90,8 @@ namespace micm { if (reactant.IsParameterized()) continue; // Skip reactants that are parameterizations + if (variable_map.count( reactant.name_ ) < 1) + throw std::runtime_error("Reactant '" + reactant.name_ + "' does not exist"); reactant_ids_.push_back(variable_map.at(reactant.name_)); ++number_of_reactants; } @@ -97,6 +99,8 @@ namespace micm { if (product.first.IsParameterized()) continue; // Skip products that are parameterizations + if (variable_map.count( product.first.name_ ) < 1) + throw std::runtime_error("Product '" + product.first.name_ + "' does not exist"); product_ids_.push_back(variable_map.at(product.first.name_)); yields_.push_back(product.second); ++number_of_products; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a87f8b2f7..5c60c6253 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,5 +4,8 @@ set(CMAKE_CXX_CLANG_TIDY "") add_subdirectory(unit) add_subdirectory(regression) add_subdirectory(integration) +if(ENABLE_JSON) +add_subdirectory(examples) +endif() add_subdirectory(tutorial) add_subdirectory(kpp) diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt new file mode 100644 index 000000000..fdad13e99 --- /dev/null +++ b/test/examples/CMakeLists.txt @@ -0,0 +1,10 @@ +################################################################################ +# Copy test data + +add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/examples ${CMAKE_BINARY_DIR}/examples) + +################################################################################ +# Build example tests + +create_standard_test(NAME TS1 SOURCES test_TS1.cpp) diff --git a/test/examples/test_TS1.cpp b/test/examples/test_TS1.cpp new file mode 100644 index 000000000..7f6aca75f --- /dev/null +++ b/test/examples/test_TS1.cpp @@ -0,0 +1,216 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +// +// Test of the TS1 example mechanism (MZ327_TS1.2_20230307 in the Chemistry Cafe) +// +// This only tests that a solver can be built for this mechanism and that it +// can be run for a given set of initial conditions without solver errors. +// +// Comparisons with other chemistry solvers are included in +// https://github.com/NCAR/MUSICA-Performance-Comparison + +#include + +#include +#include + +TEST(Examples, TS1) +{ + micm::SolverConfig config; + std::string config_path = "./examples/TS1"; + auto status = config.ReadAndParse(config_path); + EXPECT_EQ(status, micm::ConfigParseStatus::Success); + auto solver_params = config.GetSolverParams(); + micm::RosenbrockSolver<> solver{ solver_params.system_, + solver_params.processes_, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + micm::State state = solver.GetState(); + + const double temperature = 287.45; // K + const double pressure = 101319.9; // Pa + const double air_density = pressure / (8.3145 * temperature); // mol m-3 + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + // TODO: Replace with CAM-Chem conditions when Derecho is back up + std::unordered_map> initial_concentrations = { + { "O2", { 0.21 * air_density } }, // mol m-3 + { "N2", { 0.78 * air_density } }, // mol m-3 + { "HNO3", { 1.0e-9 * air_density } }, // mol m-3 + { "SO2", { 1.0e-9 * air_density } }, // mol m-3 + { "CH4", { 1.8e-6 * air_density } }, // mol m-3 + { "H2O", { 1.0e-3 * air_density } }, // mol m-3 + { "NO2", { 1.0e-9 * air_density } }, // mol m-3 + { "NO", { 1.0e-9 * air_density } }, // mol m-3 + { "OH", { 1.0e-12 * air_density } }, // mol m-3 + { "CL2", { 1.0e-9 * air_density } }, // mol m-3 + { "DMS", { 1.0e-9 * air_density } }, // mol m-3 + { "CO2", { 0.385e-3 * air_density } }, // mol m-3 + { "H2O2", { 1.0e-9 * air_density } }, // mol m-3 + { "ISOP", { 1.0e-9 * air_density } }, // mol m-3 + { "O3", { 50.0e-9 * air_density } }, // mol m-3 + { "CO", { 1.0e-9 * air_density } }, // mol m-3 + { "NH3", { 1.0e-9 * air_density } }, // mol m-3 + { "M", { air_density } } // mol m-3 + }; + state.SetConcentrations(initial_concentrations); + + // std::cout << "custom rate parameters" << std::endl; + // for(auto &rc : state.custom_rate_parameter_map_) + // std::cout << " { \"" << rc.first << "\", { 1.0e-8 } }, // s-1" << std::endl; + + // TODO: Replace with CAM-Chem rate constants when Derecho is back up + std::unordered_map> custom_rate_constants = { + { "PHOTO.jacet", { 1.0e-8 } }, // s-1 + { "PHOTO.jalknit->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jalkooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jbenzooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jbepomuc->,.10*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jbigald->,0.2*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jbigald1->,.14*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jbigald2->,.20*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jbigald3->,.20*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jbigald4->,.006*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jbrcl", { 1.0e-8 } }, // s-1 + { "PHOTO.jbro", { 1.0e-8 } }, // s-1 + { "PHOTO.jbrono2_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jbrono2_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jbzooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jc2h5ooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jc3h7ooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jc6h5ooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jccl4", { 1.0e-8 } }, // s-1 + { "PHOTO.jcf2cl2", { 1.0e-8 } }, // s-1 + { "PHOTO.jcf2clbr", { 1.0e-8 } }, // s-1 + { "PHOTO.jcf3br", { 1.0e-8 } }, // s-1 + { "PHOTO.jcfc113", { 1.0e-8 } }, // s-1 + { "PHOTO.jcfc114", { 1.0e-8 } }, // s-1 + { "PHOTO.jcfc115", { 1.0e-8 } }, // s-1 + { "PHOTO.jcfcl3", { 1.0e-8 } }, // s-1 + { "PHOTO.jch2br2", { 1.0e-8 } }, // s-1 + { "PHOTO.jch2o_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jch2o_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jch3br", { 1.0e-8 } }, // s-1 + { "PHOTO.jch3ccl3", { 1.0e-8 } }, // s-1 + { "PHOTO.jch3cho", { 1.0e-8 } }, // s-1 + { "PHOTO.jch3cl", { 1.0e-8 } }, // s-1 + { "PHOTO.jch3co3h->,0.28*jh2o2", { 1.0e-8 } }, // s-1 + { "PHOTO.jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jch4_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jch4_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jchbr3", { 1.0e-8 } }, // s-1 + { "PHOTO.jcl2", { 1.0e-8 } }, // s-1 + { "PHOTO.jcl2o2", { 1.0e-8 } }, // s-1 + { "PHOTO.jclo", { 1.0e-8 } }, // s-1 + { "PHOTO.jclono2_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jclono2_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jco2", { 1.0e-8 } }, // s-1 + { "PHOTO.jcof2", { 1.0e-8 } }, // s-1 + { "PHOTO.jcofcl", { 1.0e-8 } }, // s-1 + { "PHOTO.jeooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jglyald", { 1.0e-8 } }, // s-1 + { "PHOTO.jglyoxal->,jmgly", { 1.0e-8 } }, // s-1 + { "PHOTO.jh2402", { 1.0e-8 } }, // s-1 + { "PHOTO.jh2o2", { 1.0e-8 } }, // s-1 + { "PHOTO.jh2o_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jh2o_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jh2o_c", { 1.0e-8 } }, // s-1 + { "PHOTO.jh2so4", { 1.0e-8 } }, // s-1 + { "PHOTO.jhbr", { 1.0e-8 } }, // s-1 + { "PHOTO.jhcfc141b", { 1.0e-8 } }, // s-1 + { "PHOTO.jhcfc142b", { 1.0e-8 } }, // s-1 + { "PHOTO.jhcfc22", { 1.0e-8 } }, // s-1 + { "PHOTO.jhcl", { 1.0e-8 } }, // s-1 + { "PHOTO.jhf", { 1.0e-8 } }, // s-1 + { "PHOTO.jhno3", { 1.0e-8 } }, // s-1 + { "PHOTO.jho2no2_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jho2no2_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jhobr", { 1.0e-8 } }, // s-1 + { "PHOTO.jhocl", { 1.0e-8 } }, // s-1 + { "PHOTO.jhonitr->,jch2o_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jhpald->,.006*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jhyac", { 1.0e-8 } }, // s-1 + { "PHOTO.jisopnooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jisopooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jmacr_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jmacr_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jmek->,jacet", { 1.0e-8 } }, // s-1 + { "PHOTO.jmekooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jmgly", { 1.0e-8 } }, // s-1 + { "PHOTO.jmpan->,jpan", { 1.0e-8 } }, // s-1 + { "PHOTO.jmvk", { 1.0e-8 } }, // s-1 + { "PHOTO.jn2o", { 1.0e-8 } }, // s-1 + { "PHOTO.jn2o5_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jn2o5_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jnc4cho->,jch2o_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jno3_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jno3_b", { 1.0e-8 } }, // s-1 + { "PHOTO.jno=userdefined,", { 1.0e-8 } }, // s-1 + { "PHOTO.jnoa->,jch2o_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jnterpooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jo2_a=userdefined,", { 1.0e-8 } }, // s-1 + { "PHOTO.jo2_b=userdefined,", { 1.0e-8 } }, // s-1 + { "PHOTO.jo3_a", { 1.0e-8 } }, // s-1 + { "PHOTO.jo3_b", { 1.0e-8 } }, // s-1 + { "PHOTO.joclo", { 1.0e-8 } }, // s-1 + { "PHOTO.jocs", { 1.0e-8 } }, // s-1 + { "PHOTO.jonitr->,jch3cho", { 1.0e-8 } }, // s-1 + { "PHOTO.jpan", { 1.0e-8 } }, // s-1 + { "PHOTO.jphenooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jpooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jrooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jsf6", { 1.0e-8 } }, // s-1 + { "PHOTO.jso", { 1.0e-8 } }, // s-1 + { "PHOTO.jso2", { 1.0e-8 } }, // s-1 + { "PHOTO.jso3", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa1_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa1_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa2_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa2_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa3_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa3_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa4_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa4_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa5_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jsoa5_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jtepomuc->,.10*jno2", { 1.0e-8 } }, // s-1 + { "PHOTO.jterp2ooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jterpnit->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jterpooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jterprd1->,jch3cho", { 1.0e-8 } }, // s-1 + { "PHOTO.jterprd2->,jch3cho", { 1.0e-8 } }, // s-1 + { "PHOTO.jtolooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jxooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jxylenooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "PHOTO.jxylolooh->,jch3ooh", { 1.0e-8 } }, // s-1 + { "USER.het1", { 1.0e-8 } }, // s-1 + { "USER.het2", { 1.0e-8 } }, // s-1 + { "USER.het3", { 1.0e-8 } }, // s-1 + { "USER.het4", { 1.0e-8 } }, // s-1 + { "USER.het5", { 1.0e-8 } }, // s-1 + { "USER.het6", { 1.0e-8 } }, // s-1 + { "USER.het7", { 1.0e-8 } }, // s-1 + { "USER.het8", { 1.0e-8 } }, // s-1 + { "USER.het9", { 1.0e-8 } }, // s-1 + { "USER.het10", { 1.0e-8 } }, // s-1 + { "USER.het11", { 1.0e-8 } }, // s-1 + { "USER.het12", { 1.0e-8 } }, // s-1 + { "USER.het13", { 1.0e-8 } }, // s-1 + { "USER.het14", { 1.0e-8 } }, // s-1 + { "USER.het15", { 1.0e-8 } }, // s-1 + { "USER.het16", { 1.0e-8 } }, // s-1 + { "USER.het17", { 1.0e-8 } }, // s-1 + { "USER.k_co_oh_jpl19", { 1.0e-8 } } // s-1 + }; + state.SetCustomRateParameters(custom_rate_constants); + + double time_step = 150.0; // s + + auto result = solver.Solve(time_step, state); + + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); +} \ No newline at end of file diff --git a/test/unit/configure/process/CMakeLists.txt b/test/unit/configure/process/CMakeLists.txt index 437d7f4ac..32a708ce6 100644 --- a/test/unit/configure/process/CMakeLists.txt +++ b/test/unit/configure/process/CMakeLists.txt @@ -15,4 +15,4 @@ create_standard_test(NAME surface_config SOURCES test_surface_config.cpp) create_standard_test(NAME ternary_chemical_activation_config SOURCES test_ternary_chemical_activation_config.cpp) create_standard_test(NAME troe_config SOURCES test_troe_config.cpp) create_standard_test(NAME tunneling_config SOURCES test_tunneling_config.cpp) - +create_standard_test(NAME user_defined_config SOURCES test_user_defined_config.cpp) diff --git a/test/unit/configure/process/test_user_defined_config.cpp b/test/unit/configure/process/test_user_defined_config.cpp new file mode 100644 index 000000000..7f718ec51 --- /dev/null +++ b/test/unit/configure/process/test_user_defined_config.cpp @@ -0,0 +1,83 @@ +#include + +#include + +TEST(UserDefinedConfig, DetectsInvalidConfig) +{ + micm::SolverConfig solver_config; + + // Read and parse the configure files + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/missing_reactants"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status) << " " << micm::configParseStatusToString(status) << "\n"; + status = solver_config.ReadAndParse("./unit_configs/process/user_defined/missing_products"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + status = solver_config.ReadAndParse("./unit_configs/process/user_defined/missing_MUSICA_name"); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); +} + +TEST(UserDefinedConfig, ParseConfig) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/valid"); + EXPECT_EQ(micm::ConfigParseStatus::Success, status); + + micm::SolverParameters solver_params = solver_config.GetSolverParams(); + + auto& process_vector = solver_params.processes_; + + // first reaction + { + EXPECT_EQ(process_vector[0].reactants_.size(), 3); + EXPECT_EQ(process_vector[0].reactants_[0].name_, "bar"); + EXPECT_EQ(process_vector[0].reactants_[1].name_, "bar"); + EXPECT_EQ(process_vector[0].reactants_[2].name_, "foo"); + EXPECT_EQ(process_vector[0].products_.size(), 2); + EXPECT_EQ(process_vector[0].products_[0].first.name_, "baz"); + EXPECT_EQ(process_vector[0].products_[0].second, 1.4); + EXPECT_EQ(process_vector[0].products_[1].first.name_, "foo"); + EXPECT_EQ(process_vector[0].products_[1].second, 1.0); + micm::UserDefinedRateConstant* photo_rate_constant = + dynamic_cast(process_vector[0].rate_constant_.get()); + EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "USER.foo"); + } + + // second reaction + { + EXPECT_EQ(process_vector[1].reactants_.size(), 2); + EXPECT_EQ(process_vector[1].reactants_[0].name_, "foo"); + EXPECT_EQ(process_vector[1].reactants_[1].name_, "foo"); + EXPECT_EQ(process_vector[1].products_.size(), 1); + EXPECT_EQ(process_vector[1].products_[0].first.name_, "bar"); + EXPECT_EQ(process_vector[1].products_[0].second, 1.0); + micm::UserDefinedRateConstant* photo_rate_constant = + dynamic_cast(process_vector[1].rate_constant_.get()); + EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); + EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "USER.bar"); + } +} + +TEST(PhotolysisConfig, DetectsNonstandardKeys) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/contains_nonstandard_key"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(PhotolysisConfig, DetectsNonstandardProductCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/nonstandard_product_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} + +TEST(PhotolysisConfig, DetectsNonstandardReactantCoefficient) +{ + micm::SolverConfig solver_config; + + micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/nonstandard_reactant_coef"); + EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); +} diff --git a/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/config.json b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/reactions.json b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/reactions.json new file mode 100644 index 000000000..d9001a00c --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/reactions.json @@ -0,0 +1,17 @@ +{ + "camp-data": [ + { + "name": "nonstandard user-defined reaction", + "type": "MECHANISM", + "reactions": [ + { + "type": "USER_DEFINED", + "reactants": { }, + "products": { }, + "MUSICA name": "foo", + "mUSICA name": "foo" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/species.json b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/tolerance.json b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/contains_nonstandard_key/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/config.json b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/reactions.json b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/reactions.json new file mode 100644 index 000000000..8d942f9cc --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/reactions.json @@ -0,0 +1,21 @@ +{ + "camp-data": [ + { + "name": "User defined reaction missing MUSICA name", + "type": "MECHANISM", + "reactions": [ + { + "type": "USER_DEFINED", + "reactants": { + "foo" : { }, + "bar" : { "qty": 2 } + }, + "products": { + "baz" : { "yield": 1.4 }, + "foo" : { } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/species.json b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/tolerance.json b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_MUSICA_name/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/user_defined/missing_products/config.json b/test/unit/unit_configs/process/user_defined/missing_products/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_products/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/missing_products/reactions.json b/test/unit/unit_configs/process/user_defined/missing_products/reactions.json new file mode 100644 index 000000000..e822232d3 --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_products/reactions.json @@ -0,0 +1,18 @@ +{ + "camp-data": [ + { + "name": "User defined reaction missing products", + "type": "MECHANISM", + "reactions": [ + { + "type": "USER_DEFINED", + "MUSICA name" : "foo", + "reactants": { + "foo" : { }, + "bar" : { "qty": 2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/missing_products/species.json b/test/unit/unit_configs/process/user_defined/missing_products/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_products/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/missing_products/tolerance.json b/test/unit/unit_configs/process/user_defined/missing_products/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_products/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/user_defined/missing_reactants/config.json b/test/unit/unit_configs/process/user_defined/missing_reactants/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_reactants/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/missing_reactants/reactions.json b/test/unit/unit_configs/process/user_defined/missing_reactants/reactions.json new file mode 100644 index 000000000..015f2ee0d --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_reactants/reactions.json @@ -0,0 +1,18 @@ +{ + "camp-data": [ + { + "name": "User defined reaction missing products", + "type": "MECHANISM", + "reactions": [ + { + "type": "USER_DEFINED", + "MUSICA name" : "foo", + "products": { + "foo" : { }, + "bar" : { "yield": 2.2 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/missing_reactants/species.json b/test/unit/unit_configs/process/user_defined/missing_reactants/species.json new file mode 100644 index 000000000..ec9fb504f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_reactants/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/missing_reactants/tolerance.json b/test/unit/unit_configs/process/user_defined/missing_reactants/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/missing_reactants/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/config.json b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/reactions.json b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/reactions.json new file mode 100644 index 000000000..e060b434d --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/reactions.json @@ -0,0 +1,21 @@ +{ + "camp-data": [ + { + "name": "invaild yield name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "USER_DEFINED", + "MUSICA name": "foo", + "reactants": { + "foo" : { } + }, + "products": { + "bar": { "Yield": 1.0 } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/species.json b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/tolerance.json b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_product_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/config.json b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/reactions.json b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/reactions.json new file mode 100644 index 000000000..82fc9655c --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/reactions.json @@ -0,0 +1,21 @@ +{ + "camp-data": [ + { + "name": "invalid quantity name", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "USER_DEFINED", + "MUSICA name": "foo", + "reactants": { + "foo" : { "Qty": 2.0 } + }, + "products": { + "bar": {} + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/species.json b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/tolerance.json b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/nonstandard_reactant_coef/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} diff --git a/test/unit/unit_configs/process/user_defined/valid/config.json b/test/unit/unit_configs/process/user_defined/valid/config.json new file mode 100644 index 000000000..38307636f --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/valid/config.json @@ -0,0 +1 @@ +{"camp-files": ["species.json", "reactions.json", "tolerance.json"]} diff --git a/test/unit/unit_configs/process/user_defined/valid/reactions.json b/test/unit/unit_configs/process/user_defined/valid/reactions.json new file mode 100644 index 000000000..6f057e25d --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/valid/reactions.json @@ -0,0 +1,33 @@ +{ + "camp-data": [ + { + "name": "Valid user-defined reaction", + "type": "MECHANISM", + "reactions": [ + { + "__my comment": {}, + "type": "USER_DEFINED", + "MUSICA name": "foo", + "reactants": { + "foo" : { }, + "bar" : { "qty": 2 } + }, + "products": { + "baz" : { "yield": 1.4 }, + "foo" : { } + } + }, + { + "type": "USER_DEFINED", + "MUSICA name": "bar", + "reactants": { + "foo" : { "qty": 2 } + }, + "products": { + "bar" : { } + } + } + ] + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/valid/species.json b/test/unit/unit_configs/process/user_defined/valid/species.json new file mode 100644 index 000000000..b7716cf54 --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/valid/species.json @@ -0,0 +1,20 @@ +{ + "camp-data": [ + { + "name": "foo", + "type": "CHEM_SPEC" + }, + { + "name": "bar", + "type": "CHEM_SPEC" + }, + { + "name": "baz", + "type": "CHEM_SPEC" + }, + { + "name": "quz", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/process/user_defined/valid/tolerance.json b/test/unit/unit_configs/process/user_defined/valid/tolerance.json new file mode 100644 index 000000000..e652cd46e --- /dev/null +++ b/test/unit/unit_configs/process/user_defined/valid/tolerance.json @@ -0,0 +1,8 @@ +{ + "camp-data": [ + { + "type": "RELATIVE_TOLERANCE", + "value": 1.0e-4 + } + ] +} From 5c743c3ea7a08029aa65ebcc5ea63b96ac9fed7d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 7 Nov 2023 16:18:59 -0800 Subject: [PATCH 230/318] Auto-format code changes (#346) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/configure/solver_config.hpp | 8 ++++---- include/micm/process/process_set.hpp | 8 ++++---- .../configure/process/test_user_defined_config.cpp | 13 ++++++++----- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index a67e85808..05d18ae6a 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -976,7 +976,7 @@ namespace micm processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; - } + } /// @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 @@ -1032,7 +1032,7 @@ namespace micm sorted_object_keys.begin(), sorted_object_keys.end(), std::back_inserter(missing_keys)); - for(auto &key : missing_keys) + for (auto& key : missing_keys) std::cerr << "Missing required key '" << key << "' in object: " << object << std::endl; return ConfigParseStatus::RequiredKeyNotFound; @@ -1051,8 +1051,8 @@ namespace micm { if (!key.starts_with("__")) { - std::cerr << "Non-standard key '" << key <<"' found in object" << object << std::endl; - + std::cerr << "Non-standard key '" << key << "' found in object" << object << std::endl; + return ConfigParseStatus::ContainsNonStandardKey; } } diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 60309713e..4b06eb9d6 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -90,8 +90,8 @@ namespace micm { if (reactant.IsParameterized()) continue; // Skip reactants that are parameterizations - if (variable_map.count( reactant.name_ ) < 1) - throw std::runtime_error("Reactant '" + reactant.name_ + "' does not exist"); + if (variable_map.count(reactant.name_) < 1) + throw std::runtime_error("Reactant '" + reactant.name_ + "' does not exist"); reactant_ids_.push_back(variable_map.at(reactant.name_)); ++number_of_reactants; } @@ -99,8 +99,8 @@ namespace micm { if (product.first.IsParameterized()) continue; // Skip products that are parameterizations - if (variable_map.count( product.first.name_ ) < 1) - throw std::runtime_error("Product '" + product.first.name_ + "' does not exist"); + if (variable_map.count(product.first.name_) < 1) + throw std::runtime_error("Product '" + product.first.name_ + "' does not exist"); product_ids_.push_back(variable_map.at(product.first.name_)); yields_.push_back(product.second); ++number_of_products; diff --git a/test/unit/configure/process/test_user_defined_config.cpp b/test/unit/configure/process/test_user_defined_config.cpp index 7f718ec51..9567bb0e3 100644 --- a/test/unit/configure/process/test_user_defined_config.cpp +++ b/test/unit/configure/process/test_user_defined_config.cpp @@ -12,7 +12,7 @@ TEST(UserDefinedConfig, DetectsInvalidConfig) status = solver_config.ReadAndParse("./unit_configs/process/user_defined/missing_products"); EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); status = solver_config.ReadAndParse("./unit_configs/process/user_defined/missing_MUSICA_name"); - EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); + EXPECT_EQ(micm::ConfigParseStatus::RequiredKeyNotFound, status); } TEST(UserDefinedConfig, ParseConfig) @@ -55,14 +55,15 @@ TEST(UserDefinedConfig, ParseConfig) dynamic_cast(process_vector[1].rate_constant_.get()); EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "USER.bar"); - } + } } TEST(PhotolysisConfig, DetectsNonstandardKeys) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/contains_nonstandard_key"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/user_defined/contains_nonstandard_key"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } @@ -70,7 +71,8 @@ TEST(PhotolysisConfig, DetectsNonstandardProductCoefficient) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/nonstandard_product_coef"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/user_defined/nonstandard_product_coef"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } @@ -78,6 +80,7 @@ TEST(PhotolysisConfig, DetectsNonstandardReactantCoefficient) { micm::SolverConfig solver_config; - micm::ConfigParseStatus status = solver_config.ReadAndParse("./unit_configs/process/user_defined/nonstandard_reactant_coef"); + micm::ConfigParseStatus status = + solver_config.ReadAndParse("./unit_configs/process/user_defined/nonstandard_reactant_coef"); EXPECT_EQ(micm::ConfigParseStatus::ContainsNonStandardKey, status); } From e3b05a98be983057afe51542de31518a6e082f7a Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 8 Nov 2023 14:23:21 -0800 Subject: [PATCH 231/318] Check for unused species in solver constructor (#347) check for unused species in solver constructor --- docker/Dockerfile | 2 +- include/micm/process/process_set.hpp | 18 +++++++++++++ include/micm/solver/rosenbrock.inl | 13 ++++++++++ .../solver/rosenbrock_solver_parameters.hpp | 1 + test/integration/chapman.cpp | 10 ++++++-- .../regression/RosenbrockChapman/jit_util.hpp | 25 +++++++++++++++---- test/regression/RosenbrockChapman/util.hpp | 25 +++++++++++++++---- test/unit/process/test_process_set_policy.hpp | 22 ++++++++++++---- 8 files changed, 98 insertions(+), 18 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 8760be4dc..ab7155ba4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -20,7 +20,7 @@ RUN mkdir /build \ -D CMAKE_BUILD_TYPE=release \ -D ENABLE_CLANG_TIDY:BOOL=FALSE \ ../micm \ - && make install -j + && make install -j 8 # now test if we can use the installed files RUN cd /micm/test/integration/cmake/find_package \ diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 4b06eb9d6..bcfe85f48 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -71,6 +71,11 @@ namespace micm const MatrixPolicy& rate_constants, const MatrixPolicy& state_variables, SparseMatrixPolicy& jacobian) const; + + /// @brief Returns the set of species used in a set of processes + /// @param processes The set of processes + /// @return The set of species used in the set of processes + static std::set SpeciesUsed(const std::vector& processes); }; inline ProcessSet::ProcessSet( @@ -336,4 +341,17 @@ namespace micm } } } + + std::set ProcessSet::SpeciesUsed(const std::vector& processes) + { + std::set used_species; + for (auto &process : processes) + { + for (auto &reactant : process.reactants_) + used_species.insert(reactant.name_); + for (auto &product : process.products_) + used_species.insert(product.first.name_); + } + return used_species; + } } // namespace micm diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index d028c6022..dcf801959 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -98,6 +98,19 @@ namespace micm std::function& variables, const std::size_t i)> state_reordering; std::size_t index = 0; + auto used_species = ProcessSetPolicy::SpeciesUsed(processes); + auto available_species = system.UniqueNames(); + std::sort(available_species.begin(), available_species.end()); + std::set unused_species; + std::set_difference(available_species.begin(), available_species.end(), used_species.begin(), used_species.end(), std::inserter(unused_species, unused_species.begin())); + if (unused_species.size() > 0 && !parameters_.ignore_unused_species_) + { + std::string err_msg = "Unused species in chemical system:"; + for (auto& species: unused_species) + err_msg += " '" + species + "'"; + err_msg += ". Set solver parameter ignore_unused_species_ to allow unused species in solve."; + throw std::runtime_error(err_msg); + } for (auto& name : system.UniqueNames()) variable_map[name] = index++; diff --git a/include/micm/solver/rosenbrock_solver_parameters.hpp b/include/micm/solver/rosenbrock_solver_parameters.hpp index e492bb772..892c18df6 100644 --- a/include/micm/solver/rosenbrock_solver_parameters.hpp +++ b/include/micm/solver/rosenbrock_solver_parameters.hpp @@ -57,6 +57,7 @@ namespace micm size_t number_of_grid_cells_{ 1 }; // Number of grid cells to solve simultaneously bool reorder_state_{ true }; // Reorder state during solver construction to minimize LU fill-in bool check_singularity_{ false }; // Check for singular A matrix in linear solve of A x = b + bool ignore_unused_species_{ false }; // Allow unused species to be included in state and solve // Print RosenbrockSolverParameters to console void print() const; diff --git a/test/integration/chapman.cpp b/test/integration/chapman.cpp index a0c422b9c..4f25c0b81 100644 --- a/test/integration/chapman.cpp +++ b/test/integration/chapman.cpp @@ -32,10 +32,13 @@ TEST(ChapmanIntegration, CanBuildChapmanSystemUsingConfig) // Get solver parameters ('System', the collection of 'Process') micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + auto options = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(); + options.ignore_unused_species_ = true; + micm::RosenbrockSolver solver{ solver_params.system_, std::move(solver_params.processes_), - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + options }; micm::State state = solver.GetState(); @@ -126,10 +129,13 @@ TEST(ChapmanIntegration, CanBuildChapmanSystem) .rate_constant(micm::UserDefinedRateConstant({ .label_ = "jO3b" })) .phase(gas_phase); + auto options = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(); + options.ignore_unused_species_ = true; + micm::RosenbrockSolver solver{ micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3, r4, photo_1, photo_2, photo_3 }, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + options }; auto state = solver.GetState(); diff --git a/test/regression/RosenbrockChapman/jit_util.hpp b/test/regression/RosenbrockChapman/jit_util.hpp index c1da846d5..b9d917f4c 100644 --- a/test/regression/RosenbrockChapman/jit_util.hpp +++ b/test/regression/RosenbrockChapman/jit_util.hpp @@ -15,6 +15,9 @@ getTwoStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) { @@ -25,7 +28,7 @@ getTwoStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells)); + options); } template< @@ -41,6 +44,9 @@ getThreeStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) { @@ -51,7 +57,7 @@ getThreeStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells)); + options); } template< @@ -67,6 +73,9 @@ getFourStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) { @@ -77,7 +86,7 @@ getFourStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells)); + options); } template< @@ -93,6 +102,9 @@ getFourStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) { @@ -103,7 +115,7 @@ getFourStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); + options); } template< @@ -119,6 +131,9 @@ getSixStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + auto jit{ micm::JitCompiler::create() }; if (auto err = jit.takeError()) { @@ -129,5 +144,5 @@ getSixStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); + options); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/util.hpp b/test/regression/RosenbrockChapman/util.hpp index 31c942316..8c314e48f 100644 --- a/test/regression/RosenbrockChapman/util.hpp +++ b/test/regression/RosenbrockChapman/util.hpp @@ -88,10 +88,13 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::two_stage_rosenbrock_parameters(number_of_grid_cells)); + options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -101,10 +104,13 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells)); + options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -114,10 +120,13 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::four_stage_rosenbrock_parameters(number_of_grid_cells)); + options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -127,10 +136,13 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); + options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -140,8 +152,11 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); + auto options = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + options.ignore_unused_species_ = true; + return micm::RosenbrockSolver( micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), - micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells)); + options); } \ No newline at end of file diff --git a/test/unit/process/test_process_set_policy.hpp b/test/unit/process/test_process_set_policy.hpp index f9d2fc66d..34b323a27 100644 --- a/test/unit/process/test_process_set_policy.hpp +++ b/test/unit/process/test_process_set_policy.hpp @@ -23,12 +23,13 @@ void testProcessSet(const std::function{ foo, bar, qux, baz, quz, quuz } }; + micm::Phase gas_phase{ std::vector{ foo, bar, qux, baz, quz, quuz, corge } }; micm::State state(micm::StateParameters{ - .number_of_grid_cells_ = 2, .number_of_rate_constants_ = 3, .variable_names_{ "foo", "bar", "baz", "quz", "quuz" } }); + .number_of_grid_cells_ = 2, .number_of_rate_constants_ = 3, .variable_names_{ "foo", "bar", "baz", "quz", "quuz", "corge" } }); micm::Process r1 = micm::Process::create().reactants({ foo, baz }).products({ yields(bar, 1), yields(quuz, 2.4) }).phase(gas_phase); @@ -38,12 +39,23 @@ void testProcessSet(const std::function{ r1, r2, r3 }); + + EXPECT_EQ(used_species.size(), 6); + EXPECT_TRUE(used_species.contains("foo")); + EXPECT_TRUE(used_species.contains("bar")); + EXPECT_TRUE(used_species.contains("baz")); + EXPECT_TRUE(used_species.contains("quz")); + EXPECT_TRUE(used_species.contains("quuz")); + EXPECT_TRUE(used_species.contains("qux")); + EXPECT_FALSE(used_species.contains("corge")); + ProcessSetPolicy set = create_set(std::vector{ r1, r2, r3 }, state); EXPECT_EQ(state.variables_.size(), 2); - EXPECT_EQ(state.variables_[0].size(), 5); - state.variables_[0] = { 0.1, 0.2, 0.3, 0.4, 0.5 }; - state.variables_[1] = { 1.1, 1.2, 1.3, 1.4, 1.5 }; + EXPECT_EQ(state.variables_[0].size(), 6); + state.variables_[0] = { 0.1, 0.2, 0.3, 0.4, 0.5, 0.0 }; + state.variables_[1] = { 1.1, 1.2, 1.3, 1.4, 1.5, 0.0 }; MatrixPolicy rate_constants{ 2, 3 }; rate_constants[0] = { 10.0, 20.0, 30.0 }; rate_constants[1] = { 110.0, 120.0, 130.0 }; From b68af0fcf1f6151a9af72930b19c3c9b4ba59ed6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 8 Nov 2023 14:39:47 -0800 Subject: [PATCH 232/318] Auto-format code changes (#348) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/process/process_set.hpp | 6 ++-- .../solver/rosenbrock_solver_parameters.hpp | 8 ++--- test/integration/chapman.cpp | 8 ++--- .../regression/RosenbrockChapman/jit_util.hpp | 31 ++++++------------- test/regression/RosenbrockChapman/util.hpp | 26 ++++++---------- test/unit/process/test_process_set_policy.hpp | 6 ++-- 6 files changed, 32 insertions(+), 53 deletions(-) diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index bcfe85f48..db1250a68 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -345,11 +345,11 @@ namespace micm std::set ProcessSet::SpeciesUsed(const std::vector& processes) { std::set used_species; - for (auto &process : processes) + for (auto& process : processes) { - for (auto &reactant : process.reactants_) + for (auto& reactant : process.reactants_) used_species.insert(reactant.name_); - for (auto &product : process.products_) + for (auto& product : process.products_) used_species.insert(product.first.name_); } return used_species; diff --git a/include/micm/solver/rosenbrock_solver_parameters.hpp b/include/micm/solver/rosenbrock_solver_parameters.hpp index 892c18df6..2b00af10f 100644 --- a/include/micm/solver/rosenbrock_solver_parameters.hpp +++ b/include/micm/solver/rosenbrock_solver_parameters.hpp @@ -54,10 +54,10 @@ namespace micm double absolute_tolerance_{ 1e-3 }; double relative_tolerance_{ 1e-4 }; - size_t number_of_grid_cells_{ 1 }; // Number of grid cells to solve simultaneously - bool reorder_state_{ true }; // Reorder state during solver construction to minimize LU fill-in - bool check_singularity_{ false }; // Check for singular A matrix in linear solve of A x = b - bool ignore_unused_species_{ false }; // Allow unused species to be included in state and solve + size_t number_of_grid_cells_{ 1 }; // Number of grid cells to solve simultaneously + bool reorder_state_{ true }; // Reorder state during solver construction to minimize LU fill-in + bool check_singularity_{ false }; // Check for singular A matrix in linear solve of A x = b + bool ignore_unused_species_{ false }; // Allow unused species to be included in state and solve // Print RosenbrockSolverParameters to console void print() const; diff --git a/test/integration/chapman.cpp b/test/integration/chapman.cpp index 4f25c0b81..c869caa36 100644 --- a/test/integration/chapman.cpp +++ b/test/integration/chapman.cpp @@ -35,11 +35,9 @@ TEST(ChapmanIntegration, CanBuildChapmanSystemUsingConfig) auto options = micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(); options.ignore_unused_species_ = true; - micm::RosenbrockSolver solver{ - solver_params.system_, - std::move(solver_params.processes_), - options - }; + micm::RosenbrockSolver solver{ solver_params.system_, + std::move(solver_params.processes_), + options }; micm::State state = solver.GetState(); diff --git a/test/regression/RosenbrockChapman/jit_util.hpp b/test/regression/RosenbrockChapman/jit_util.hpp index b9d917f4c..ad0bdd710 100644 --- a/test/regression/RosenbrockChapman/jit_util.hpp +++ b/test/regression/RosenbrockChapman/jit_util.hpp @@ -25,10 +25,7 @@ getTwoStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) EXPECT_TRUE(false); } return micm::JitRosenbrockSolver( - jit.get(), - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template< @@ -54,10 +51,7 @@ getThreeStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) EXPECT_TRUE(false); } return micm::JitRosenbrockSolver( - jit.get(), - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template< @@ -83,10 +77,7 @@ getFourStageMultiCellJitChapmanSolver(const size_t number_of_grid_cells) EXPECT_TRUE(false); } return micm::JitRosenbrockSolver( - jit.get(), - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template< @@ -102,7 +93,8 @@ getFourStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - auto options = micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + auto options = + micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); options.ignore_unused_species_ = true; auto jit{ micm::JitCompiler::create() }; @@ -112,10 +104,7 @@ getFourStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) EXPECT_TRUE(false); } return micm::JitRosenbrockSolver( - jit.get(), - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template< @@ -131,7 +120,8 @@ getSixStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - auto options = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + auto options = + micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); options.ignore_unused_species_ = true; auto jit{ micm::JitCompiler::create() }; @@ -141,8 +131,5 @@ getSixStageDAMultiCellJitChapmanSolver(const size_t number_of_grid_cells) EXPECT_TRUE(false); } return micm::JitRosenbrockSolver( - jit.get(), - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + jit.get(), micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } \ No newline at end of file diff --git a/test/regression/RosenbrockChapman/util.hpp b/test/regression/RosenbrockChapman/util.hpp index 8c314e48f..179e1500e 100644 --- a/test/regression/RosenbrockChapman/util.hpp +++ b/test/regression/RosenbrockChapman/util.hpp @@ -92,9 +92,7 @@ micm::RosenbrockSolver get options.ignore_unused_species_ = true; return micm::RosenbrockSolver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -108,9 +106,7 @@ micm::RosenbrockSolver get options.ignore_unused_species_ = true; return micm::RosenbrockSolver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -124,9 +120,7 @@ micm::RosenbrockSolver get options.ignore_unused_species_ = true; return micm::RosenbrockSolver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -136,13 +130,12 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - auto options = micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + auto options = + micm::RosenbrockSolverParameters::four_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); options.ignore_unused_species_ = true; return micm::RosenbrockSolver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> @@ -152,11 +145,10 @@ micm::RosenbrockSolver get micm::Phase gas_phase = createGasPhase(); std::vector processes = createProcesses(gas_phase); - auto options = micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); + auto options = + micm::RosenbrockSolverParameters::six_stage_differential_algebraic_rosenbrock_parameters(number_of_grid_cells); options.ignore_unused_species_ = true; return micm::RosenbrockSolver( - micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), - std::move(processes), - options); + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), std::move(processes), options); } \ No newline at end of file diff --git a/test/unit/process/test_process_set_policy.hpp b/test/unit/process/test_process_set_policy.hpp index 34b323a27..85643226f 100644 --- a/test/unit/process/test_process_set_policy.hpp +++ b/test/unit/process/test_process_set_policy.hpp @@ -28,8 +28,10 @@ void testProcessSet(const std::function{ foo, bar, qux, baz, quz, quuz, corge } }; - micm::State state(micm::StateParameters{ - .number_of_grid_cells_ = 2, .number_of_rate_constants_ = 3, .variable_names_{ "foo", "bar", "baz", "quz", "quuz", "corge" } }); + micm::State state( + micm::StateParameters{ .number_of_grid_cells_ = 2, + .number_of_rate_constants_ = 3, + .variable_names_{ "foo", "bar", "baz", "quz", "quuz", "corge" } }); micm::Process r1 = micm::Process::create().reactants({ foo, baz }).products({ yields(bar, 1), yields(quuz, 2.4) }).phase(gas_phase); From eb7a3fecefdc215babdb230996d36d3b49874a05 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 10 Nov 2023 10:45:33 -0800 Subject: [PATCH 233/318] add scaling factor to user-defined rate constant --- .../process/user_defined_rate_constant.hpp | 4 +++- .../test_user_defined_rate_constant.cpp | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index 02130da45..69c2c44a6 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -12,6 +12,8 @@ namespace micm { /// @brief Label for the reaction used to identify user-defined parameters std::string label_; + /// @brief Scaling factor to apply to user-provided rate constants + double scaling_factor_{ 1.0 }; }; /// @brief A photolysis rate constant @@ -75,7 +77,7 @@ namespace micm const Conditions& conditions, std::vector::const_iterator custom_parameters) const { - return (double)*custom_parameters; + return (double)*custom_parameters * parameters_.scaling_factor_; } inline std::vector UserDefinedRateConstant::CustomParameters() const diff --git a/test/unit/process/test_user_defined_rate_constant.cpp b/test/unit/process/test_user_defined_rate_constant.cpp index 50e30e26b..6e32fac0a 100644 --- a/test/unit/process/test_user_defined_rate_constant.cpp +++ b/test/unit/process/test_user_defined_rate_constant.cpp @@ -57,3 +57,21 @@ TEST(UserDefinedRateConstant, ConstructorWithRateAndName) EXPECT_EQ(k, 1.1); EXPECT_EQ(photo.CustomParameters()[0], "a name"); } + +TEST(UserDefinedRateConstant, CustomScalingFactor) +{ + auto state_parameters = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = {"user"}, + .custom_rate_parameter_labels_ = { "my rate", }, + }; + + micm::State state{ state_parameters }; + state.custom_rate_parameters_[0][0] = 1.2; + std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::UserDefinedRateConstant photo({ .label_ = "a name", .scaling_factor_ = 2.0 }); + auto k = photo.calculate(state.conditions_[0], params); + EXPECT_EQ(k, 1.2 * 2.0); + EXPECT_EQ(photo.CustomParameters()[0], "a name"); +} From f62515c823f4178e614b9f27970a80d65dff4c2b Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 10 Nov 2023 11:52:32 -0800 Subject: [PATCH 234/318] add scaling factor to user-defined rate constant configurations --- include/micm/configure/solver_config.hpp | 38 +++++++++---------- .../micm/process/arrhenius_rate_constant.hpp | 1 - .../micm/process/branched_rate_constant.hpp | 1 - .../micm/process/surface_rate_constant.hpp | 1 - ...nary_chemical_activation_rate_constant.hpp | 1 - include/micm/process/troe_rate_constant.hpp | 1 - .../micm/process/tunneling_rate_constant.hpp | 1 - .../process/user_defined_rate_constant.hpp | 2 +- .../process/test_emission_config.cpp | 2 + .../process/test_first_order_loss_config.cpp | 2 + .../process/test_photolysis_config.cpp | 2 + .../process/test_user_defined_config.cpp | 2 + .../process/emission/valid/reactions.json | 3 +- .../first_order_loss/valid/reactions.json | 3 +- .../process/photolysis/valid/reactions.json | 3 +- .../process/user_defined/valid/reactions.json | 3 +- 16 files changed, 36 insertions(+), 30 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 05d18ae6a..1d37eab13 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -458,7 +458,7 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; const std::string MUSICA_NAME = "MUSICA name"; - const std::string SCALING_FACTOR = "scaling_factor"; + const std::string SCALING_FACTOR = "scaling factor"; auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { SCALING_FACTOR }); if (status != ConfigParseStatus::Success) @@ -479,17 +479,14 @@ namespace micm return products.first; } - if (object.contains(SCALING_FACTOR)) - { - std::cerr << "Scaling factor supplied to photolysis rate. This is not yet implemented." << std::endl; - } + double scaling_factor = object.contains(SCALING_FACTOR) ? object[SCALING_FACTOR].get() : 1.0; std::string name = "PHOTO." + object[MUSICA_NAME].get(); - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; @@ -835,17 +832,14 @@ namespace micm << std::endl; } - if (object.contains(SCALING_FACTOR)) - { - std::cerr << "Scaling factor supplied to emission rate. This is not yet implemented." << std::endl; - } + double scaling_factor = object.contains(SCALING_FACTOR) ? object[SCALING_FACTOR].get() : 1.0; std::string name = "EMIS." + object[MUSICA_NAME].get(); - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; @@ -855,8 +849,9 @@ namespace micm { const std::string SPECIES = "species"; const std::string MUSICA_NAME = "MUSICA name"; + const std::string SCALING_FACTOR = "scaling factor"; - auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, {}); + auto status = ValidateSchema(object, { "type", SPECIES, MUSICA_NAME }, { SCALING_FACTOR }); if (status != ConfigParseStatus::Success) { return status; @@ -877,12 +872,14 @@ namespace micm return products.first; } + double scaling_factor = object.contains(SCALING_FACTOR) ? object[SCALING_FACTOR].get() : 1.0; + std::string name = "LOSS." + object[MUSICA_NAME].get(); - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; @@ -893,8 +890,9 @@ namespace micm const std::string REACTANTS = "reactants"; const std::string PRODUCTS = "products"; const std::string MUSICA_NAME = "MUSICA name"; + const std::string SCALING_FACTOR = "scaling factor"; - auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, {}); + auto status = ValidateSchema(object, { "type", REACTANTS, PRODUCTS, MUSICA_NAME }, { SCALING_FACTOR }); if (status != ConfigParseStatus::Success) { return status; @@ -912,12 +910,14 @@ namespace micm return products.first; } + double scaling_factor = object.contains(SCALING_FACTOR) ? object[SCALING_FACTOR].get() : 1.0; + std::string name = "USER." + object[MUSICA_NAME].get(); - user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name })); + user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name }); + std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; diff --git a/include/micm/process/arrhenius_rate_constant.hpp b/include/micm/process/arrhenius_rate_constant.hpp index e680903c1..ca9f7b667 100644 --- a/include/micm/process/arrhenius_rate_constant.hpp +++ b/include/micm/process/arrhenius_rate_constant.hpp @@ -29,7 +29,6 @@ namespace micm public: const ArrheniusRateConstantParameters parameters_; - public: /// @brief Default constructor ArrheniusRateConstant(); diff --git a/include/micm/process/branched_rate_constant.hpp b/include/micm/process/branched_rate_constant.hpp index 8291bd7dc..bbb0738b8 100644 --- a/include/micm/process/branched_rate_constant.hpp +++ b/include/micm/process/branched_rate_constant.hpp @@ -37,7 +37,6 @@ namespace micm const double k0_; const double z_; - public: /// @brief Default constructor BranchedRateConstant(); diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index 714b7fbd7..a72786504 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -33,7 +33,6 @@ namespace micm double diffusion_coefficient_; // [m2 s-1] double mean_free_speed_factor_; // 8 * gas_constant / ( pi * molecular_weight ) [K-1] - public: /// @brief /// @param name A name for this reaction SurfaceRateConstant(const SurfaceRateConstantParameters& parameters); diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index afd80844f..f20a225d0 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -35,7 +35,6 @@ namespace micm public: const TernaryChemicalActivationRateConstantParameters parameters_; - public: /// @brief Default constructor TernaryChemicalActivationRateConstant(); diff --git a/include/micm/process/troe_rate_constant.hpp b/include/micm/process/troe_rate_constant.hpp index f721720f6..bc1fbccf3 100644 --- a/include/micm/process/troe_rate_constant.hpp +++ b/include/micm/process/troe_rate_constant.hpp @@ -35,7 +35,6 @@ namespace micm public: const TroeRateConstantParameters parameters_; - public: /// @brief Default constructor TroeRateConstant(); diff --git a/include/micm/process/tunneling_rate_constant.hpp b/include/micm/process/tunneling_rate_constant.hpp index deab180c8..0b07bae52 100644 --- a/include/micm/process/tunneling_rate_constant.hpp +++ b/include/micm/process/tunneling_rate_constant.hpp @@ -25,7 +25,6 @@ namespace micm public: const TunnelingRateConstantParameters parameters_; - public: /// @brief Default constructor TunnelingRateConstant(); diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index 69c2c44a6..fbaf1475f 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -19,9 +19,9 @@ namespace micm /// @brief A photolysis rate constant class UserDefinedRateConstant : public RateConstant { + public: UserDefinedRateConstantParameters parameters_; - public: /// @brief Default constructor. UserDefinedRateConstant(); diff --git a/test/unit/configure/process/test_emission_config.cpp b/test/unit/configure/process/test_emission_config.cpp index 949f41f2f..77581166f 100644 --- a/test/unit/configure/process/test_emission_config.cpp +++ b/test/unit/configure/process/test_emission_config.cpp @@ -34,6 +34,7 @@ TEST(EmissionConfig, ParseConfig) dynamic_cast(process_vector[0].rate_constant_.get()); EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.foo"); + EXPECT_EQ(emission_rate_constant->parameters_.scaling_factor_, 1.0); } // second reaction @@ -46,6 +47,7 @@ TEST(EmissionConfig, ParseConfig) dynamic_cast(process_vector[1].rate_constant_.get()); EXPECT_EQ(emission_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(emission_rate_constant->CustomParameters()[0], "EMIS.bar"); + EXPECT_EQ(emission_rate_constant->parameters_.scaling_factor_, 2.5); } } diff --git a/test/unit/configure/process/test_first_order_loss_config.cpp b/test/unit/configure/process/test_first_order_loss_config.cpp index 0046ea7a0..eca0ad082 100644 --- a/test/unit/configure/process/test_first_order_loss_config.cpp +++ b/test/unit/configure/process/test_first_order_loss_config.cpp @@ -33,6 +33,7 @@ TEST(FirstOrderLossConfig, ParseConfig) dynamic_cast(process_vector[0].rate_constant_.get()); EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.foo"); + EXPECT_EQ(first_order_loss_rate_constant->parameters_.scaling_factor_, 1.0); } // second reaction @@ -44,6 +45,7 @@ TEST(FirstOrderLossConfig, ParseConfig) dynamic_cast(process_vector[1].rate_constant_.get()); EXPECT_EQ(first_order_loss_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(first_order_loss_rate_constant->CustomParameters()[0], "LOSS.bar"); + EXPECT_EQ(first_order_loss_rate_constant->parameters_.scaling_factor_, 2.5); } } diff --git a/test/unit/configure/process/test_photolysis_config.cpp b/test/unit/configure/process/test_photolysis_config.cpp index 1e8831d9f..75acd3e51 100644 --- a/test/unit/configure/process/test_photolysis_config.cpp +++ b/test/unit/configure/process/test_photolysis_config.cpp @@ -39,6 +39,7 @@ TEST(PhotolysisConfig, ParseConfig) dynamic_cast(process_vector[0].rate_constant_.get()); EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jfoo"); + EXPECT_EQ(photo_rate_constant->parameters_.scaling_factor_, 1.0); } // second reaction @@ -54,6 +55,7 @@ TEST(PhotolysisConfig, ParseConfig) dynamic_cast(process_vector[1].rate_constant_.get()); EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "PHOTO.jbar"); + EXPECT_EQ(photo_rate_constant->parameters_.scaling_factor_, 2.5); } } diff --git a/test/unit/configure/process/test_user_defined_config.cpp b/test/unit/configure/process/test_user_defined_config.cpp index 9567bb0e3..9135a2f27 100644 --- a/test/unit/configure/process/test_user_defined_config.cpp +++ b/test/unit/configure/process/test_user_defined_config.cpp @@ -41,6 +41,7 @@ TEST(UserDefinedConfig, ParseConfig) dynamic_cast(process_vector[0].rate_constant_.get()); EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "USER.foo"); + EXPECT_EQ(photo_rate_constant->parameters_.scaling_factor_, 1.0); } // second reaction @@ -55,6 +56,7 @@ TEST(UserDefinedConfig, ParseConfig) dynamic_cast(process_vector[1].rate_constant_.get()); EXPECT_EQ(photo_rate_constant->SizeCustomParameters(), 1); EXPECT_EQ(photo_rate_constant->CustomParameters()[0], "USER.bar"); + EXPECT_EQ(photo_rate_constant->parameters_.scaling_factor_, 2.5); } } diff --git a/test/unit/unit_configs/process/emission/valid/reactions.json b/test/unit/unit_configs/process/emission/valid/reactions.json index 6320878e5..936ffef60 100644 --- a/test/unit/unit_configs/process/emission/valid/reactions.json +++ b/test/unit/unit_configs/process/emission/valid/reactions.json @@ -13,7 +13,8 @@ { "type": "EMISSION", "species": "bar", - "MUSICA name": "bar" + "MUSICA name": "bar", + "scaling factor": 2.5 } ] } diff --git a/test/unit/unit_configs/process/first_order_loss/valid/reactions.json b/test/unit/unit_configs/process/first_order_loss/valid/reactions.json index 7d5cb807d..031a6a1c2 100644 --- a/test/unit/unit_configs/process/first_order_loss/valid/reactions.json +++ b/test/unit/unit_configs/process/first_order_loss/valid/reactions.json @@ -13,7 +13,8 @@ { "type": "FIRST_ORDER_LOSS", "species": "bar", - "MUSICA name": "bar" + "MUSICA name": "bar", + "scaling factor": 2.5 } ] } diff --git a/test/unit/unit_configs/process/photolysis/valid/reactions.json b/test/unit/unit_configs/process/photolysis/valid/reactions.json index 161f38319..dc4dec9a6 100644 --- a/test/unit/unit_configs/process/photolysis/valid/reactions.json +++ b/test/unit/unit_configs/process/photolysis/valid/reactions.json @@ -24,7 +24,8 @@ "bar": { "yield": 0.5 }, "foo": { } }, - "MUSICA name": "jbar" + "MUSICA name": "jbar", + "scaling factor": 2.5 } ] } diff --git a/test/unit/unit_configs/process/user_defined/valid/reactions.json b/test/unit/unit_configs/process/user_defined/valid/reactions.json index 6f057e25d..6262fd2fe 100644 --- a/test/unit/unit_configs/process/user_defined/valid/reactions.json +++ b/test/unit/unit_configs/process/user_defined/valid/reactions.json @@ -25,7 +25,8 @@ }, "products": { "bar" : { } - } + }, + "scaling factor": 2.5 } ] } From 54a3f4466eec1bfc21dc12f81ca0c8ca1fef357a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 10 Nov 2023 20:30:14 +0000 Subject: [PATCH 235/318] Auto-format code using Clang-Format --- include/micm/configure/solver_config.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 1d37eab13..a8ff6943e 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -485,8 +485,8 @@ namespace micm user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); + std::unique_ptr rate_ptr = std::make_unique( + UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; @@ -838,8 +838,8 @@ namespace micm user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); + std::unique_ptr rate_ptr = std::make_unique( + UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; @@ -873,13 +873,13 @@ namespace micm } double scaling_factor = object.contains(SCALING_FACTOR) ? object[SCALING_FACTOR].get() : 1.0; - + std::string name = "LOSS." + object[MUSICA_NAME].get(); user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); + std::unique_ptr rate_ptr = std::make_unique( + UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; @@ -911,13 +911,13 @@ namespace micm } double scaling_factor = object.contains(SCALING_FACTOR) ? object[SCALING_FACTOR].get() : 1.0; - + std::string name = "USER." + object[MUSICA_NAME].get(); user_defined_rate_arr_.push_back(UserDefinedRateConstant({ .label_ = name, .scaling_factor_ = scaling_factor })); - std::unique_ptr rate_ptr = - std::make_unique(UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); + std::unique_ptr rate_ptr = std::make_unique( + UserDefinedRateConstantParameters{ .label_ = name, .scaling_factor_ = scaling_factor }); processes_.push_back(Process(reactants.second, products.second, std::move(rate_ptr), gas_phase_)); return ConfigParseStatus::Success; From 7f8a595ee054bf2e9e8ee560ed1601c8694a74ae Mon Sep 17 00:00:00 2001 From: David Fillmore <1524012+dwfncar@users.noreply.github.com> Date: Fri, 10 Nov 2023 14:17:49 -0700 Subject: [PATCH 236/318] Changed rate constant expression to allow for N = 0. (#354) --- .../process/ternary_chemical_activation_rate_constant.hpp | 2 +- include/micm/process/troe_rate_constant.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index f20a225d0..02a19545c 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -104,7 +104,7 @@ namespace micm return k0 / (1.0 + k0 * air_number_density / kinf) * std::pow( parameters_.Fc_, - 1.0 / (1.0 + 1.0 / parameters_.N_ * std::pow(std::log10(k0 * air_number_density / kinf), 2))); + parameters_.N_ / (parameters_.N_ + std::pow(std::log10(k0 * air_number_density / kinf), 2))); } } // namespace micm diff --git a/include/micm/process/troe_rate_constant.hpp b/include/micm/process/troe_rate_constant.hpp index bc1fbccf3..b2fed5335 100644 --- a/include/micm/process/troe_rate_constant.hpp +++ b/include/micm/process/troe_rate_constant.hpp @@ -100,7 +100,7 @@ namespace micm return k0 * air_number_density / (1.0 + k0 * air_number_density / kinf) * std::pow( parameters_.Fc_, - 1.0 / (1.0 + (1.0 / parameters_.N_) * std::pow(std::log10(k0 * air_number_density / kinf), 2))); + parameters_.N_ / (parameters_.N_ + std::pow(std::log10(k0 * air_number_density / kinf), 2))); } -} // namespace micm \ No newline at end of file +} // namespace micm From a6dabea32f8098ccaf83ff644658ac324d5d713a Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 10 Nov 2023 21:18:25 +0000 Subject: [PATCH 237/318] Auto-format code using Clang-Format --- .../micm/process/ternary_chemical_activation_rate_constant.hpp | 3 +-- include/micm/process/troe_rate_constant.hpp | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index 02a19545c..7a2ea4623 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -103,8 +103,7 @@ namespace micm return k0 / (1.0 + k0 * air_number_density / kinf) * std::pow( - parameters_.Fc_, - parameters_.N_ / (parameters_.N_ + std::pow(std::log10(k0 * air_number_density / kinf), 2))); + parameters_.Fc_, parameters_.N_ / (parameters_.N_ + std::pow(std::log10(k0 * air_number_density / kinf), 2))); } } // namespace micm diff --git a/include/micm/process/troe_rate_constant.hpp b/include/micm/process/troe_rate_constant.hpp index b2fed5335..ff2b08715 100644 --- a/include/micm/process/troe_rate_constant.hpp +++ b/include/micm/process/troe_rate_constant.hpp @@ -99,8 +99,7 @@ namespace micm return k0 * air_number_density / (1.0 + k0 * air_number_density / kinf) * std::pow( - parameters_.Fc_, - parameters_.N_ / (parameters_.N_ + std::pow(std::log10(k0 * air_number_density / kinf), 2))); + parameters_.Fc_, parameters_.N_ / (parameters_.N_ + std::pow(std::log10(k0 * air_number_density / kinf), 2))); } } // namespace micm From be55caf8e762332f7d2932858d8621e27d4ce7f8 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 10 Nov 2023 16:55:04 -0700 Subject: [PATCH 238/318] Removed unused json files. --- test/surface_rxn/configs/config.json | 1 - test/surface_rxn/configs/test_surface.json | 80 ---------------------- 2 files changed, 81 deletions(-) delete mode 100644 test/surface_rxn/configs/config.json delete mode 100644 test/surface_rxn/configs/test_surface.json diff --git a/test/surface_rxn/configs/config.json b/test/surface_rxn/configs/config.json deleted file mode 100644 index 045297e2b..000000000 --- a/test/surface_rxn/configs/config.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-files": ["species.json", "mechanism.json"]} diff --git a/test/surface_rxn/configs/test_surface.json b/test/surface_rxn/configs/test_surface.json deleted file mode 100644 index 6610d4f07..000000000 --- a/test/surface_rxn/configs/test_surface.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "camp-data" : [ - { - "type" : "RELATIVE_TOLERANCE", - "value" : 1.0e-15 - }, - { - "name" : "foo", - "type" : "CHEM_SPEC", - "diffusion coeff [m2 s-1]" : 0.95E-05, - "molecular weight [kg mol-1]" : 0.04607, - "absolute tolerance" : 1.0e-20 - }, - { - "name" : "bar", - "type" : "CHEM_SPEC" - }, - { - "name" : "baz", - "type" : "CHEM_SPEC" - }, - { - "name" : "aerosol stuff", - "type" : "CHEM_SPEC", - "phase" : "AEROSOL", - "molecular weight [kg mol-1]" : 0.5, - "density [kg m-3]" : 1000.0, - "absolute tolerance" : 1.0e-20 - }, - { - "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 - }, - { - "name" : "surface reacting phase", - "type" : "AERO_PHASE", - "species" : ["aerosol stuff", "more aerosol stuff"] - }, - { - "name" : "not surface reacting phase", - "type" : "AERO_PHASE", - "species": ["aerosol stuff"] - }, - { - "type" : "AERO_REP_SINGLE_PARTICLE", - "name" : "my aero rep 1", - "maximum computational particles" : 1 - }, - { - "type" : "AERO_REP_SINGLE_PARTICLE", - "name" : "my aero rep 2", - "maximum computational particles" : 1 - }, - { - "type" : "AERO_REP_SINGLE_PARTICLE", - "name" : "my aero rep 3", - "maximum computational particles" : 1 - }, - { - "name" : "surface mechanism", - "type" : "MECHANISM", - "reactions" : [ - { - "type" : "SURFACE", - "gas-phase reactant" : "foo", - "reaction probability" : 2.0e-2, - "gas-phase products" : { - "bar" : { }, - "baz" : { "yield" : 0.4 } - }, - "aerosol phase" : "surface reacting phase" - } - ] - } - ] -} From 5c7dff5d0dcaa748adbfe9b58fc49952984bf865 Mon Sep 17 00:00:00 2001 From: dwfncar Date: Fri, 10 Nov 2023 17:32:24 -0700 Subject: [PATCH 239/318] Removed config copy. --- test/surface_rxn/CMakeLists.txt | 7 ------- test/surface_rxn/config.json | 1 - 2 files changed, 8 deletions(-) delete mode 100644 test/surface_rxn/config.json diff --git a/test/surface_rxn/CMakeLists.txt b/test/surface_rxn/CMakeLists.txt index 70b717586..35bdb28da 100644 --- a/test/surface_rxn/CMakeLists.txt +++ b/test/surface_rxn/CMakeLists.txt @@ -12,10 +12,3 @@ endif() ################################################################################ -################################################################################ -# Copy test data - -add_custom_target(copy_surface_rxn_configs ALL ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) - -################################################################################ diff --git a/test/surface_rxn/config.json b/test/surface_rxn/config.json deleted file mode 100644 index 045297e2b..000000000 --- a/test/surface_rxn/config.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-files": ["species.json", "mechanism.json"]} From bfef464758d2219fb05b82e62cafee4a746d697e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 09:12:05 -0700 Subject: [PATCH 240/318] Added initial conditions. --- test/surface_rxn/test_surface_rxn.cpp | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index 2981b9c31..a9dd02e56 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -8,10 +8,6 @@ int main(const int argc, const char* argv[]) { - - const double rxn_gamma = 2.0e-2; // [unitless] - const double bar_yield = 1.0; // [unitless] - const double baz_yield = 0.4; // [unitless] const double DENSITY_stuff = 1000.0; // [kg m-3] const double DENSITY_more_stuff = 1000.0; // [kg m-3] const double MW_stuff = 0.5; // [kg mol-1] @@ -19,8 +15,26 @@ int main(const int argc, const char* argv[]) const double MW_foo = 0.04607; // [kg mol-1] const double Dg_foo = 0.95e-5; // diffusion coeff [m2 s-1] const double sp_number = 1.3e6; // single-particle number concentration [# m-3] + const double rxn_gamma = 2.0e-2; // [unitless] + const double bar_yield = 1.0; // [unitless] + const double baz_yield = 0.4; // [unitless] const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] + + // environment + const double temperature = 272.5; // temperature (K) + const double pressure = 101253.3; // pressure (Pa) + + // initial conditions + const double conc_foo = 1.0; + const double conc_stuff = 2.0e-3; + const double conc_more_stuff = 3.0e-3; + + double radius = mode_GMD / 2.0 * exp(5.0 * log(mode_GSD) * log(mode_GSD) / 2.0); + + double number_conc = 6.0 / (std::numbers::pi * pow(mode_GMD, 3.0) + * exp(9.0/2.0 * log(mode_GSD) * log(mode_GSD) )); + return 0; } From e634ac7aa313c4ae83a302476cf2b283f918bcf4 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 09:18:31 -0700 Subject: [PATCH 241/318] Added initial conditions. --- test/surface_rxn/test_surface_rxn.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index a9dd02e56..502fa3dbd 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -8,20 +8,19 @@ int main(const int argc, const char* argv[]) { + // parameters const double DENSITY_stuff = 1000.0; // [kg m-3] const double DENSITY_more_stuff = 1000.0; // [kg m-3] const double MW_stuff = 0.5; // [kg mol-1] const double MW_more_stuff = 0.2; // [kg mol-1] const double MW_foo = 0.04607; // [kg mol-1] const double Dg_foo = 0.95e-5; // diffusion coeff [m2 s-1] - const double sp_number = 1.3e6; // single-particle number concentration [# m-3] const double rxn_gamma = 2.0e-2; // [unitless] const double bar_yield = 1.0; // [unitless] const double baz_yield = 0.4; // [unitless] const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] - // environment const double temperature = 272.5; // temperature (K) const double pressure = 101253.3; // pressure (Pa) @@ -31,10 +30,13 @@ int main(const int argc, const char* argv[]) const double conc_stuff = 2.0e-3; const double conc_more_stuff = 3.0e-3; + // effective radius double radius = mode_GMD / 2.0 * exp(5.0 * log(mode_GSD) * log(mode_GSD) / 2.0); + // particle number concentration [# m-3] double number_conc = 6.0 / (std::numbers::pi * pow(mode_GMD, 3.0) - * exp(9.0/2.0 * log(mode_GSD) * log(mode_GSD) )); - + * exp(9.0/2.0 * log(mode_GSD) * log(mode_GSD) )) + * (conc_stuff / DENSITY_stuff + conc_more_stuff / DENSITY_more_stuff); + return 0; } From 699174e43689b72a525815f552d3fccb0956f90d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 09:55:44 -0700 Subject: [PATCH 242/318] Species declarations. --- test/surface_rxn/test_surface_rxn.cpp | 31 +++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index 502fa3dbd..972ed9b2b 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -4,22 +4,20 @@ #include #include -// based on CAMP/test/unit_rxn_data/test_rxn_surface.F90 - int main(const int argc, const char* argv[]) { - // parameters + // parameters, from CAMP/test/unit_rxn_data/test_rxn_surface.F90 + const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] + const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] const double DENSITY_stuff = 1000.0; // [kg m-3] const double DENSITY_more_stuff = 1000.0; // [kg m-3] const double MW_stuff = 0.5; // [kg mol-1] const double MW_more_stuff = 0.2; // [kg mol-1] const double MW_foo = 0.04607; // [kg mol-1] - const double Dg_foo = 0.95e-5; // diffusion coeff [m2 s-1] + const double Dg_foo = 0.95e-5; // diffusion coefficient [m2 s-1] const double rxn_gamma = 2.0e-2; // [unitless] const double bar_yield = 1.0; // [unitless] const double baz_yield = 0.4; // [unitless] - const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] - const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] // environment const double temperature = 272.5; // temperature (K) @@ -34,9 +32,24 @@ int main(const int argc, const char* argv[]) double radius = mode_GMD / 2.0 * exp(5.0 * log(mode_GSD) * log(mode_GSD) / 2.0); // particle number concentration [# m-3] - double number_conc = 6.0 / (std::numbers::pi * pow(mode_GMD, 3.0) - * exp(9.0/2.0 * log(mode_GSD) * log(mode_GSD) )) + double number_conc = 6.0 / (M_PI * std::pow(mode_GMD, 3.0) + * std::exp(9.0/2.0 * std::log(mode_GSD) * std::log(mode_GSD) )) * (conc_stuff / DENSITY_stuff + conc_more_stuff / DENSITY_more_stuff); - + + micm::Species foo("foo", + { { "molecular weight [kg mol-1]", MW_foo }, + { "diffusion coefficient [m2 s-1]", Dg_foo } }); + micm::Species bar("bar"); + micm::Species baz("baz"); + + auto state_parameters_ = micm::StateParameters{ + .number_of_grid_cells_ = 1, + .number_of_rate_constants_ = 1, + .variable_names_ = { "surface" }, + .custom_rate_parameter_labels_ + = { "effective radius [m]", + "particle number concentration [# m-3]" }, + }; + return 0; } From 95c63fdc3d11de9f355609c36e4a41018d27a73d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 10:03:24 -0700 Subject: [PATCH 243/318] Initialize state. --- test/surface_rxn/test_surface_rxn.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index 972ed9b2b..bab14d39e 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -51,5 +51,13 @@ int main(const int argc, const char* argv[]) "particle number concentration [# m-3]" }, }; + micm::State state{ state_parameters_ }; + state.custom_rate_parameters_[0][0] = radius; + state.custom_rate_parameters_[0][1] = number_conc; + state.conditions_[0].temperature_ = temperature; + std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); + micm::SurfaceRateConstant surface{ + { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + return 0; } From 09269ef15338839f99d40539a2e0e30ec86d4baf Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 17:20:11 -0700 Subject: [PATCH 244/318] Added phase definition. --- test/surface_rxn/test_surface_rxn.cpp | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index bab14d39e..a4ea38410 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -42,6 +42,9 @@ int main(const int argc, const char* argv[]) micm::Species bar("bar"); micm::Species baz("baz"); + // define phase + micm::Phase gas_phase{ std::vector{ foo, bar, baz } }; + auto state_parameters_ = micm::StateParameters{ .number_of_grid_cells_ = 1, .number_of_rate_constants_ = 1, @@ -57,7 +60,32 @@ int main(const int argc, const char* argv[]) state.conditions_[0].temperature_ = temperature; std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); micm::SurfaceRateConstant surface{ - { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + + // state.SetConcentration("foo", conc_foo); + + /* + Process process = micm::Process::create() + .reactants({ f }) + .products({ }) + .rate_constant() + .phase(gas_phase); + + auto chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + auto reactions = std::vector{ process }; + + + micm::RosenbrockSolver solver{ + chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + */ + + double time_step = 3600; // s + int nstep = 24; + + for (int i = 0; i < nstep; ++i) + { + } return 0; } From 811970b77d9e554eb56edb3c0f4cf8a193f40f35 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 17:45:05 -0700 Subject: [PATCH 245/318] Added process definition. --- test/surface_rxn/test_surface_rxn.cpp | 37 ++++++++++++++------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index a4ea38410..302eb9531 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -45,6 +45,24 @@ int main(const int argc, const char* argv[]) // define phase micm::Phase gas_phase{ std::vector{ foo, bar, baz } }; + auto chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + + micm::SurfaceRateConstant surface{ + { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + + micm::Process surface_process = micm::Process::create() + .reactants({ foo }) + .products({ micm::yields(bar, bar_yield), micm::yields(baz, baz_yield) }) + .rate_constant(surface) + .phase(gas_phase); + + // auto reactions = std::vector{ process }; + + /* + micm::RosenbrockSolver solver{ + chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + auto state_parameters_ = micm::StateParameters{ .number_of_grid_cells_ = 1, .number_of_rate_constants_ = 1, @@ -59,27 +77,10 @@ int main(const int argc, const char* argv[]) state.custom_rate_parameters_[0][1] = number_conc; state.conditions_[0].temperature_ = temperature; std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); - micm::SurfaceRateConstant surface{ - { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + */ // state.SetConcentration("foo", conc_foo); - /* - Process process = micm::Process::create() - .reactants({ f }) - .products({ }) - .rate_constant() - .phase(gas_phase); - - auto chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); - auto reactions = std::vector{ process }; - - - micm::RosenbrockSolver solver{ - chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - */ - double time_step = 3600; // s int nstep = 24; From ed01811caf5723e776ea30511011f7294b8ad666 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 12 Nov 2023 18:22:57 -0700 Subject: [PATCH 246/318] Added custom rate paramters. --- test/surface_rxn/test_surface_rxn.cpp | 41 ++++++++++++++------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/surface_rxn/test_surface_rxn.cpp index 302eb9531..2ade667a7 100644 --- a/test/surface_rxn/test_surface_rxn.cpp +++ b/test/surface_rxn/test_surface_rxn.cpp @@ -42,50 +42,51 @@ int main(const int argc, const char* argv[]) micm::Species bar("bar"); micm::Species baz("baz"); - // define phase + // Phase micm::Phase gas_phase{ std::vector{ foo, bar, baz } }; + // System auto chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + // Rate micm::SurfaceRateConstant surface{ { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + // Process micm::Process surface_process = micm::Process::create() .reactants({ foo }) .products({ micm::yields(bar, bar_yield), micm::yields(baz, baz_yield) }) .rate_constant(surface) .phase(gas_phase); - // auto reactions = std::vector{ process }; + auto reactions = std::vector{ surface_process }; - /* - micm::RosenbrockSolver solver{ + // Solver + micm::RosenbrockSolver<> solver{ chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - auto state_parameters_ = micm::StateParameters{ - .number_of_grid_cells_ = 1, - .number_of_rate_constants_ = 1, - .variable_names_ = { "surface" }, - .custom_rate_parameter_labels_ - = { "effective radius [m]", - "particle number concentration [# m-3]" }, - }; - - micm::State state{ state_parameters_ }; - state.custom_rate_parameters_[0][0] = radius; - state.custom_rate_parameters_[0][1] = number_conc; + // State + micm::State state = solver.GetState(); state.conditions_[0].temperature_ = temperature; - std::vector::const_iterator params = state.custom_rate_parameters_[0].begin(); - */ - - // state.SetConcentration("foo", conc_foo); + state.conditions_[0].pressure_ = pressure; + state.SetCustomRateParameter("foo.effective radius [m]", radius); + state.SetCustomRateParameter("foo.particle number concentration [# m-3]", number_conc); + state.SetConcentration(foo, conc_foo); double time_step = 3600; // s int nstep = 24; for (int i = 0; i < nstep; ++i) { + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } } return 0; From 05ff1267751e8e01b3de10b3ad2fc9150aa26f3b Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 13 Nov 2023 08:49:40 -0800 Subject: [PATCH 247/318] address data type conversion warning --- include/micm/solver/state.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index b6e71c72e..6197fabe5 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -52,7 +52,7 @@ namespace micm inline void State::SetConcentrations( const std::unordered_map>& species_to_concentration) { - const int num_grid_cells = conditions_.size(); + const std::size_t num_grid_cells = conditions_.size(); for (const auto& pair : species_to_concentration) SetConcentration({ pair.first }, pair.second); } From c5967441e18e2f10aa6c145414f194061db04feb Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 13 Nov 2023 18:44:05 +0000 Subject: [PATCH 248/318] Added Chapman configs. --- etc/configs/kpp/arr.eqn | 15 -- etc/configs/kpp/arr.spc | 19 -- etc/configs/kpp/atoms_H_to_Ar.kpp | 22 -- etc/configs/kpp/chapman.eqn | 10 - etc/configs/kpp/chapman.spc | 11 - etc/configs/kpp/small_strato.def | 42 ---- etc/configs/kpp/small_strato.eqn | 15 -- etc/configs/kpp/small_strato.spc | 17 -- etc/scripts/kpp_to_micm.py | 383 ------------------------------ etc/scripts/test_kpp_to_micm.py | 40 ---- examples/Chapman/reactions.json | 97 ++++++++ examples/Chapman/species.json | 29 +++ 12 files changed, 126 insertions(+), 574 deletions(-) delete mode 100644 etc/configs/kpp/arr.eqn delete mode 100644 etc/configs/kpp/arr.spc delete mode 100644 etc/configs/kpp/atoms_H_to_Ar.kpp delete mode 100644 etc/configs/kpp/chapman.eqn delete mode 100644 etc/configs/kpp/chapman.spc delete mode 100644 etc/configs/kpp/small_strato.def delete mode 100644 etc/configs/kpp/small_strato.eqn delete mode 100644 etc/configs/kpp/small_strato.spc delete mode 100644 etc/scripts/kpp_to_micm.py delete mode 100644 etc/scripts/test_kpp_to_micm.py create mode 100644 examples/Chapman/reactions.json create mode 100644 examples/Chapman/species.json diff --git a/etc/configs/kpp/arr.eqn b/etc/configs/kpp/arr.eqn deleted file mode 100644 index a4016e5d2..000000000 --- a/etc/configs/kpp/arr.eqn +++ /dev/null @@ -1,15 +0,0 @@ -#EQUATIONS - -<1> NO2 + hv = NO + O3P : 6.69e-1*(SUN/60.0e0); -<2> O3P + O2 + AIR = O3 : ARR_ac(5.68e-34, -2.80e0); -<3> O3P + O3 = 2O2 : ARR_ab(8.00e-12, 2060.0e0); -<4> O3P + NO + AIR = NO2 : ARR_ac(1.00e-31, -1.60e0); -<5> O3P + NO2 = NO : ARR_ab(6.50e-12,- 120.0e0); -<7> O3 + NO = NO2 : ARR_ab(1.80e-12, 1370.0e0); -<8> O3 + NO2 = NO3 : ARR_ab(1.40e-13, 2470.0e0); -<9> NO + NO3 = 2NO2 : ARR_ab(1.80e-11, -110.0e0); -<10> NO + NO + O2 = 2NO2 : ARR_ab(3.30e-39, -530.0e0); -<14> NO2 + NO3 = NO + NO2 : ARR_ab(4.50e-14, 1260.0e0); -<15> NO3 + hv = NO : 1.59e0*(SUN/60.0e0); -<16> NO3 + hv = NO2 + O3P : 1.50e+1*(SUN/60.0e0); -<17> O3 + hv = O3P : 3.76e-2*(SUN/60.0e0); diff --git a/etc/configs/kpp/arr.spc b/etc/configs/kpp/arr.spc deleted file mode 100644 index cffabd3bc..000000000 --- a/etc/configs/kpp/arr.spc +++ /dev/null @@ -1,19 +0,0 @@ -#INCLUDE atoms.kpp - -#DEFVAR - - O3 = 3O; - H2O2 = 2H + 2O; - NO = N + O; - NO2 = N + 2O; - NO3 = N + 3O; - O3P = O; - - - -#DEFFIX - AIR = IGNORE; - O2 = 2O; - H2O = 2H + O; - H2 = 2H; - CH4 = C + 4H; diff --git a/etc/configs/kpp/atoms_H_to_Ar.kpp b/etc/configs/kpp/atoms_H_to_Ar.kpp deleted file mode 100644 index e0d373315..000000000 --- a/etc/configs/kpp/atoms_H_to_Ar.kpp +++ /dev/null @@ -1,22 +0,0 @@ -#ATOMS -H { 1 Hydrogen }; -He { 2 Helium }; -Li { 3 Lithium }; -Be { 4 Beryllium }; -B { 5 Boron }; -C { 6 Carbon }; -N { 7 Nitrogen }; -O { 8 Oxygen }; -F { 9 Fluorine }; -Ne { 10 Neon }; -Na { 11 Sodium }; -Mg { 12 Magnesium }; -Al { 13 Aluminium }; -Si { 14 Silicon }; -P { 15 Phosphorus }; -S { 16 Sulfur }; -Cl { 17 Chlorine }; -Ar { 18 Argon }; - -Pls; {plus (positive charge as pseudo-atom for charge balance)} -Min; {minus (negative charge as pseudo-atom for charge balance)} diff --git a/etc/configs/kpp/chapman.eqn b/etc/configs/kpp/chapman.eqn deleted file mode 100644 index c15640dd5..000000000 --- a/etc/configs/kpp/chapman.eqn +++ /dev/null @@ -1,10 +0,0 @@ -#EQUATIONS { Small Stratospheric Mechanism } - - O2 + hv = 2O : (2.643E-10) * SUN*SUN*SUN; - O + O2 = O3 : (8.018E-17); - O3 + hv = O + O2 : (6.120E-04) * SUN; - O + O3 = 2O2 : (1.576E-15); - O3 + hv = O1D + O2 : (1.070E-03) * SUN*SUN; - O1D + M = O + M : (7.110E-11); - O1D + O3 = 2O2 : (1.200E-10); - diff --git a/etc/configs/kpp/chapman.spc b/etc/configs/kpp/chapman.spc deleted file mode 100644 index 0dab2f343..000000000 --- a/etc/configs/kpp/chapman.spc +++ /dev/null @@ -1,11 +0,0 @@ -#INCLUDE atoms.kpp - -#DEFVAR -O = O; { Oxygen atomic ground state } -O1D = O; { Oxygen atomic excited state } -O3 = O + O + O; { Ozone } - -#DEFFIX -M = O + O + N + N; { Atmospheric generic molecule } -O2 = O + O; { Molecular oxygen } - diff --git a/etc/configs/kpp/small_strato.def b/etc/configs/kpp/small_strato.def deleted file mode 100644 index 3e02128ae..000000000 --- a/etc/configs/kpp/small_strato.def +++ /dev/null @@ -1,42 +0,0 @@ -#INCLUDE small_strato.spc { Includes file w/ species definitons } -#INCLUDE small_strato.eqn { Includes file w/ chemical equations } - -#LOOKATALL { Output all species to small_strato.dat} -#MONITOR O3;N;O2;O;NO;O1D;NO2; { Print selected species to screen } - -#CHECK O; N; { Check Mass Balance of oxygen & nitrogen } - -#INITVALUES { Set initial values of species } - CFACTOR = 1. ; { and set units conversion factor to 1 } - O1D = 9.906E+01 ; - O = 6.624E+08 ; - O3 = 5.326E+11 ; - O2 = 1.697E+16 ; - NO = 8.725E+08 ; - NO2 = 2.240E+08 ; - M = 8.120E+16 ; - -{ Fortran code to be inlined into ROOT_Global } -#INLINE F90_INIT - TSTART = (12*3600) - TEND = TSTART + (3*24*3600) - DT = 0.25*3600 - TEMP = 270 -#ENDINLINE - -{ Matlab code to be inlined into ROOT_Global } -#INLINE MATLAB_INIT - global TSTART TEND DT TEMP - TSTART = (12*3600); - TEND = TSTART + (3*24*3600); - DT = 0.25*3600; - TEMP = 270; -#ENDINLINE - -{ C code to be inlined into ROOT_GLOBAL } -#INLINE C_INIT - TSTART = (12*3600); - TEND = TSTART + (3*24*3600); - DT = 0.25*3600; - TEMP = 270; -#ENDINLINE diff --git a/etc/configs/kpp/small_strato.eqn b/etc/configs/kpp/small_strato.eqn deleted file mode 100644 index 8c9b530d1..000000000 --- a/etc/configs/kpp/small_strato.eqn +++ /dev/null @@ -1,15 +0,0 @@ -#EQUATIONS { Small Stratospheric Mechanism } - - - O2 + hv = 2O : (2.643E-10) * SUN*SUN*SUN; - O + O2 = O3 : (8.018E-17); - O3 + hv = O + O2 : (6.120E-04) * SUN; - O + O3 = 2O2 : (1.576E-15); - O3 + hv = O1D + O2 : (1.070E-03) * SUN*SUN; - O1D + M = O + M : (7.110E-11); - O1D + O3 = 2O2 : (1.200E-10); - NO + O3 = NO2 + O2 : (6.062E-15); - NO2 + O = NO + O2 : (1.069E-11); - NO2 + hv = NO + O : (1.289E-02) * SUN; - - diff --git a/etc/configs/kpp/small_strato.spc b/etc/configs/kpp/small_strato.spc deleted file mode 100644 index 841314cd3..000000000 --- a/etc/configs/kpp/small_strato.spc +++ /dev/null @@ -1,17 +0,0 @@ -#INCLUDE atoms.kpp - -#DEFVAR -O = O; { Oxygen atomic ground state } -O1D = O; { Oxygen atomic excited state } -O3 = O + O + O; { Ozone } -NO = N + O; { Nitric oxide } -NO2 = N + O + O; { Nitrogen dioxide } - - -#DEFFIX -M = O + O + N + N;{ Atmospheric generic molecule } -O2 = O + O; { Molecular oxygen } - - - - diff --git a/etc/scripts/kpp_to_micm.py b/etc/scripts/kpp_to_micm.py deleted file mode 100644 index 956f88151..000000000 --- a/etc/scripts/kpp_to_micm.py +++ /dev/null @@ -1,383 +0,0 @@ -""" -Copyright (C) 2023 -National Center for Atmospheric Research, -SPDX-License-Identifier: Apache-2.0 - -File: - kpp_to_micm.py - -Usage: - python kpp_to_micm.py - python kpp_to_micm.py --help - -Description: - kpp_to_micm.py translates KPP config files to MICM JSON config files - (desginated by the suffixes .kpp, .spc, .eqn, .def) - from a single directory specified by the - --kpp_dir and --kpp_name arguments. - - In the initial implementation, - the KPP sections #ATOMS (not yet used), #DEFFIX, - #DEFVAR, and #EQUATIONS are read and parsed. - Equations with the hv reactant are MICM PHOTOLYSIS reactions, - equations with a single coefficient are assumed to be ARRHENIUS reactions. - -TODO: - (1) Translate stoichiometric coefficients in the equation string - with more than one digit. - (2) Add pytest unit test for method micm_equation_json. - (3+) Add support for many more reaction types ... - -Revision History: - v1.00 2023/08/03 Initial implementation - v1.01 2023/08/16 Added method parse_kpp_arrhenius - v1.02 2023/10/05 Added unit conversion option -""" - -import os -import sys -import argparse -import logging -import json -from glob import glob - -__version__ = 'v1.02' - -""" -Physical Constants -""" -N_Avogadro = 6.02214076e23 - - -def read_kpp_config(kpp_dir, kpp_name): - """ - Read all KPP config files in a directory - - Parameters - (str) kpp_dir: KPP directory - - Returns - (list of str): all lines from all config files - """ - - suffixes = ['.kpp', '.spc', '.eqn', '.def'] - - lines = list() - - for suffix in suffixes: - files = glob(os.path.join(kpp_dir, kpp_name + '*' + suffix)) - logging.debug(files) - for filename in files: - with open(filename, 'r') as f: - lines.extend(f.readlines()) - - # remove empty lines and tabs - lines = [line.replace('\t', '') for line in lines if line.strip()] - - for line in lines: - logging.debug(line.strip()) - - return lines - - -def split_by_section(lines): - """ - Split KPP config lines by section - - Parameters - (list of str) lines: all lines config files - - Returns - (dict of list of str): lines in each section - """ - - sections = {'#ATOMS': [], - '#DEFVAR': [], - '#DEFFIX': [], - '#EQUATIONS': []} - - joined_lines = ''.join(lines) - section_blocks = joined_lines.split('#') - - for section in sections: - for section_block in section_blocks: - if section.replace('#', '') in section_block: - sections[section].extend(section_block.split('\n')[1:-1]) - - return sections - - -def micm_species_json(lines, fixed=False, tolerance=1.0e-12): - """ - Generate MICM species JSON - - Parameters - (list of str) lines: lines of species section - (bool) fixed: set constant tracer - (float) tolerance: absolute tolerance - - Returns - (list of dict): list of MICM species entries - """ - - species_json = list() # list of dict - - for line in lines: - lhs, rhs = tuple(line.split('=')) - logging.debug((lhs, rhs)) - species_dict = {'name': lhs.strip().lstrip(), 'type': 'CHEM_SPEC'} - if fixed: - species_dict['tracer type'] = 'CONSTANT' - else: - species_dict['absolute tolerance'] = tolerance - species_json.append(species_dict) - - return species_json - - -def parse_kpp_arrhenius(kpp_str, to_si_units=False, - N_reactants=2): - """ - Parse KPP Arrhenius reaction - - Parameters - (str) kpp_str: Arrhenius reaction string - (bool) to_si_units: convert A coefficient - from (cm^3 molecule-1)^(N-1) s-1 to (m^3 mol-1)^(N-1) s-1 - where N is the number of reactants - (int) N_reactants: number of reactants - - Returns - (dict): MICM Arrhenius reaction coefficients - - Arrhenius formula from KPP - -------------------------- - - KPP_REAL ARR_abc( float A0, float B0, float C0 ) - { - double ARR_RES; - - ARR_RES = (double)A0 - * exp( -(double)B0/TEMP ) - * pow( (TEMP/300.0), (double)C0 ); - - return (KPP_REAL)ARR_RES; - } - - Arrhenius formula from MICM - --------------------------- - - inline double ArrheniusRateConstant::calculate( - const double& temperature, const double& pressure) const - { - return parameters_.A_ * std::exp(parameters_.C_ / temperature) - * pow(temperature / parameters_.D_, parameters_.B_) * - (1.0 + parameters_.E_ * pressure); - } - """ - logging.debug(kpp_str) - coeffs = [float(coeff.replace(' ', '')) for coeff in - kpp_str.split('(')[1].split(')')[0].split(',')] - logging.debug(coeffs) - arr_dict = dict() - arr_dict['type'] = 'ARRHENIUS' - # note the interchange of B and C, and change of sign - # in the KPP and MICM conventions - if ('_abc(' in kpp_str): - arr_dict['A'] = coeffs[0] - arr_dict['B'] = coeffs[2] - arr_dict['C'] = - coeffs[1] - arr_dict['D'] = 300.0 - elif ('_ab(' in kpp_str): - arr_dict['A'] = coeffs[0] - arr_dict['C'] = - coeffs[1] - arr_dict['D'] = 300.0 - elif ('_ac(' in kpp_str): - arr_dict['A'] = coeffs[0] - arr_dict['B'] = coeffs[1] - arr_dict['D'] = 300.0 - else: - logging.error('unrecognized KPP Arrhenius syntax') - if to_si_units: - arr_dict['A'] *= (N_Avogadro * 1.0e-6)**(N_reactants - 1) - logging.debug(arr_dict) - return arr_dict - - -def micm_equation_json(lines, to_si_units=False): - """ - Generate MICM equation JSON - - Parameters - (list of str) lines: lines of equation section - (bool) to_si_units: convert A coefficient - from cm^3 molecule-1 s-1 to m^3 mol-1 s-1 - - Returns - (list of dict): list of MICM equation entries - """ - - equations = list() # list of dict - - for line in lines: - logging.debug(line) - - # split on equal sign into left hand and right hand sides - lhs, rhs = tuple(line.split('=')) - - # extract reaction coefficients - rhs, coeffs = tuple(rhs.split(':')) - coeffs = coeffs.replace(';', '') - - # get reactants and products - reactants = lhs.split('+') - products = rhs.split('+') - - # extract equation label delimited by < > - label, reactants[0] = tuple(reactants[0].split('>')) - label = label.lstrip('<') - - # remove trailing and leading whitespace - reactants = [reactant.strip().lstrip() for reactant in reactants] - products = [product.strip().lstrip() for product in products] - - N_reactants = len(reactants) - - equation_dict = dict() - - if 'SUN' in coeffs: - equation_dict['type'] = 'PHOTOLYSIS' - elif 'ARR' in coeffs: - equation_dict = parse_kpp_arrhenius(coeffs, - to_si_units=to_si_units, N_reactants=N_reactants) - else: - # default to Arrhenius with a single coefficient - coeffs = coeffs.replace('(', '').replace(')', '') - equation_dict['type'] = 'ARRHENIUS' - equation_dict['A'] = float(coeffs) - if to_si_units: - equation_dict['A'] *= (N_Avogadro * 1.0e-6)**(N_reactants - 1) - - equation_dict['reactants'] = dict() - equation_dict['products'] = dict() - - for reactant in reactants: - if reactant[0].isdigit(): - equation_dict['reactants'][reactant[1:]] \ - = {'qty': float(reactant[0])} - elif 'hv' in reactant: - pass - else: - equation_dict['reactants'][reactant] = dict() - - for product in products: - if product[0].isdigit(): - equation_dict['products'][product[1:]] \ - = {'yield': float(product[0])} - else: - equation_dict['products'][product] = dict() - - equation_dict['MUSICA name'] = label - - equations.append(equation_dict) - - return equations - - -if __name__ == '__main__': - - """ - Parse command line arguments - """ - parser = argparse.ArgumentParser() - parser.add_argument('--logfile', type=str, - default=sys.stdout, - help='log file (default stdout)') - parser.add_argument('--kpp_dir', type=str, - default=os.path.join('..', 'configs', 'kpp'), - help='KPP input config directory') - parser.add_argument('--kpp_name', type=str, - default='small_strato', - help='KPP config name') - parser.add_argument('--micm_dir', type=str, - default=os.path.join('..', 'configs', 'micm'), - help='MICM output species config file') - parser.add_argument('--mechanism', type=str, - default='Chapman', - help='mechanism name') - parser.add_argument('--to_si_units', action='store_true', - default=False, - help='convert units from molecules cm-3 to mol m-3') - parser.add_argument('--debug', action='store_true', - help='set logging level to debug') - args = parser.parse_args() - - """ - Setup logging - """ - logging_level = logging.DEBUG if args.debug else logging.INFO - logging.basicConfig(stream=args.logfile, level=logging_level) - - """ - Read KPP config files - """ - lines = read_kpp_config(args.kpp_dir, args.kpp_name) - - """ - Split KPP config by section - """ - sections = split_by_section(lines) - for section in sections: - logging.info('____ KPP section %s ____' % section) - for line in sections[section]: - logging.info(line) - print('\n') - - """ - Generate MICM species JSON from KPP #DEFFIX section - """ - deffix_json = micm_species_json(sections['#DEFFIX'], fixed=True) - - """ - Generate MICM species JSON from KPP #DEFVAR section - """ - defvar_json = micm_species_json(sections['#DEFVAR']) - - """ - Generate MICM equations JSON from KPP #EQUATIONS section - """ - equations_json = micm_equation_json(sections['#EQUATIONS'], - to_si_units=args.to_si_units) - - """ - Assemble MICM species JSON - """ - micm_species_json = {'camp-data': deffix_json + defvar_json} - micm_species_json_str = json.dumps(micm_species_json, indent=4) - logging.info('____ MICM species ____') - logging.info(micm_species_json_str) - print('\n') - - """ - Assemble MICM reactions JSON - """ - micm_reactions_json = {'camp-data': - [{'name': args.mechanism, 'type': 'MECHANISM', 'reactions': equations_json}]} - micm_reactions_json_str = json.dumps(micm_reactions_json, indent=4) - logging.info('____ MICM reactions ____') - logging.info(micm_reactions_json_str) - print('\n') - - """ - Write MICM JSON - """ - micm_mechanism_dir = os.path.join(args.micm_dir, args.mechanism) - if not os.path.exists(args.micm_dir): - os.mkdir(args.micm_dir) - if not os.path.exists(micm_mechanism_dir): - os.mkdir(micm_mechanism_dir) - with open(os.path.join(micm_mechanism_dir, 'species.json'), 'w') as f: - json.dump(micm_species_json, f, indent=4) - with open(os.path.join(micm_mechanism_dir, 'reactions.json'), 'w') as f: - json.dump(micm_reactions_json, f, indent=4) - diff --git a/etc/scripts/test_kpp_to_micm.py b/etc/scripts/test_kpp_to_micm.py deleted file mode 100644 index 2eac98e50..000000000 --- a/etc/scripts/test_kpp_to_micm.py +++ /dev/null @@ -1,40 +0,0 @@ -""" -Copyright (C) 2023 -National Center for Atmospheric Research, -SPDX-License-Identifier: Apache-2.0 - -File: - test_kpp_to_micm.py - -Usage: - pytest test_kpp_to_micm.py --log-cli-level=DEBUG -""" - -import kpp_to_micm - -def test_parse_kpp_arrhenius(): - """ - examples - ARR_ab(1.0e-12, 2000.0) - ARR_ac(1.0e-12, -3.0) - ARR_abc(1.0e-12, 2000.0, -3.0) - """ - - kpp_A, kpp_B, kpp_C = 1.0e-12, 2000.0, -3.0 - - arr_dict = kpp_to_micm.parse_kpp_arrhenius( - 'ARR_ab(%.2e, %.2f)' % (kpp_A, kpp_B)) - assert arr_dict['A'] == kpp_A - assert arr_dict['C'] == - kpp_B - - arr_dict = kpp_to_micm.parse_kpp_arrhenius( - 'ARR_ac(%.2e, %.2f)' % (kpp_A, kpp_C)) - assert arr_dict['A'] == kpp_A - assert arr_dict['B'] == kpp_C - - arr_dict = kpp_to_micm.parse_kpp_arrhenius( - 'ARR_abc(%.2e, %.2f, %.2f)' % (kpp_A, kpp_B, kpp_C)) - assert arr_dict['A'] == kpp_A - assert arr_dict['C'] == - kpp_B - assert arr_dict['B'] == kpp_C - diff --git a/examples/Chapman/reactions.json b/examples/Chapman/reactions.json new file mode 100644 index 000000000..85129afe1 --- /dev/null +++ b/examples/Chapman/reactions.json @@ -0,0 +1,97 @@ +{ + "camp-data": [ + { + "name": "Chapman", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "O2": {} + }, + "products": { + "O": { + "yield": 2.0 + } + }, + "MUSICA name": "R1" + }, + { + "type": "ARRHENIUS", + "A": 48.28552461368, + "reactants": { + "O": {}, + "O2": {} + }, + "products": { + "O3": {} + }, + "MUSICA name": "R2" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "O3": {} + }, + "products": { + "O": {}, + "O2": {} + }, + "MUSICA name": "R3" + }, + { + "type": "ARRHENIUS", + "A": 949.089383776, + "reactants": { + "O": {}, + "O3": {} + }, + "products": { + "O2": { + "yield": 2.0 + } + }, + "MUSICA name": "R4" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "O3": {} + }, + "products": { + "O1D": {}, + "O2": {} + }, + "MUSICA name": "R5" + }, + { + "type": "ARRHENIUS", + "A": 42817420.803600006, + "reactants": { + "O1D": {}, + "M": {} + }, + "products": { + "O": {}, + "M": {} + }, + "MUSICA name": "R6" + }, + { + "type": "ARRHENIUS", + "A": 72265689.12, + "reactants": { + "O1D": {}, + "O3": {} + }, + "products": { + "O2": { + "yield": 2.0 + } + }, + "MUSICA name": "R7" + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/Chapman/species.json b/examples/Chapman/species.json new file mode 100644 index 000000000..c962907ff --- /dev/null +++ b/examples/Chapman/species.json @@ -0,0 +1,29 @@ +{ + "camp-data": [ + { + "name": "M", + "type": "CHEM_SPEC", + "tracer type": "CONSTANT" + }, + { + "name": "O2", + "type": "CHEM_SPEC", + "tracer type": "CONSTANT" + }, + { + "name": "O", + "type": "CHEM_SPEC", + "absolute tolerance": 1e-12 + }, + { + "name": "O1D", + "type": "CHEM_SPEC", + "absolute tolerance": 1e-12 + }, + { + "name": "O3", + "type": "CHEM_SPEC", + "absolute tolerance": 1e-12 + } + ] +} \ No newline at end of file From 2f1ade175847a703c66c0457af9c5a7203766a48 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 13 Nov 2023 18:45:40 +0000 Subject: [PATCH 249/318] Added config.json. --- examples/Chapman/config.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 examples/Chapman/config.json diff --git a/examples/Chapman/config.json b/examples/Chapman/config.json new file mode 100644 index 000000000..04d0ef289 --- /dev/null +++ b/examples/Chapman/config.json @@ -0,0 +1,6 @@ +{ + "camp-files": [ + "species.json", + "reactions.json" + ] +} From 5190925af72dd8870d056d1170419adcff0b6f68 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 13 Nov 2023 18:54:31 +0000 Subject: [PATCH 250/318] Moved test_Chapman.cpp to test/examples. --- examples/Chapman/config.json | 7 +- .../test_Chapman.cpp} | 0 test/kpp/CMakeLists.txt | 21 ---- test/kpp/configs/kpp_chapman/config.json | 1 - test/kpp/configs/kpp_chapman/reactions.json | 97 ------------------- test/kpp/configs/kpp_chapman/species.json | 29 ------ 6 files changed, 1 insertion(+), 154 deletions(-) rename test/{kpp/test_kpp_to_micm.cpp => examples/test_Chapman.cpp} (100%) delete mode 100644 test/kpp/CMakeLists.txt delete mode 100644 test/kpp/configs/kpp_chapman/config.json delete mode 100644 test/kpp/configs/kpp_chapman/reactions.json delete mode 100644 test/kpp/configs/kpp_chapman/species.json diff --git a/examples/Chapman/config.json b/examples/Chapman/config.json index 04d0ef289..61165b00d 100644 --- a/examples/Chapman/config.json +++ b/examples/Chapman/config.json @@ -1,6 +1 @@ -{ - "camp-files": [ - "species.json", - "reactions.json" - ] -} +{"camp-files": ["species.json", "reactions.json"]} diff --git a/test/kpp/test_kpp_to_micm.cpp b/test/examples/test_Chapman.cpp similarity index 100% rename from test/kpp/test_kpp_to_micm.cpp rename to test/examples/test_Chapman.cpp diff --git a/test/kpp/CMakeLists.txt b/test/kpp/CMakeLists.txt deleted file mode 100644 index 420b18ca0..000000000 --- a/test/kpp/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -################################################################################ -# Test utilities - -include(test_util) - -################################################################################ -# Tests - -if(ENABLE_JSON) - create_standard_test(NAME kpp_to_micm SOURCES test_kpp_to_micm.cpp) -endif() - -################################################################################ - -################################################################################ -# Copy test data - -add_custom_target(copy_kpp_configs ALL ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_CURRENT_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) - -################################################################################ diff --git a/test/kpp/configs/kpp_chapman/config.json b/test/kpp/configs/kpp_chapman/config.json deleted file mode 100644 index 61165b00d..000000000 --- a/test/kpp/configs/kpp_chapman/config.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-files": ["species.json", "reactions.json"]} diff --git a/test/kpp/configs/kpp_chapman/reactions.json b/test/kpp/configs/kpp_chapman/reactions.json deleted file mode 100644 index 85129afe1..000000000 --- a/test/kpp/configs/kpp_chapman/reactions.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "camp-data": [ - { - "name": "Chapman", - "type": "MECHANISM", - "reactions": [ - { - "type": "PHOTOLYSIS", - "reactants": { - "O2": {} - }, - "products": { - "O": { - "yield": 2.0 - } - }, - "MUSICA name": "R1" - }, - { - "type": "ARRHENIUS", - "A": 48.28552461368, - "reactants": { - "O": {}, - "O2": {} - }, - "products": { - "O3": {} - }, - "MUSICA name": "R2" - }, - { - "type": "PHOTOLYSIS", - "reactants": { - "O3": {} - }, - "products": { - "O": {}, - "O2": {} - }, - "MUSICA name": "R3" - }, - { - "type": "ARRHENIUS", - "A": 949.089383776, - "reactants": { - "O": {}, - "O3": {} - }, - "products": { - "O2": { - "yield": 2.0 - } - }, - "MUSICA name": "R4" - }, - { - "type": "PHOTOLYSIS", - "reactants": { - "O3": {} - }, - "products": { - "O1D": {}, - "O2": {} - }, - "MUSICA name": "R5" - }, - { - "type": "ARRHENIUS", - "A": 42817420.803600006, - "reactants": { - "O1D": {}, - "M": {} - }, - "products": { - "O": {}, - "M": {} - }, - "MUSICA name": "R6" - }, - { - "type": "ARRHENIUS", - "A": 72265689.12, - "reactants": { - "O1D": {}, - "O3": {} - }, - "products": { - "O2": { - "yield": 2.0 - } - }, - "MUSICA name": "R7" - } - ] - } - ] -} \ No newline at end of file diff --git a/test/kpp/configs/kpp_chapman/species.json b/test/kpp/configs/kpp_chapman/species.json deleted file mode 100644 index c962907ff..000000000 --- a/test/kpp/configs/kpp_chapman/species.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "camp-data": [ - { - "name": "M", - "type": "CHEM_SPEC", - "tracer type": "CONSTANT" - }, - { - "name": "O2", - "type": "CHEM_SPEC", - "tracer type": "CONSTANT" - }, - { - "name": "O", - "type": "CHEM_SPEC", - "absolute tolerance": 1e-12 - }, - { - "name": "O1D", - "type": "CHEM_SPEC", - "absolute tolerance": 1e-12 - }, - { - "name": "O3", - "type": "CHEM_SPEC", - "absolute tolerance": 1e-12 - } - ] -} \ No newline at end of file From f787bdffb066299f29a4d26bb2bb508940c3bc6f Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 13 Nov 2023 18:55:28 +0000 Subject: [PATCH 251/318] Removed kpp subdir from CMakeLists.txt. --- test/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5c60c6253..e24e29012 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,4 +8,3 @@ if(ENABLE_JSON) add_subdirectory(examples) endif() add_subdirectory(tutorial) -add_subdirectory(kpp) From 3773bfb1399a69091cce1354845a6c62c25f0e06 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 13 Nov 2023 19:04:56 +0000 Subject: [PATCH 252/318] Removed kpp dir, added test_Chapman example. --- test/examples/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt index fdad13e99..bcd85040e 100644 --- a/test/examples/CMakeLists.txt +++ b/test/examples/CMakeLists.txt @@ -7,4 +7,5 @@ add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory ################################################################################ # Build example tests +create_standard_test(NAME Chapman SOURCES test_Chapman.cpp) create_standard_test(NAME TS1 SOURCES test_TS1.cpp) From 3d4ae6fbde59f4cffda5fa880d02f3be30a85a04 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 13 Nov 2023 19:18:04 +0000 Subject: [PATCH 253/318] Use configs from build/examples/Chapman. --- test/examples/test_Chapman.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/examples/test_Chapman.cpp b/test/examples/test_Chapman.cpp index a491bc73a..1a2a633f9 100644 --- a/test/examples/test_Chapman.cpp +++ b/test/examples/test_Chapman.cpp @@ -38,7 +38,7 @@ int main(const int argc, const char* argv[]) micm::SolverConfig solver_config; // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./configs/kpp_chapman"); + micm::ConfigParseStatus status = solver_config.ReadAndParse("./examples/Chapman"); if (status != micm::ConfigParseStatus::Success) { From 79425ff8d84918ef3c60b1de3b7216dea7420658 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Mon, 13 Nov 2023 15:57:34 -0600 Subject: [PATCH 254/318] correcting verb? preposition? grammar.... --- include/micm/util/vector_matrix.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index e1f99d661..edae9ed14 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -60,7 +60,7 @@ namespace micm [&](T const &elem) { *iter = elem; - // don't iterate passed the end of the vector + // don't iterate past the end of the vector std::size_t remaining_elements = std::distance(iter, matrix_.data_.end()); iter += std::min(L, remaining_elements); }); @@ -73,7 +73,7 @@ namespace micm for (auto &elem : vec) { elem = *iter; - // don't iterate passed the end of the vector + // don't iterate past the end of the vector std::size_t remaining_elements = std::distance(iter, matrix_.data_.end()); iter += std::min(L, remaining_elements); } @@ -169,7 +169,7 @@ namespace micm for (auto &elem : other_row) { *iter = elem; - // don't iterate passed the end of the vector + // don't iterate past the end of the vector std::size_t remaining_elements = std::distance(iter, data.end()); iter += std::min(L, remaining_elements); } From 8f05687924ad0d3bced29fac3326e484d0c0118f Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 13 Nov 2023 15:50:35 -0800 Subject: [PATCH 255/318] convert from OpenAtmos format to SI units --- include/micm/configure/solver_config.hpp | 15 +++++++++++++++ .../configure/process/test_arrhenius_config.cpp | 9 ++++++--- .../configure/process/test_branched_config.cpp | 11 +++++++---- .../test_ternary_chemical_activation_config.cpp | 9 ++++++--- test/unit/configure/process/test_troe_config.cpp | 11 +++++++---- .../configure/process/test_tunneling_config.cpp | 7 +++++-- test/unit/configure/test_solver_config.cpp | 7 +++++++ 7 files changed, 53 insertions(+), 16 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index a8ff6943e..aee1378a6 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -42,6 +42,8 @@ namespace micm MutuallyExclusiveOption }; + constexpr double MolesM3ToMoleculesCm3 = 1.0e-6 * 6.02214076e23; + inline std::string configParseStatusToString(const ConfigParseStatus& status) { switch (status) @@ -522,6 +524,7 @@ namespace micm { parameters.A_ = object["A"].get(); } + parameters.A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); if (object.contains("B")) { parameters.B_ = object["B"].get(); @@ -588,6 +591,8 @@ namespace micm { parameters.k0_A_ = object["k0_A"].get(); } + // Account for the conversion of reactant concentrations (including M) to molecules cm-3 + parameters.k0_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()); if (object.contains("k0_B")) { parameters.k0_B_ = object["k0_B"].get(); @@ -600,6 +605,8 @@ namespace micm { parameters.kinf_A_ = object["kinf_A"].get(); } + // Account for terms in denominator and exponent that include [M] but not other reactants + parameters.kinf_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); if (object.contains("kinf_B")) { parameters.kinf_B_ = object["kinf_B"].get(); @@ -656,6 +663,8 @@ namespace micm { parameters.k0_A_ = object["k0_A"].get(); } + // Account for the conversion of reactant concentrations (including M) to molecules cm-3 + parameters.k0_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); if (object.contains("k0_B")) { parameters.k0_B_ = object["k0_B"].get(); @@ -668,6 +677,8 @@ namespace micm { parameters.kinf_A_ = object["kinf_A"].get(); } + // Account for terms in denominator and exponent that include [M] but not other reactants + parameters.kinf_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-2); if (object.contains("kinf_B")) { parameters.kinf_B_ = object["kinf_B"].get(); @@ -732,6 +743,8 @@ namespace micm BranchedRateConstantParameters parameters; parameters.X_ = object[X].get(); + // Account for the conversion of reactant concentrations to molecules cm-3 + parameters.X_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); parameters.Y_ = object[Y].get(); parameters.a0_ = object[A0].get(); parameters.n_ = object[N].get(); @@ -780,6 +793,8 @@ namespace micm { parameters.A_ = object["A"].get(); } + // Account for the conversion of reactant concentrations to molecules cm-3 + parameters.A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); if (object.contains("B")) { parameters.B_ = object["B"].get(); diff --git a/test/unit/configure/process/test_arrhenius_config.cpp b/test/unit/configure/process/test_arrhenius_config.cpp index 65837f22e..2840fabf0 100644 --- a/test/unit/configure/process/test_arrhenius_config.cpp +++ b/test/unit/configure/process/test_arrhenius_config.cpp @@ -26,6 +26,9 @@ TEST(ArrheniusConfig, ParseConfig) auto& process_vector = solver_params.processes_; + // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 + const double conv = 1.0e-6 * 6.02214076e23; + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); @@ -40,7 +43,7 @@ TEST(ArrheniusConfig, ParseConfig) micm::ArrheniusRateConstant* ternary_rate_constant = dynamic_cast(process_vector[0].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.A_, 1.0); + EXPECT_EQ(params.A_, 1.0 * conv * conv); EXPECT_EQ(params.B_, 0.0); EXPECT_EQ(params.C_, 0.0); EXPECT_EQ(params.D_, 300); @@ -60,7 +63,7 @@ TEST(ArrheniusConfig, ParseConfig) micm::ArrheniusRateConstant* ternary_rate_constant = dynamic_cast(process_vector[1].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.A_, 32.1 * conv); EXPECT_EQ(params.B_, -2.3); EXPECT_EQ(params.C_, 102.3); EXPECT_EQ(params.D_, 63.4); @@ -80,7 +83,7 @@ TEST(ArrheniusConfig, ParseConfig) micm::ArrheniusRateConstant* ternary_rate_constant = dynamic_cast(process_vector[2].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.A_, 32.1 * conv); EXPECT_EQ(params.B_, -2.3); EXPECT_EQ(params.C_, -1 * 2e23 / BOLTZMANN_CONSTANT); EXPECT_EQ(params.D_, 63.4); diff --git a/test/unit/configure/process/test_branched_config.cpp b/test/unit/configure/process/test_branched_config.cpp index 0e6618167..bae9cb5ca 100644 --- a/test/unit/configure/process/test_branched_config.cpp +++ b/test/unit/configure/process/test_branched_config.cpp @@ -27,6 +27,9 @@ TEST(BranchedConfig, ParseConfig) auto& process_vector = solver_params.processes_; EXPECT_EQ(process_vector.size(), 4); + // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 + const double conv = 1.0e-6 * 6.02214076e23; + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); @@ -41,7 +44,7 @@ TEST(BranchedConfig, ParseConfig) micm::BranchedRateConstant* branched_rate_constant = dynamic_cast(process_vector[0].rate_constant_.get()); auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 12.3); + EXPECT_EQ(params.X_, 12.3 * std::pow(conv, 2)); EXPECT_EQ(params.Y_, 42.3); EXPECT_EQ(params.a0_, 1.0e-5); EXPECT_EQ(params.n_, 3); @@ -58,7 +61,7 @@ TEST(BranchedConfig, ParseConfig) micm::BranchedRateConstant* branched_rate_constant = dynamic_cast(process_vector[1].rate_constant_.get()); auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 12.3); + EXPECT_EQ(params.X_, 12.3 * std::pow(conv, 2)); EXPECT_EQ(params.Y_, 42.3); EXPECT_EQ(params.a0_, 1.0e-5); EXPECT_EQ(params.n_, 3); @@ -76,7 +79,7 @@ TEST(BranchedConfig, ParseConfig) micm::BranchedRateConstant* branched_rate_constant = dynamic_cast(process_vector[2].rate_constant_.get()); auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 0.32); + EXPECT_EQ(params.X_, 0.32 * conv); EXPECT_EQ(params.Y_, 2.3e8); EXPECT_EQ(params.a0_, 0.423); EXPECT_EQ(params.n_, 6); @@ -94,7 +97,7 @@ TEST(BranchedConfig, ParseConfig) micm::BranchedRateConstant* branched_rate_constant = dynamic_cast(process_vector[3].rate_constant_.get()); auto& params = branched_rate_constant->parameters_; - EXPECT_EQ(params.X_, 0.32); + EXPECT_EQ(params.X_, 0.32 * conv); EXPECT_EQ(params.Y_, 2.3e8); EXPECT_EQ(params.a0_, 0.423); EXPECT_EQ(params.n_, 6); diff --git a/test/unit/configure/process/test_ternary_chemical_activation_config.cpp b/test/unit/configure/process/test_ternary_chemical_activation_config.cpp index d4a416a16..d51a399ce 100644 --- a/test/unit/configure/process/test_ternary_chemical_activation_config.cpp +++ b/test/unit/configure/process/test_ternary_chemical_activation_config.cpp @@ -25,6 +25,9 @@ TEST(TernaryChemicalActivationConfig, ParseConfig) auto& process_vector = solver_params.processes_; + // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 + const double conv = 1.0e-6 * 6.02214076e23; + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); @@ -39,10 +42,10 @@ TEST(TernaryChemicalActivationConfig, ParseConfig) micm::TernaryChemicalActivationRateConstant* ternary_rate_constant = dynamic_cast(process_vector[0].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 1.0); + EXPECT_EQ(params.k0_A_, 1.0 * conv * conv); EXPECT_EQ(params.k0_B_, 0.0); EXPECT_EQ(params.k0_C_, 0.0); - EXPECT_EQ(params.kinf_A_, 1.0); + EXPECT_EQ(params.kinf_A_, 1.0 * conv); EXPECT_EQ(params.kinf_B_, 0.0); EXPECT_EQ(params.kinf_C_, 0.0); EXPECT_EQ(params.Fc_, 0.6); @@ -62,7 +65,7 @@ TEST(TernaryChemicalActivationConfig, ParseConfig) micm::TernaryChemicalActivationRateConstant* ternary_rate_constant = dynamic_cast(process_vector[1].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 32.1); + EXPECT_EQ(params.k0_A_, 32.1 * conv); EXPECT_EQ(params.k0_B_, -2.3); EXPECT_EQ(params.k0_C_, 102.3); EXPECT_EQ(params.kinf_A_, 63.4); diff --git a/test/unit/configure/process/test_troe_config.cpp b/test/unit/configure/process/test_troe_config.cpp index f7631359e..58a80d835 100644 --- a/test/unit/configure/process/test_troe_config.cpp +++ b/test/unit/configure/process/test_troe_config.cpp @@ -24,6 +24,9 @@ TEST(TroeConfig, ParseConfig) auto& process_vector = solver_params.processes_; + // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 + const double conv = 1.0e-6 * 6.02214076e23; + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); @@ -38,10 +41,10 @@ TEST(TroeConfig, ParseConfig) micm::TroeRateConstant* ternary_rate_constant = dynamic_cast(process_vector[0].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 1.0); + EXPECT_EQ(params.k0_A_, 1.0 * std::pow(conv, 3)); EXPECT_EQ(params.k0_B_, 0.0); EXPECT_EQ(params.k0_C_, 0.0); - EXPECT_EQ(params.kinf_A_, 1.0); + EXPECT_EQ(params.kinf_A_, 1.0 * std::pow(conv, 2)); EXPECT_EQ(params.kinf_B_, 0.0); EXPECT_EQ(params.kinf_C_, 0.0); EXPECT_EQ(params.Fc_, 0.6); @@ -61,10 +64,10 @@ TEST(TroeConfig, ParseConfig) micm::TroeRateConstant* ternary_rate_constant = dynamic_cast(process_vector[1].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 32.1); + EXPECT_EQ(params.k0_A_, 32.1 * std::pow(conv, 2)); EXPECT_EQ(params.k0_B_, -2.3); EXPECT_EQ(params.k0_C_, 102.3); - EXPECT_EQ(params.kinf_A_, 63.4); + EXPECT_EQ(params.kinf_A_, 63.4 * conv); EXPECT_EQ(params.kinf_B_, -1.3); EXPECT_EQ(params.kinf_C_, 908.5); EXPECT_EQ(params.Fc_, 1.3); diff --git a/test/unit/configure/process/test_tunneling_config.cpp b/test/unit/configure/process/test_tunneling_config.cpp index ca77fdb26..ee329a888 100644 --- a/test/unit/configure/process/test_tunneling_config.cpp +++ b/test/unit/configure/process/test_tunneling_config.cpp @@ -24,6 +24,9 @@ TEST(TunnelingConfig, ParseConfig) auto& process_vector = solver_params.processes_; + // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 + const double conv = 1.0e-6 * 6.02214076e23; + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); @@ -38,7 +41,7 @@ TEST(TunnelingConfig, ParseConfig) micm::TunnelingRateConstant* tunneling_rate_constant = dynamic_cast(process_vector[0].rate_constant_.get()); auto& params = tunneling_rate_constant->parameters_; - EXPECT_EQ(params.A_, 1.0); + EXPECT_EQ(params.A_, 1.0 * conv * conv); EXPECT_EQ(params.B_, 0.0); EXPECT_EQ(params.C_, 0.0); } @@ -56,7 +59,7 @@ TEST(TunnelingConfig, ParseConfig) micm::TunnelingRateConstant* tunneling_rate_constant = dynamic_cast(process_vector[1].rate_constant_.get()); auto& params = tunneling_rate_constant->parameters_; - EXPECT_EQ(params.A_, 32.1); + EXPECT_EQ(params.A_, 32.1 * conv); EXPECT_EQ(params.B_, -2.3); EXPECT_EQ(params.C_, 102.3); } diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index cf229a0b7..1f673acbe 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -110,6 +110,13 @@ TEST(SolverConfig, ReadAndParseProcessObjects) double D_param[] = { 300.0, 300.0, 300.0, 300.0 }; double E_param[] = { 0.0, 0.0, 0.0, 0.0 }; + // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 + const double conv = 1.0e-6 * 6.02214076e23; + A_param[0] *= conv; // 2 reactants + A_param[1] *= conv; // 2 reactants + A_param[2] *= conv; // 2 reactants + A_param[3] *= conv * conv; // 3 reactants + for (short i = 3; i < 7; i++) { arrhenius_rate_const = dynamic_cast(process_vector[i].rate_constant_.get()); From bc9993e682bf4075c42230090043912a5a84c520 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Mon, 13 Nov 2023 16:27:20 -0800 Subject: [PATCH 256/318] modify tests for some round-off differences with NVHPC compiler --- test/unit/configure/process/test_troe_config.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/configure/process/test_troe_config.cpp b/test/unit/configure/process/test_troe_config.cpp index 58a80d835..b643a7ad7 100644 --- a/test/unit/configure/process/test_troe_config.cpp +++ b/test/unit/configure/process/test_troe_config.cpp @@ -41,7 +41,8 @@ TEST(TroeConfig, ParseConfig) micm::TroeRateConstant* ternary_rate_constant = dynamic_cast(process_vector[0].rate_constant_.get()); auto& params = ternary_rate_constant->parameters_; - EXPECT_EQ(params.k0_A_, 1.0 * std::pow(conv, 3)); + // NVHPC compiler has some round-off error + EXPECT_NEAR(params.k0_A_, 1.0 * std::pow(conv, 3), std::pow(conv, 3) * 1e-12); EXPECT_EQ(params.k0_B_, 0.0); EXPECT_EQ(params.k0_C_, 0.0); EXPECT_EQ(params.kinf_A_, 1.0 * std::pow(conv, 2)); From 27508239d33d18385f648147037d5f462f640499 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Tue, 14 Nov 2023 21:28:28 +0000 Subject: [PATCH 257/318] Changed reactions.json units to non-SI. --- examples/Chapman/reactions.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/Chapman/reactions.json b/examples/Chapman/reactions.json index 85129afe1..f37fd2030 100644 --- a/examples/Chapman/reactions.json +++ b/examples/Chapman/reactions.json @@ -18,7 +18,7 @@ }, { "type": "ARRHENIUS", - "A": 48.28552461368, + "A": 8.018e-17, "reactants": { "O": {}, "O2": {} @@ -41,7 +41,7 @@ }, { "type": "ARRHENIUS", - "A": 949.089383776, + "A": 1.576e-15, "reactants": { "O": {}, "O3": {} @@ -66,7 +66,7 @@ }, { "type": "ARRHENIUS", - "A": 42817420.803600006, + "A": 7.11e-11, "reactants": { "O1D": {}, "M": {} @@ -79,7 +79,7 @@ }, { "type": "ARRHENIUS", - "A": 72265689.12, + "A": 1.2e-10, "reactants": { "O1D": {}, "O3": {} @@ -94,4 +94,4 @@ ] } ] -} \ No newline at end of file +} From 6beb6128926be4d522e35f4d1032cceea4f7920b Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 14 Nov 2023 16:07:20 -0600 Subject: [PATCH 258/318] started vectorized matrix tutorial --- docs/source/user_guide/index.rst | 1 + .../user_guide/vectorized_matrix_solver.rst | 87 +++++++++++++ test/tutorial/CMakeLists.txt | 1 + .../test_vectorized_matrix_solver.cpp | 116 ++++++++++++++++++ 4 files changed, 205 insertions(+) create mode 100644 docs/source/user_guide/vectorized_matrix_solver.rst create mode 100644 test/tutorial/test_vectorized_matrix_solver.cpp diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 064955a8b..2670c6aa2 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -48,3 +48,4 @@ If you would like to include the json examples, you must configure micm to build but_how_fast_is_it openmp jit + vectorized_matrix_solver diff --git a/docs/source/user_guide/vectorized_matrix_solver.rst b/docs/source/user_guide/vectorized_matrix_solver.rst new file mode 100644 index 000000000..4c849d489 --- /dev/null +++ b/docs/source/user_guide/vectorized_matrix_solver.rst @@ -0,0 +1,87 @@ +.. _Vectorized matrix solver: + +Vectorized matrix solver +######################## + +Many of the tutorials have defined a chemical system without talking about their location. Each of these is essentially +defining a `box model `_. We often want to model chemistry +in more than one location; multiple boxes. In weather and climate models, the domain is broken up into grids 2d or 3d grids. +Each location may be referred to as a grid cell or column. With ``micm``, we can solve multiple grid cells simultaneously in +several ways. + + +The tutorials listed below solve chemistry in more than one grid cell. + +* :ref:`Multiple grid cells`, this tutorial does so with a different data structure +* :ref:`OpenMP`, this tutorial uses a single thread per grid cell + + +In :ref:`Multiple grid cells` we assumed we were solving for the concentrations of an :math:`N\times D` matrix, :math:`N` species and +:math:`D` grid cells. However, we used the default matrix ordering in that tutorial. This is fine for simulations that aren't +too large, but computationally may be inferior. This is because the same species appears in each grid cell, :math:`N` elements +away from each other in memory. When solving the chemical equations, the same mathematical operations have to be applied to each +species. We can organize the memory of the matrix so that the same species across all :math:`D` grid cells are directly next +to each other in memory. In this way, we help to encourage the compiler to vectorize the code and take advantage of +`SIMD instructions `_. + +Organizing the data this way may be familiar to GPU programmers as +`coalesced memory access `_. + +To understand this, we'll look at how the memeory is organized when solving the robertson equations with 3 grid cells using +the various matrix types and ordering in ``micm``. + +.. math:: + + A &\longrightarrow B, &k_{1, \mathrm{user\ defined}} \\ + 2B &\longrightarrow B + C, &k_{2, \mathrm{user\ defined}} \\ + B + C &\longrightarrow A + C, \qquad &k_{3, \mathrm{user\ defined}} \\ + + +This entire example is contained below. Copy and paste to follow along. + +.. tab-set:: + + .. tab-item:: Build the Mechanism with the API + + .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + + +Matrix types in micm +-------------------- + +``micm`` has its own matrix types, each providing a separate data layout. The data layouts are meant to provide efficient access +such that cache misses are minimized. This is done by placing all of the data into a contiguous vector, and optionally imposing +some sort of ordering on the data, like grouping species across gridcells together in memory. + + +The dimensions of all ``micm`` +matrices are :math:`\mathrm{number\ of\ grid\ cells} \times \mathrm{number\ of\ species}` + +* :cpp:class:`micm::Matrix`, a dense matrix, what you probably think of when you think of a matrix, with the caveat that all rows and columns live in one long array + +* :cpp:class:`micm::VectorMatrix`, the matrix type of interest here, where the data is ordered such that species across grid cells lie beside each other in memory + +* :cpp:class:`micm::SparseMatrix`, a `sparse matrix `_ data structure + + * The sparse matrix also allows you to specify wether the data is ordered as a regular dense matrix, with zeros removed, or with a vectorized ordering, the topic of this tutorial. + + * :cpp:class:`micm::SparseMatrixStandardOrdering` + + * :cpp:class:`micm::SparseMatrixVectorOrdering` + +Regular, dense matrix ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Up until now, we've mostly created our :cpp:class:`micm::RosenbrockSolver` solvers like this: + +.. code-block:: cpp + + RosenbrockSolver<> solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) }; + +The empty angle brackets ``<>`` tell the compiler to use the default template paramters. The first two are most important and +denote the ``MatrixPolicy`` and ``SparseMatrixPolicy`` to be used when we solve. By default, these are :cpp:class:`micm::Matrix` +and :cpp:class:`micm::SparseMatrix`. The most visible data affected by these paratemeters is the state's +:cpp:member:`micm::State::variables_` parameter, representing the concnetration of each of the species. \ No newline at end of file diff --git a/test/tutorial/CMakeLists.txt b/test/tutorial/CMakeLists.txt index 9c3f7a0bf..825f9d15b 100644 --- a/test/tutorial/CMakeLists.txt +++ b/test/tutorial/CMakeLists.txt @@ -13,6 +13,7 @@ create_standard_test(NAME multiple_grid_cells SOURCES test_multiple_grid_cells.c create_standard_test(NAME rate_constants_no_user_defined_example_by_hand SOURCES test_rate_constants_no_user_defined_by_hand.cpp) create_standard_test(NAME rate_constants_user_defined_example_by_hand SOURCES test_rate_constants_user_defined_by_hand.cpp) create_standard_test(NAME solver_configuration SOURCES test_solver_configuration.cpp) +create_standard_test(NAME vectorized_matrix_solver SOURCES test_vectorized_matrix_solver.cpp) if(ENABLE_JSON) create_standard_test(NAME rate_constants_no_user_defined_example_with_config SOURCES test_rate_constants_no_user_defined_with_config.cpp) create_standard_test(NAME rate_constants_user_defined_example_with_config SOURCES test_rate_constants_user_defined_with_config.cpp) diff --git a/test/tutorial/test_vectorized_matrix_solver.cpp b/test/tutorial/test_vectorized_matrix_solver.cpp new file mode 100644 index 000000000..3fa387a0d --- /dev/null +++ b/test/tutorial/test_vectorized_matrix_solver.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Use our namespace so that this example is easier to read +using namespace micm; + +template +using Group3Matrix = micm::VectorMatrix; + +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; + +int main() +{ + auto a = Species("A"); + auto b = Species("B"); + auto c = Species("C"); + + Phase gas_phase{ std::vector{ a, b, c } }; + + Process r1 = Process::create() + .reactants({ a }) + .products({ yields(b, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r1" })) + .phase(gas_phase); + + Process r2 = Process::create() + .reactants({ b, b }) + .products({ yields(b, 1), yields(c, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r2" })) + .phase(gas_phase); + + Process r3 = Process::create() + .reactants({ b, c }) + .products({ yields(a, 1), yields(c, 1) }) + .rate_constant(UserDefinedRateConstant({ .label_ = "r3" })) + .phase(gas_phase); + + auto params = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false); + + RosenbrockSolver<> solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + params }; + + auto state = solver.GetState(); + + // mol m-3 + state.SetConcentration(a, std::vector(3, 1)); + state.SetConcentration(b, std::vector(3, 2)); + state.SetConcentration(c, std::vector(3, 3)); + + for (auto& elem : state.variables_.AsVector()) + { + std::cout << elem << std::endl; + } + + std::cout << std::endl; + + RosenbrockSolver vectorized_solver{ + System(SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, params + }; + + auto vectorized_state = solver.GetState(); + + // mol m-3 + vectorized_state.SetConcentration(a, std::vector(3, 1)); + vectorized_state.SetConcentration(b, std::vector(3, 2)); + vectorized_state.SetConcentration(c, std::vector(3, 3)); + + for (auto& elem : vectorized_state.variables_.AsVector()) + { + std::cout << elem << std::endl; + } + + // double k1 = 0.04; + // double k2 = 3e7; + // double k3 = 1e4; + // state.SetCustomRateParameter("r1", std::vector(3, k1)); + // state.SetCustomRateParameter("r2", std::vector(3, k2)); + // state.SetCustomRateParameter("r3", std::vector(3, k3)); + + // double temperature = 272.5; // [K] + // double pressure = 101253.3; // [Pa] + // double air_density = 1e6; // [mol m-3] + + // for (size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) + // { + // state.conditions_[cell].temperature_ = temperature; + // state.conditions_[cell].pressure_ = pressure; + // state.conditions_[cell].air_density_ = air_density; + // } + + // // choose a timestep and print the initial state + // double time_step = 200; // s + + // auto result = solver.Solve(time_step, state); + + // for (int i = 0; i < 10; ++i) + // { + // double elapsed_solve_time = 0; + // while (elapsed_solve_time < time_step) + // { + // auto result = solver.Solve(time_step - elapsed_solve_time, state); + // elapsed_solve_time = result.final_time_; + // state.variables_[0] = result.result_.AsVector(); + // } + + // } +} \ No newline at end of file From 312a4c1ebab465de365e816730876e2619660177 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 14 Nov 2023 16:36:05 -0600 Subject: [PATCH 259/318] maybe finished? --- .../user_guide/vectorized_matrix_solver.rst | 60 +++++++++++++++++-- .../test_vectorized_matrix_solver.cpp | 18 +++--- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/docs/source/user_guide/vectorized_matrix_solver.rst b/docs/source/user_guide/vectorized_matrix_solver.rst index 4c849d489..b2d44c517 100644 --- a/docs/source/user_guide/vectorized_matrix_solver.rst +++ b/docs/source/user_guide/vectorized_matrix_solver.rst @@ -75,13 +75,61 @@ Regular, dense matrix ordering Up until now, we've mostly created our :cpp:class:`micm::RosenbrockSolver` solvers like this: -.. code-block:: cpp - - RosenbrockSolver<> solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false) }; +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 46-50 The empty angle brackets ``<>`` tell the compiler to use the default template paramters. The first two are most important and denote the ``MatrixPolicy`` and ``SparseMatrixPolicy`` to be used when we solve. By default, these are :cpp:class:`micm::Matrix` and :cpp:class:`micm::SparseMatrix`. The most visible data affected by these paratemeters is the state's -:cpp:member:`micm::State::variables_` parameter, representing the concnetration of each of the species. \ No newline at end of file +:cpp:member:`micm::State::variables_` parameter, representing the concnetration of each of the species. + +After we created that first ``solver``, if we get a state and set the concentration of the species and then print them, +they are printed in order of grid cell first and then species. Here, when setting the species, read the double as ``grid cell.species``. + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 52-61 + +.. code-block:: bash + + 1.1 + 1.2 + 1.3 + 2.1 + 2.2 + 2.3 + 3.1 + 3.2 + 3.3 + +But, if we create a vectorized solver, set the same concentrations and print out the state variables, we see that it is organized +first by species and then by grid cell. Note that we needed to set some partial template speciailizations at the top of the file +to create thise. + +At the top of the file: + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 14-18 + +and the vectorized solver creation + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 65-78 + +.. code-block:: bash + + 1.1 + 2.1 + 3.1 + 1.2 + 2.2 + 3.2 + 1.3 + 2.3 + 3.3 + +And that's all you have to do to orgnaize the data by species first. By specifying the template parameter on the +solver, each operation will use the same ordering for all of the data sets needed to solver the chemical system. \ No newline at end of file diff --git a/test/tutorial/test_vectorized_matrix_solver.cpp b/test/tutorial/test_vectorized_matrix_solver.cpp index 3fa387a0d..dd2e6a1c2 100644 --- a/test/tutorial/test_vectorized_matrix_solver.cpp +++ b/test/tutorial/test_vectorized_matrix_solver.cpp @@ -51,10 +51,9 @@ int main() auto state = solver.GetState(); - // mol m-3 - state.SetConcentration(a, std::vector(3, 1)); - state.SetConcentration(b, std::vector(3, 2)); - state.SetConcentration(c, std::vector(3, 3)); + state.SetConcentration(a, {1.1, 2.1, 3.1}); + state.SetConcentration(b, {1.2, 2.2, 3.2}); + state.SetConcentration(c, {1.3, 2.3, 3.3}); for (auto& elem : state.variables_.AsVector()) { @@ -67,18 +66,19 @@ int main() System(SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, params }; - auto vectorized_state = solver.GetState(); + auto vectorized_state = vectorized_solver.GetState(); - // mol m-3 - vectorized_state.SetConcentration(a, std::vector(3, 1)); - vectorized_state.SetConcentration(b, std::vector(3, 2)); - vectorized_state.SetConcentration(c, std::vector(3, 3)); + vectorized_state.SetConcentration(a, {1.1, 2.1, 3.1}); + vectorized_state.SetConcentration(b, {1.2, 2.2, 3.2}); + vectorized_state.SetConcentration(c, {1.3, 2.3, 3.3}); for (auto& elem : vectorized_state.variables_.AsVector()) { std::cout << elem << std::endl; } + std::cout << std::endl; + // double k1 = 0.04; // double k2 = 3e7; // double k3 = 1e4; From 8565b41a4f7ccb32af58ac56e62745d4fc2494e7 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 14 Nov 2023 16:55:10 -0600 Subject: [PATCH 260/318] pretty print the result and run the solver --- .../user_guide/vectorized_matrix_solver.rst | 34 +++++- .../test_vectorized_matrix_solver.cpp | 111 ++++++++++-------- 2 files changed, 95 insertions(+), 50 deletions(-) diff --git a/docs/source/user_guide/vectorized_matrix_solver.rst b/docs/source/user_guide/vectorized_matrix_solver.rst index b2d44c517..76f62f277 100644 --- a/docs/source/user_guide/vectorized_matrix_solver.rst +++ b/docs/source/user_guide/vectorized_matrix_solver.rst @@ -77,7 +77,7 @@ Up until now, we've mostly created our :cpp:class:`micm::RosenbrockSolver` solve .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp :language: cpp - :lines: 46-50 + :lines: 86 The empty angle brackets ``<>`` tell the compiler to use the default template paramters. The first two are most important and denote the ``MatrixPolicy`` and ``SparseMatrixPolicy`` to be used when we solve. By default, these are :cpp:class:`micm::Matrix` @@ -89,7 +89,7 @@ they are printed in order of grid cell first and then species. Here, when settin .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp :language: cpp - :lines: 52-61 + :lines: 88-97 .. code-block:: bash @@ -117,7 +117,7 @@ and the vectorized solver creation .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp :language: cpp - :lines: 65-78 + :lines: 101-112 .. code-block:: bash @@ -132,4 +132,30 @@ and the vectorized solver creation 3.3 And that's all you have to do to orgnaize the data by species first. By specifying the template parameter on the -solver, each operation will use the same ordering for all of the data sets needed to solver the chemical system. \ No newline at end of file +solver, each operation will use the same ordering for all of the data sets needed to solver the chemical system. + +You can use the vectorized just as you would the regular solver, and in fact the same output is produced. + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 116-134 + +.. code-block:: bash + + Cell 0 + Species Regular Vectorized + A 1.02174 1.02174 + B 1.58226e-06 1.58226e-06 + C 2.57826 2.57826 + + Cell 1 + Species Regular Vectorized + A 2.01153 2.01153 + B 1.75155e-06 1.75155e-06 + C 4.58846 4.58846 + + Cell 2 + Species Regular Vectorized + A 3.0096 3.0096 + B 1.82514e-06 1.82514e-06 + C 6.5904 6.5904 \ No newline at end of file diff --git a/test/tutorial/test_vectorized_matrix_solver.cpp b/test/tutorial/test_vectorized_matrix_solver.cpp index dd2e6a1c2..6f6c21143 100644 --- a/test/tutorial/test_vectorized_matrix_solver.cpp +++ b/test/tutorial/test_vectorized_matrix_solver.cpp @@ -17,6 +17,42 @@ using Group3Matrix = micm::VectorMatrix; template using Group3SparseVectorMatrix = micm::SparseMatrix>; +void solve(auto& solver, auto& state) +{ + double k1 = 0.04; + double k2 = 3e7; + double k3 = 1e4; + state.SetCustomRateParameter("r1", std::vector(3, k1)); + state.SetCustomRateParameter("r2", std::vector(3, k2)); + state.SetCustomRateParameter("r3", std::vector(3, k3)); + + double temperature = 272.5; // [K] + double pressure = 101253.3; // [Pa] + double air_density = 1e6; // [mol m-3] + + for (size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) + { + state.conditions_[cell].temperature_ = temperature; + state.conditions_[cell].pressure_ = pressure; + state.conditions_[cell].air_density_ = air_density; + } + + double time_step = 100; // s + + auto result = solver.Solve(time_step, state); + + for (int i = 0; i < 10; ++i) + { + double elapsed_solve_time = 0; + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + } +} + int main() { auto a = Species("A"); @@ -44,16 +80,16 @@ int main() .phase(gas_phase); auto params = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(3, false); + auto system = System(SystemParameters{ .gas_phase_ = gas_phase }); + auto reactions = std::vector{ r1, r2, r3 }; - RosenbrockSolver<> solver{ System(SystemParameters{ .gas_phase_ = gas_phase }), - std::vector{ r1, r2, r3 }, - params }; + RosenbrockSolver<> solver{ system, reactions, params }; auto state = solver.GetState(); - state.SetConcentration(a, {1.1, 2.1, 3.1}); - state.SetConcentration(b, {1.2, 2.2, 3.2}); - state.SetConcentration(c, {1.3, 2.3, 3.3}); + state.SetConcentration(a, { 1.1, 2.1, 3.1 }); + state.SetConcentration(b, { 1.2, 2.2, 3.2 }); + state.SetConcentration(c, { 1.3, 2.3, 3.3 }); for (auto& elem : state.variables_.AsVector()) { @@ -62,15 +98,13 @@ int main() std::cout << std::endl; - RosenbrockSolver vectorized_solver{ - System(SystemParameters{ .gas_phase_ = gas_phase }), std::vector{ r1, r2, r3 }, params - }; + RosenbrockSolver vectorized_solver{ system, reactions, params }; auto vectorized_state = vectorized_solver.GetState(); - vectorized_state.SetConcentration(a, {1.1, 2.1, 3.1}); - vectorized_state.SetConcentration(b, {1.2, 2.2, 3.2}); - vectorized_state.SetConcentration(c, {1.3, 2.3, 3.3}); + vectorized_state.SetConcentration(a, { 1.1, 2.1, 3.1 }); + vectorized_state.SetConcentration(b, { 1.2, 2.2, 3.2 }); + vectorized_state.SetConcentration(c, { 1.3, 2.3, 3.3 }); for (auto& elem : vectorized_state.variables_.AsVector()) { @@ -79,38 +113,23 @@ int main() std::cout << std::endl; - // double k1 = 0.04; - // double k2 = 3e7; - // double k3 = 1e4; - // state.SetCustomRateParameter("r1", std::vector(3, k1)); - // state.SetCustomRateParameter("r2", std::vector(3, k2)); - // state.SetCustomRateParameter("r3", std::vector(3, k3)); - - // double temperature = 272.5; // [K] - // double pressure = 101253.3; // [Pa] - // double air_density = 1e6; // [mol m-3] - - // for (size_t cell = 0; cell < solver.parameters_.number_of_grid_cells_; ++cell) - // { - // state.conditions_[cell].temperature_ = temperature; - // state.conditions_[cell].pressure_ = pressure; - // state.conditions_[cell].air_density_ = air_density; - // } - - // // choose a timestep and print the initial state - // double time_step = 200; // s - - // auto result = solver.Solve(time_step, state); - - // for (int i = 0; i < 10; ++i) - // { - // double elapsed_solve_time = 0; - // while (elapsed_solve_time < time_step) - // { - // auto result = solver.Solve(time_step - elapsed_solve_time, state); - // elapsed_solve_time = result.final_time_; - // state.variables_[0] = result.result_.AsVector(); - // } - - // } + solve(solver, state); + solve(vectorized_solver, vectorized_state); + + for (size_t cell = 0; cell < params.number_of_grid_cells_; ++cell) + { + std::cout << "Cell " << cell << std::endl; + std::cout << std::setw(10) << "Species" << std::setw(20) << "Regular" << std::setw(20) << "Vectorized" << std::endl; + + for (auto& species : system.UniqueNames()) + { + auto regular_idx = state.variable_map_[species]; + auto vectorized_idx = vectorized_state.variable_map_[species]; + + std::cout << std::setw(10) << species << std::setw(20) << state.variables_[cell][regular_idx] << std::setw(20) + << vectorized_state.variables_[cell][vectorized_idx] << std::endl; + } + + std::cout << std::endl; + } } \ No newline at end of file From fdce0778e2e5fc3e2c01fc704ff0c56a0b55ebb4 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Tue, 14 Nov 2023 16:56:42 -0600 Subject: [PATCH 261/318] correcting result setting --- test/tutorial/test_rate_constants_user_defined_by_hand.cpp | 2 +- test/tutorial/test_rate_constants_user_defined_with_config.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index ec06f6638..370a5acfd 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -191,7 +191,7 @@ int main(const int argc, const char* argv[]) { auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; - state.variables_[0] = result.result_.AsVector(); + state.variables_ = result.result_; } print_state(time_step * (i + 1), state); diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index fb013fa5d..c9fd6bb53 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -112,7 +112,7 @@ int main(const int argc, const char* argv[]) { auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; - state.variables_[0] = result.result_.AsVector(); + state.variables_ = result.result_; } print_state(time_step * (i + 1), state); From 81fee90c072b6ccec8e1b83d364ba783a58ba8f7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 14 Nov 2023 15:21:34 -0800 Subject: [PATCH 262/318] Auto-format code changes (#360) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/configure/solver_config.hpp | 12 ++++++------ test/unit/configure/process/test_troe_config.cpp | 2 +- .../unit/configure/process/test_tunneling_config.cpp | 2 +- test/unit/configure/test_solver_config.cpp | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index aee1378a6..a23945926 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -524,7 +524,7 @@ namespace micm { parameters.A_ = object["A"].get(); } - parameters.A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); + parameters.A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size() - 1); if (object.contains("B")) { parameters.B_ = object["B"].get(); @@ -606,7 +606,7 @@ namespace micm parameters.kinf_A_ = object["kinf_A"].get(); } // Account for terms in denominator and exponent that include [M] but not other reactants - parameters.kinf_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); + parameters.kinf_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size() - 1); if (object.contains("kinf_B")) { parameters.kinf_B_ = object["kinf_B"].get(); @@ -664,7 +664,7 @@ namespace micm parameters.k0_A_ = object["k0_A"].get(); } // Account for the conversion of reactant concentrations (including M) to molecules cm-3 - parameters.k0_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); + parameters.k0_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size() - 1); if (object.contains("k0_B")) { parameters.k0_B_ = object["k0_B"].get(); @@ -678,7 +678,7 @@ namespace micm parameters.kinf_A_ = object["kinf_A"].get(); } // Account for terms in denominator and exponent that include [M] but not other reactants - parameters.kinf_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-2); + parameters.kinf_A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size() - 2); if (object.contains("kinf_B")) { parameters.kinf_B_ = object["kinf_B"].get(); @@ -744,7 +744,7 @@ namespace micm BranchedRateConstantParameters parameters; parameters.X_ = object[X].get(); // Account for the conversion of reactant concentrations to molecules cm-3 - parameters.X_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); + parameters.X_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size() - 1); parameters.Y_ = object[Y].get(); parameters.a0_ = object[A0].get(); parameters.n_ = object[N].get(); @@ -794,7 +794,7 @@ namespace micm parameters.A_ = object["A"].get(); } // Account for the conversion of reactant concentrations to molecules cm-3 - parameters.A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size()-1); + parameters.A_ *= std::pow(MolesM3ToMoleculesCm3, reactants.second.size() - 1); if (object.contains("B")) { parameters.B_ = object["B"].get(); diff --git a/test/unit/configure/process/test_troe_config.cpp b/test/unit/configure/process/test_troe_config.cpp index b643a7ad7..a6e5437c8 100644 --- a/test/unit/configure/process/test_troe_config.cpp +++ b/test/unit/configure/process/test_troe_config.cpp @@ -26,7 +26,7 @@ TEST(TroeConfig, ParseConfig) // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 const double conv = 1.0e-6 * 6.02214076e23; - + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); diff --git a/test/unit/configure/process/test_tunneling_config.cpp b/test/unit/configure/process/test_tunneling_config.cpp index ee329a888..5cb1a4c4e 100644 --- a/test/unit/configure/process/test_tunneling_config.cpp +++ b/test/unit/configure/process/test_tunneling_config.cpp @@ -26,7 +26,7 @@ TEST(TunnelingConfig, ParseConfig) // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 const double conv = 1.0e-6 * 6.02214076e23; - + // first reaction { EXPECT_EQ(process_vector[0].reactants_.size(), 3); diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 1f673acbe..1b406cf8d 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -112,10 +112,10 @@ TEST(SolverConfig, ReadAndParseProcessObjects) // Convert Arrhenius parameters from expecting molecules cm-3 to moles m-3 const double conv = 1.0e-6 * 6.02214076e23; - A_param[0] *= conv; // 2 reactants - A_param[1] *= conv; // 2 reactants - A_param[2] *= conv; // 2 reactants - A_param[3] *= conv * conv; // 3 reactants + A_param[0] *= conv; // 2 reactants + A_param[1] *= conv; // 2 reactants + A_param[2] *= conv; // 2 reactants + A_param[3] *= conv * conv; // 3 reactants for (short i = 3; i < 7; i++) { From 0bc5528222ad589487697a11605e9d7b73153842 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 15 Nov 2023 09:02:40 -0600 Subject: [PATCH 263/318] correcting other tutorial line numbers --- docs/source/user_guide/multiple_grid_cells.rst | 10 +++++----- docs/source/user_guide/openmp.rst | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index f91b9ed02..4840eca62 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -66,7 +66,7 @@ the order the species are added to the gas phase. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 73-77 + :lines: 73-75 Now we need to get a state and set the concentations of each of the species. In the @@ -76,25 +76,25 @@ to set the concentrations. Here we will set the concentations by providing the : .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 79-84 + :lines: 77-82 Then we set the reaction rates by creating a vector that is 3 elements long, one for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 86-91 + :lines: 84-89 And lastly set the temperature, pressure, and air density for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 93-102 + :lines: 91-100 Now we are ready to run the simulation. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 104-127 + :lines: 102-125 And these are the results. diff --git a/docs/source/user_guide/openmp.rst b/docs/source/user_guide/openmp.rst index 4d620d655..294d7854f 100644 --- a/docs/source/user_guide/openmp.rst +++ b/docs/source/user_guide/openmp.rst @@ -49,14 +49,14 @@ and run the rosenbrock solver. .. literalinclude:: ../../../test/tutorial/test_openmp.cpp :language: cpp - :lines: 29-69 + :lines: 26-63 The main function simply reads the configuration file, sets the number of threads, and then sets up an OpenMP blcok with three threads. The function defined above is called on each thread. .. literalinclude:: ../../../test/tutorial/test_openmp.cpp :language: cpp - :lines: 71-109 + :lines: 65-107 Running this program should give an output similar to this: From 416a518e7643878d526049ed0adeaecf6e5932f1 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 15 Nov 2023 09:07:58 -0600 Subject: [PATCH 264/318] grammar --- docs/source/user_guide/vectorized_matrix_solver.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/source/user_guide/vectorized_matrix_solver.rst b/docs/source/user_guide/vectorized_matrix_solver.rst index 76f62f277..590b43e27 100644 --- a/docs/source/user_guide/vectorized_matrix_solver.rst +++ b/docs/source/user_guide/vectorized_matrix_solver.rst @@ -103,9 +103,12 @@ they are printed in order of grid cell first and then species. Here, when settin 3.2 3.3 +Vectorized matrix ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^ + But, if we create a vectorized solver, set the same concentrations and print out the state variables, we see that it is organized first by species and then by grid cell. Note that we needed to set some partial template speciailizations at the top of the file -to create thise. +to create these. At the top of the file: @@ -113,7 +116,7 @@ At the top of the file: :language: cpp :lines: 14-18 -and the vectorized solver creation +and then we pass these templates as the template arguments to the vectorized Rosenbrock solver .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp :language: cpp @@ -132,7 +135,7 @@ and the vectorized solver creation 3.3 And that's all you have to do to orgnaize the data by species first. By specifying the template parameter on the -solver, each operation will use the same ordering for all of the data sets needed to solver the chemical system. +solver, each operation will use the same ordering for all of the data structures needed to solve the chemical system. You can use the vectorized just as you would the regular solver, and in fact the same output is produced. From c817bd4025281b73b34051cfc9bd72aaa654263e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 15 Nov 2023 08:41:05 -0700 Subject: [PATCH 265/318] Added test/integration/analytic_surface_rxn.cpp. --- test/CMakeLists.txt | 1 - .../analytic_surface_rxn.cpp} | 0 test/surface_rxn/CMakeLists.txt | 14 -------------- 3 files changed, 15 deletions(-) rename test/{surface_rxn/test_surface_rxn.cpp => integration/analytic_surface_rxn.cpp} (100%) delete mode 100644 test/surface_rxn/CMakeLists.txt diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d392bb295..e24e29012 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -8,4 +8,3 @@ if(ENABLE_JSON) add_subdirectory(examples) endif() add_subdirectory(tutorial) -add_subdirectory(surface_rxn) diff --git a/test/surface_rxn/test_surface_rxn.cpp b/test/integration/analytic_surface_rxn.cpp similarity index 100% rename from test/surface_rxn/test_surface_rxn.cpp rename to test/integration/analytic_surface_rxn.cpp diff --git a/test/surface_rxn/CMakeLists.txt b/test/surface_rxn/CMakeLists.txt deleted file mode 100644 index 35bdb28da..000000000 --- a/test/surface_rxn/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -################################################################################ -# Test utilities - -include(test_util) - -################################################################################ -# Tests - -if(ENABLE_JSON) - create_standard_test(NAME surface_rxn SOURCES test_surface_rxn.cpp) -endif() - -################################################################################ - From d2616bd02fe3b2d7456793d7322fa0e296b0ba8c Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Wed, 15 Nov 2023 08:47:29 -0700 Subject: [PATCH 266/318] Building analytic_surface_rxn as separate test for now, to be moved to analytical_policy.hpp. --- test/integration/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index df835627c..163eb902d 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -9,8 +9,9 @@ include(test_util) create_standard_test(NAME chapman_integration SOURCES chapman.cpp) create_standard_test(NAME analytical_rosenbrock_integration SOURCES analytical_rosenbrock.cpp) create_standard_test(NAME terminator_integration SOURCES terminator.cpp) +create_standard_test(NAME analytical_surface_rxn SOURCES analytic_surface_rxn.cpp) if(ENABLE_LLVM) create_standard_test(NAME analytical_jit_rosenbrock_integration SOURCES analytical_jit_rosenbrock.cpp) create_standard_test(NAME jit_terminator_integration SOURCES jit_terminator.cpp) -endif() \ No newline at end of file +endif() From f3edb07415e70c3726b8cde33ee4bc84dc426523 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Wed, 15 Nov 2023 11:19:59 -0600 Subject: [PATCH 267/318] added images, sections for sparse matrix --- docs/source/user_guide/images/dense.png | Bin 0 -> 377070 bytes docs/source/user_guide/images/sparse.png | Bin 0 -> 372764 bytes .../user_guide/vectorized_matrix_solver.rst | 14 +++++++++++++- 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 docs/source/user_guide/images/dense.png create mode 100644 docs/source/user_guide/images/sparse.png diff --git a/docs/source/user_guide/images/dense.png b/docs/source/user_guide/images/dense.png new file mode 100644 index 0000000000000000000000000000000000000000..0f69c89c858ce3fae2d91e26ef9540d2842e810b GIT binary patch literal 377070 zcmeFaXIN8fw>GK>Dk>r(A}F9DMG%lCy;uKS+UMqXgZKT7`;Xpl zg3;w%Rqs2ZdDx?IqjoVc4eMx4p?QNHW1$Vk@W{wWI&z%L_r1^k?|#&&vd9J`wWuCn zT#e%IrPV!sPQRtr6|N$*KXW-brs*FR%-og(B*P zsSE6kAy%GVxm%eA2}7H;yo&u4^RJ+$xi)y0!g<}UH%JrxkBJ*c9=L?CpwsoBvw(&T zL|ELlv_i1#m0w@c6I<6Gpn(Z#xT9xa+Z94af`?}e}yM58Pm-q_u9!SREif7IM8sYKLE8=44v1*!M#Dx zqm8kDxvL`F~P+`Rj@yPYoILWu?a2lz9t{qgnPQ^0e z(ORpqYbaQmXGDn`R;h6?QHvIH-IJgFd4n@YO9|fQ=gKLQMBqAGM&Y`aiIxr#m|Neg zIE-5mrk<=i!}#8o=ifdo&0g?7>lSX%<2+s*Cg)_=(qTiiU@le2RNWl_dVkU#mQ$aE~L1VO>KKh$mxL`H!%-J&JVl zk@&X-QF4w9;kL+bhb%6O1hyi3&8>J2Zs|dXJYDk|;Ux|y#ItH_=0?(+*r@frweQb7 zTWyhF9rsMJ&ohnp4||==lo&@@x;vBqX5`-tUsrZw>E%|7GcA4o;pa#?rcXjC&#iNn zoUF7$Wl+?4DYVyv6+U%dT@cawjDJHo$-9mwKX;(-{jD;F!x&JXXc-q6*^T>3Quc>t zWs;#`wU-1t7=?q`kQ;AY9``~!xnhPG3^~1=FV(WMp8xTH%DRAGS<%^lPGI;sNju&? zYMAH7=?30W@=o%%7KkP|#cO`Mq5HzP9W+0o<66^~1LH>O-@Y4FA}Y|Um>(T+H4xSW zBL0;dV!n?>wcXopnoK;LCJ-E<98DrCt74a;*43dF~i!-I#Nsi90 zdrBTpjYInN@49VVJIF=t55JaDk6Bn(LF-d- z9<5l9550;k&0exA)JEJJV4}*guY9l(U#PD~Sayh$+wjx6Y@xx$jVa_1j$$%oV2zIW zl8}pa>o4(KWkrh%=4tw|mF(Mm*rI5aeY1F9B;s*yOVRfLdIh!F7ZQvRjRIy1kcj48 zMebA;O<#TKu>~dpGt<#P%W9js|B=dAu{fmd~6{ zTc@ittVY{KnAmG9ndxIQKQAgA4P8wCZhM6wqrzX*#^H6{OCPLT-vD@u4Vk*fm7has zZ9Z3YtnP*B8&XADbn?u9yly6&o0D7P(`Py~7>iwe>%kmi{=%`D&Hk%pjY+kM6ONFU zJiR#I!j)W%)Q}r`@S@*kNevdP-{jCKkm+qV6j(Q`)=W@9l>(o-f%KPnBCH z(586)qgP>N$8m`np+~ux0x7zTbG~C=9s1HD4Y-w_$1E}xGcXdh(nFI%$}%84CB=4ynb84-vhrZ@W1|-C(o8$6*o1(g9ua$uf zp-0Z|*N5Yo9IS|!CHAB~u*d=#SKU5A!?y)-PH5?CTtxbbrx}4`I>6JA_-yev;@cJC z{&XC2lMmGRwmR?wqN?wP+#OdpXMEHTdyeP9R}xMqn!$3q#c}-sgm=!R=N|D{rydAn zjNpu=w~>*2@g>^D;(H5QbIhb1wNb~dZ>!09sI|z3%S)eetrzW8K$S~!YaCvjJHL-Z z=w2hI%_3GJBz%(OHkV*Q@xiJ!(^<@}hQ8=s!SdAdy=S=wmB+(FwoOGB_H;pCsQK+T zorAY1yL!G4SSDQK*kL74kwp95n!j*>W>(yO!L$p=XG6A zQBk+=U);QsW;kQgP@^9p^f|9!LLQ7FsitD%R#})GUGfG0}|s^>KM1P;Z(i39L#QpoU`u5-IE-elC+u|j~VnF z&jzB*lUop0P{vqIVQX}i$JU3_{Vr3?JE_I=t+DFq=L^7##p*hyF9MidEYtg6URzSp z%-85|Nxi^~@>JaRD3!w(TeND{>@WX7dp*G3^*IWy!+R^RZtk#|!@8X)cRVZu<|Yp_ z^r}d;ds~bsP~)j~@z96mP{=)T)0OYWm2^z32%F(a8!sf;8Q$I%KEUpo!Pg0KUz2h-~bY@`?C+&l$9Y6F4S=Ny0(#-EiWU#ZW~Ro6iv; zOF03vZw1IjjmNJ(y2qbqHGSKSb1hfEd~ukO{MMaihF3-~|7?ri{ODsmks|vIMXw?L zvT;P~3@$6`u_!web&NYoGI6jDz~w1=y1ED9LV2V_iCZ zr;oxS9zTXVir^@KW=5DftF)StKD0eW^FT@h&R6d=4^V=oe1NyL9oW^@mT^`pU}sYf zV`^5sMMT*hX>C zgjhif$jSWC_0Srsj^Wgk{L$pmY!MQ2g|L@19>P~s+&O;tEEGa74Wy99?Do+!v%V+e zPvnRJYV$}fbKVU&!>GYB>Ci4K&uq`Sv{@@gkh{GMqocV>@*DGEvN#}bhs>|S=dW%v zK6s2;yrDpoDuTX@8fH`@EzQa4qp{INc8vv&&@s|=%W=+h4p)}Y^d2?ZfS0uf(?8H& z3$P#Tq)Uu#yI!nxucy*$wKqmHTbhwS>gpD2c>BT!D^A0$+eGKSQ@!JxnI?!SpYk5; zI(Dh5CI=l`!~Fk%78}laK#`a%D3!`j3CXU|`CcWMMY`n5O8vqLFDsxVpi~8D@ z2;<`w#=Z(t7-VV(Nw^vCVy==i&0+15Tfk<)olmF5i#k;XVcD4{9yS(t&kTi|SE2%E z=iZhVZ`mB*5m zdsZ%AjPhl+7FL`{*?eN;BH?976x4Guy4=-%OS*bv(8|vbWi$r$a?Hu-jpUTwWjoS* zoYDFdPn;*gGBQK9Htcw;+eJ59SvRE{W07nhyQ3}ph?-9s`=N7V1Ocz3q2|q2YAn!; zMh>UBxb#&gS`KU)eiT&g$6eN{%qwIz)wI(kRhXPjka@O{m@7JvX7ugDW-tOkM!Pfo z7+Tf`m8y(i?!lcpjuSpRXv`hbo8d8om)ChtO`Se~Z6C=d^3T6MCHwzYb{#|7K&JP%tVc{fLDLOm;h48E9*G9vB&XgU;-T-Oksn_N zn*o%M`)uV|m?-Om=oYr`40p*5%!hs?mMy1igH^UDMzbrlYAQl=%jc{FbIiw871HdH zswR8YxdY{%YoK?mk>9QNZK|)Q8x9W#_X`5ZVDF&owwWef{W4G2j1T^$5Ge9nqsWM! zMR-w+P}MsP5Sd+>u$W@=Q;?4nMO#nT%yjV6@ZIrF<=5fs4}f5?v~!p97!-V24%Q-@ z=#ltsAK6j%G5Cr?_iGY(ff-Nq@u43`Y|qp13k@o!P*d^o=+SmSrSSx2Bb0p#iy5y( zId1H1NFWvAY2R(xCM%{a{6Y+uM@b`ZL*%h5Bm@rT-D-_RCHU{%D<8>eGl@CuS`Y2n zYJ!q2YzkL=#7<2ImlaR28!XYG#I|Bd=e%gE{l~9)u>-FnjgA zp)d1J0{^k4fVpW-2ttxSCR<%dCDRZYvNv;iu5a7x1FX!E$hD7!Zxf?kDWm9YqLLd& zJKm$hl#=`Iij2%Sgm7-fwsNpHa&QpHGL+SvhrAyPhZXRs4`1cR@-l&V>;dq&^9#*SU8~v!{NBnqh>$(Z;WZpmE$*Iosbc40m z$|(~^-3Y^H->sOPnOh9>-8Zbs2iTVl6IWurn5S9E9pgH3vG}&pbuHPsMvYH3hw~s8 z9dq)&E#VEa>J|dQLyft5*4K)GRIO?<;Gj|H*f6I`4FHahk~)pbXn-bT`yTT?%|Ts(`R=ePC&T-ZGjV(Yov`eW1mfz`3$v5+K!5+| z+=?do_TjTk2B4mv-0^kRex|THF}fxZ9vW}XgC+&TWC6}XL=BN+-^<7tpy*paeDQ>y zR0fwhlyfPx>i4KauVPuB`_88F@-a}vjRwa z85>X0v;tGjwQvETcCkQ-({{OMm2`e2=ePzR(l;RBPEkyF<^*wG-ermKr2J4 zUCak6lTpp5Xl)jtP)j;K$mC@xwTph(se7?|*g+-#xUiq-^1?8^cs04QB5Xac;k>Pk zTIFio(93iW4UyL22Y*z-w|5VmXbXsqirGVo1u2@D?G(Qh?v1g5vLziMDICNlcaPAw zbCJ`k^rrAAW=c>zLmMSo;AsI~IO+0@Tlin;I(be#A}3MEok7QwV*Eh7rr1fO(#&;Z zuSrm|Te4JVnMe~m$I$K~wWBk;(50n4$3@!C;^LUz{#0DZmLA<@tvXEfnk_kP z%B&{u#U^~}(Di4al;AMNUt-zx&FY9?bbG(pNQi1$wO_p7-O}oz>LKfawnu+z0dSP* zRyV#3D(&$*&$dlgFmrNv>cqLn``D7)V%=vAPn;7R{?4dCT*?Oe)KM#!PztbUF^*p5 zGJd^h(6Xa|Stm)JpB31J>#q8+vX?CaM6Vrh5l|?4xQbq7F>(}|8D1?jUALHDj_DoK z;^wN2@fv$?*w)o)U;FVG`xMSDSM~Eru!=(0Z4&pAzP!2_cBQw2^PPRG1atakul<%9Xx4@y}WS^Eht3% zN^RRhIh=qg!Hk{;&2`j_TfMh0-EJ7viC9CC$X9$-!brmLSXfv}nPSd`0QcQM|D}(d$nlBE3IsFPV5u>+vVAVE zyT?niQUEOiSt5A>D%2==D;_Q{ZIuw5X3RI1D1@Lf2WU;Jc{U136@&VuW2JPqdF>Lk zf3WfFQi>8tvAx}Rf|*_q!M5+U-$YxwWx^YMg@)S50xkn39H9&w?>+Dk;J^QRJCSsH zL-td1tnO(Z573kJKVk`7BcU00vssvkayX}aNo>zx}u&$du&HPGod)kWXd9; z%*y1Ug4m1$R~VX%+a;XIt9bCLnv$U0;Uf<27C9!}wyNrw@&j*VPYc&}iG0}y1V~r9 z%$IJtjC?JmqtY5KNkgO{_ox;|0h0x42LaWoSK7pn*hq|ZC$Y}yt9PCUF?hOsvn*8p$x0G83Tj=@sC`>g zlbJrdJ<@uKeY`OLpj3hO>l_5@56#NQSY<1`6q*y*TVC5@TMf+Y#BG(FB?O}zeQYag z%n86Kpp+6I$7)s~S3fDxK`6-}`+JDqBxyJak1^G)<;)~)AA zoq;32XSU{-oV}4AQP5|t|}X0ZT!G_aYC=ZoM9m(Z##wdv`kcuxni^>oe3RU7;>E;zX){Gu_s$%+$OUrTOQ-;Kl;MMh#}62NnZx$pBLEGqP|W?thU z(OV*jHCCmWI7t_}vT>EOT|3>Eq_uKg)HVDVN~?OLW?vlFPJJJ1ATwKi{+XUC*%MwA zvu|*z`Iu~DK|=zW4ip;z`U*h<`#Azed=m(X9(q4ywW9PAsL)c#N$TS~MemSK>%%2Q zVzy-oC2jaR4(_sno&8>fOVnsbr@09ZPsovk z8o}y04^mv0`b%aQdcO(Wr#Ka$T|RfRkv~84oZ0^MUd8KR!FX+jY%pA(Q=a1z8t>c= zOyHRAAi2Z=6tWTK2S0W0!h#;J+7ZTXQz_)Hbul{65MH~^kZ%2sKHh?C%kT39PTD6<78| zgC368l&@B~J5m#16j5@Pv-Oe6Dj!57XD`|hk3fFM2#czQq3*?(_L~>B5?}!WPuC9! z@m&u?;+d<_RhM>&ErwzEDA47{r|WfNr70bj|pj zoqx9T+V|YfTe|FZ4#)F%u|XoRv;*HgPH0+)l{KbSsd{OdVc_RxBi2EN^`eNCt97oY zdZb0UwE|7k`v(C~3c-k6BN-MO-oyNm5>TLt+jx7X^ym24%=>)U4%*j5z4;4H0be$wX94Vbm@S5v< zJ}RQw3B=Q&tR#@2ow9RbQ982{X3QjDF2~w%Eegga1}?{Uj7n-DK|>+Ziey-8E!ED{ z_)Gi|Rzi?SFBCKJrVYHqUA-ABWNUgfPy;l!^Zhc_l&Nj!pPAakA9+a6%P5qV!AU5{psFNtLh8#UYnnAA+)r57sAbts=&Z ztTc~ge-v40N2ydMj;~F+9u_dPnr=^B4~W~Sm$ww-pL0Nex!m^c$^}n%H~aX(oG+S9 zFAgPPUnI0N7cUR&7DaD^O%iz{#@A{z0?SXgdmRkWyN=yBGdbZ_-EiJsMFA~~F}s;R z$l>a!;iJ#?+8U6c{Wxudnq+gSAgZCBMFSTXdB4Iy{_Da~+L5aoLh?Z86#61sC!7H! z?q&6+bhIHmFxCt|!|W}{Z8{|072}7ye~u0t$4xIim>~b0hLc(}NJCL`c+;J$40O9A zf3ZghuNTy#=bs?buy0tB@r@pYX6ndUD!lT0t?lkZsI`9B9 z0%6ruqChB+k!?RX3|&B9lyf#yZQnafFa=c&-%q z2hoijc?s0xC+dRIJy&Ea2oy3&+PE}U<5(3xg8t^(FxQI95C;PYJ@X&w*wHSzf$K2J zglUHWpg{+}Fi(PB0gCH;x}iXCDxgA&6H1b|ZDj=+VrW#>O>c=c0F)}HAVVBUfa3NR zun9gj9-iR;iM!|R%a3Bf2o-4~=8i6^vF}`gJkf?-84ctITW0OMR4b&Zx}*nQH1ERr zJS#cmfF?)b(AJ!Vo`SA^<3bl?BBkO3{#ewx;LP6_r zCqtSpf-yVk4G{I?FMg5q%>+8ON;9dk1^wQU5$Z}AMaACuxufG{!J;7Zl@1B2Q!Rdj zJ_cy`rR=hjpHVys-xtQ)y4NM^$C}5o>~{0j&-c9tI0xQh?S>Fd6&NePc%kQC@f{8+ zz8nJDCaqsYF#8AsDTB8+^G1NirrBQ6s}O`8SoI*j*2#Pm+{IY?2i^WX6$^ytoD=+# zj{lf4h_Bp!`vVE*!IVlq%TTPhH~y8c26um>HAgDQCB-m}NZ6b;Pl}h6)~ua#tx@=9 zFS^r`Y3=8L)Slg>XQqb~HbR@q6beB6tL`U*m0u8=o}5+QBwk z`sgdUXpAi1mk?-s@fPIcOgFs5=-ZKVPJCr{_0a*)DKh=8H2*w*zZ;rL$NUNFjJY7n z?)a2b0uY9T;?HxNV<1F80|5wdEgLWalttNm1pR}@)jdy%an%GN#hLv)XzL@Ug|&m5 z>Lhm3@e^?KyXHE6W`e_)!cK40ds_ottQ|zy@M-7ax7Tpq+@I?$XKo+|)Hdn>Y22Op z@a{H6|1JxY@@uEOb{AuTp4aH*+ehhMU{bh8#qhC|(e~i#@6h!+a_mL3YiNwEwRvQ% zK`ke%rhvA}ltUL+zI6`chzq!NSYym{xf`{oHz^po>J4q!1ZuwCNVsyp8eRi#UXy{* zJYp#R@Drn_3tJy~Q0qGJA<1dL`asy#h(94Q{ZjJ!$+aFnJe zR2?1}0ECgrxdvn13bADR35oFWRV3H5-gHZiOAtxHoGohmC*7==wu=?%qD&zlSpdc0 zZR-vGbB1)db8ALNToWfnQR3a}JQSV)8|=CjaIrK~(3M~Gkd`?N|3{QjLjNTIOSp70 zki;}-!YLKV7J4`0l`V>@AKazT1idO!*llXM8O)qo6F69VT%_IEI^^N3ZjUu3Hw5GF zou0^r@vq*v^;p*?;;a_jjh|FLKrhX_6QkIEVRs!+S3LB}kwKsp5S0WUG7%@b2$Qag6&7SgYMRK{MGt;agmw|An$Kw^rO zXczMU&UVXfqKY?oqP05FYEvvKO%t_#Lhc7Vxx3rWsg&iXdMUUW1?;g z^|z+#M?nM$JqugqCL625<~bl)x}-lKwn26g8`pN7-x=j`Pi)!~|9twKP`MK{OSo}B zwVl$|1a2o>z{osvcGx=TI?)!eLW)ncer8qB=iQRyknQ;u+mS|GGs%J`D>t(dC8=n2 zn>+wes{!r6|`;$ycr{|giLo7|0KCNwgY~Bz)$*`(3fKePhoeD+ANsBSo zxm$(4?_`}2sf4E!`CmT)5$lVs5y|NR4M;0Ow``o&b_Vn@2?PB~Z|X_9weJ%0c&?!o zi3|JZgy3?8;qAAU%ovC`oZS-)dw$^F7Obc)*B_0O^5b+hssd7NadBF|AkZm)^Nv@k zOrYkEjuZ5_Bsq5=-KV;Z5Fly$FfYk`=QwLW94}IIUEnNzW-CsoP;Ic1W4Y&ues2B2 zyki#YxX;te+*|=E&|zL-p&AxT{{B3;uJhY!*p2+@wRNAKbP-a zb#Dd&>1b7mS-O=W?#sm_z^-ig@|^{NBS~8UF*7?zND@nEjQ)dqwLnwXhM^cl@@PF) z4k}!;YFFP8pY%^NGp!vf9Drbo#R@Q4#={k-K2kjEHXTo-G=m}>9|DZ+pJZ=&V#T+F z4_n!m=I9g$wnS4WKsQvQ#*_2 zaA+8XWz!6*RnO|v3fG0;Myzr}0<}~p*I(Uja>PQ&Ujk&|(Wp&}?eMsj@DGcbKT7X@ zf#{xQ7vfwm8n5A&E{NQNk~$T8-H{dD;jPrtgt%Q_Py6Aql~l9cxAx_+Nr(pfQAb<% zqmy=}tEZ;Ahg9bBxaM@$7dta{lKanqQgw^piO*`xOLDqK<|o5by?S12)X*zHdBvy6 z322Y`E&hBy5YB#vd)^f}YfKYaG;%ZUEI!`v^l7VH^ z_J`g`)s}^K%-S>ux-8h|$s@XX~CN`njdFbRh;q00(Lfc=<-3%CndCW5y{Ia`j?byd1%iIgP?96GfHBsJ!00L6O=%a_2*}~uR zThgB6-pPeqC019u;0Jyqp0YnRfJr9;;jQ1eyX|a2!~LHCN#bg-WYiwE5;LHXIl3*> z%LS0y=1!SrVlM)YhFyQiVINZS4Kjx35il&YKVZ_-TKBTwVitA0VT@k8s*kO6K>(_a zk*}BD%~|r~_=G7}Qn2n9ItA;zqw9nlE(7f1OjeRyTqmx1!tsOW<4i(2Zqa};%DgSN z^9rtBM`fQ^pIxTCL!S0`BIe{n<@;PR6-%8UwL4xvm_FjqOA%OH72w@1FmU~*l<*s>&TTLft^NL514apKa$_?`rkYCN*6#q&oE%PkR5ZnbQjK?69Cs%a=%@RTt<4(PhM(kOv+m zW7!^m@_9yfoBj>dEh43gx_f}?a`2_LRZ(0SfHC&$ zRrVhycB61(oK``zQJVeOlW8nM54z;3|##?3nApzA;cjU{5n$a2A06H*zui5rnRXu2@sbfczqYfe7uMwau zH=sqP##`@FH&q5}tynyS;WgdKBn0$*z(k8vB=dkZ5b=ZJi1Gm?j2%yNwsAr!P>p() z_S$u<%f*8P*ant8cKe0Flip$7W@C&S8WKghW8skVEIj$w^|x4UW#jVD@(p$pgfgf! z7E<2)&$jFLiAOG9#ka#1;Wy2A*m5Fb)iC*9TLx6v96Eh=>XYY*Ygiw|rIbdA&a35Z zufu~lzJ)o8+$Rp^Ha@tC7W_KtaQ6?zILl@<{y@~K1Jp>POlD!y(WE2qBR(FdyIzgq zHV~kp5isge>FsIABY`dK?aXnX%=1@@) zWn~iP_S11Uu%A00z41L)1lGrwr~oz<5Vht~FAj@$cK}l^ zc@Nt!Lgk(Pr~A-|3yULF$5Ev;6lIS1gQTw}b<;UdMnx(V2tt?{WOiICwT>M9W^W5t z7W;Nt-kc3&a^-JktVQ<(3R#xj1xawg9 z`FH^9b%~m;sd403d0W9iq1d)8z%&w(a?(Al5HO9fAB8ijQL{i-cSbjB53u189;+)P zxpK$L%h-$qgCu;8&G>>2v?;*(E3h0ulaZc{y$>+0yAkJuff*dLrOy!&?J-vms+(Du zf-PI$zpAOshjna!Dt_j6_&^hwIZ-#)MwM64Ui-w@H~tZt^d{t6z}}M8y!0I3K2VpI zAMD3?S~CJ_!fy}#;;)Db2@ELZE`K959eQim+X~-}3|h${C&_8ht<9G}03s7!h{~|( zOV>dJd*lG)&sA}=*TE-pk_by7Gvfeaj}8rL=G^(-JG`d`LT_L+1t?3qzC@oH3A^lk za^m;}HfL!&H9YGOEOU{Ywo)+v2lEI-kfu2Vn2Bp{_o{v*N9b;)8Crkv_F!D5WZPLw z`&v}(tp5?GOL%{_9z%HV+e}{9TLC@vG6z;d z@7y4vOo8bit1mVlKx&7>K)^5A@fNNmj z83DgP7vL)D7P-YC7{hQdvo&#>g4tXhl&^_Yd62GoDQN}w@p|Ob)t}R(18_%8GiQ=J zg^-J1g^trLA-p*9v`<3;!RFP{d%gMQVVvf^%d55uJo<>k+<0+!ey{w59iu>rrr+5v z_*%rs);ECLR8@L4%v$KzQ3-(0)nk9MwHP*V2-;Btl|Xn9jx+P|7}F?Z!Vr>0vJDFuG!>NfOhh6p_~40b~Sx$c3u9&FsQoL zJkW(O=Nx z0QmI0f7KFX^5$5q9cBcEpkbXX3(bnmsgT*16x})EyEmF?OgG$)n*_O7WJLRlVZd~# z5{BEX(yTKf(lueI;g7VXiCKIb5sp1#>aS8@#$bvDOwDY1B)_r?R#57~783}On-LDfa^-4z7he1f_YTKQ*E$AkUzWm(4=oQ%nr5G}>;RRG* zY6RM&dVAYS6n2x(as$8slu9Gg%6n(fUm+*-*PXA6f7^sV)f`<~K0rbnsi|6BRw(rk zmQBo%TS9S*0~0}kxetE;dQS4a#n)xX%RB;?x@q_HPzBD|wxK+vpAh-J|r(^+59bluvw6jVpXe(azzw;$%MMFS(2*Y$6g z$1erjr*Q7A?^*5dy7`SGXqGd!5x2QBvVZ6>s5|&G9Z4Kv+!KL7AedmSiTweB2OgA5 z&HA5=MA;e=O+N+LOw?$#QfQ6!YcJA#@X-hwlf^b!wn{~>QQnLe7EAb0s?lkRbDP$E zE^n#JFh73)7;KTwWJ@mCA+1E_-8Y#3q~sylk~8b?nTEu zS~Bha*6yO(2N|r>ae_Yo);`urXL;#adi~`YGdJ}2PbN%p&Z2uyCQNtM*^1Z|?aRM8 zrL&o-701Phie|aw+HmHP2c0U!I03v}!Y8wi4!g^U$WoN$FfL&S-li+49Ri3HAnN6xrfctPXs5HW z!5?(JF9o6qv-9vS-h#sRV2;u^KoL^dalBcI7Klr>j-XvyZu~g%#kpgVW3Ilqd`IKv z3BB9in zq_7_W!D;m828d=h+xM(>!?%Ev$KOopvI4RK0CS%WaTsW`*en-)h`r`%zp@qRI|4bc zvL)GvaZH6qL7*|#s3x|Pl9Ez((2^!JY@Q1qgxSy0!K2*}sIhY}PV}`{NuZmYustlA zOS+zB0CCe0iAO$aTkJt*$|&3b@;aO$**gWr-bZ-)DRDoMyMiM+&KL#* zx>T2NWTcP`#Mc1H{JQ5mh#`r3`lltuJ?k53RiR?1f$3n zN{Hx(rFlOjpd#Bb1{9pTa|}o~Qy`UhYr|!8mxrJW1%Ntpdie0$QWCX;PUhjJtU7W7 z8O6nfMGBcftX&vd`23#u5G8=Q>w&VG)d#4{4NJI*;{ zz`Ta&KK;~PQKK^zl|QM^E2tW@mfkX)oqPObrxx@=lKzJxUiLkrDN;Jl@Jkn^wbJp_ z1@?W97P6i;vkVUgGf*oq-T&tBI2<2e-cGI537FOa3R#oRJjD>Gb=>0f&;vg~Ku;rD z*Mw}lh|!{){y8I32+Ww9l#!NeA9`dq9Y;-Awc#Yj25wZxzRK2x6+yHfC?==BQ+&qp zCcD|bWcy7<#G^FsBMD$y)I_U4I?2D;AozR+diuN?5N9vRswpMQ@BwrDfa=d~ zA2k&g^c4Se)EB385(dH(8_x^_H@H1;fRXQtJv5xsPb5IU*;qpM(ea8Kq47fMt{Zk& zX#eY1wVfBGnXfy|e9eUe+fHWmM8i#K4~t%gc@AS1?HcAZylqJ(Y`A|TY4{nEF5WKuL*z~eu5(UG#7xAOhxt%c`C1shzk{jl_qFIU zbP(|TgK{c<=#we5n$k2d1m0K4#c8idm+Zpw)DS}nRO{LkZ;$_!0wfNEh#DEqGt4>z zN)@8qByarUPL1fanSm#c{y~bhbK&<-SDgBsy9qq7LYOc%^>k=|!d~dmJhg8;FYt4h z*3baxAb!byv_5IsY54Xc$0{DsD(MH{tf}a_?JC`x-HiVHApGTFch~2TNKs~lF7QwT zwMx29qGxR9Q|-w?X;)qq2C$z#k^%%GF969NtVY{CJQK>|R^5Cf-QKsbI)%l4d6nPf z#zqf}WGjOq$;%Z3vO!mp6RxG_`wBZ*CD3+{rGFH%W|0j|;jXK15MOR{f;+q{BR3@GlRt+gtP2ouXeTzoeAE5f8(b<5Vt%JYZ~d|7}6I5dbS*=QpC)YnV0p``75b0}BCnpu+<)kQA1C5YDs7?pr`}tftb$Tc9ygJz z=dfzx^fa$$6iL;jnQfiGTmuzkT1=LSRytZA?Rg`X5~BuYb)T zA)NHa5M2hp`PX;;!O9Jv+?P%J#5LIW4?g=>OZc?iyg@Iox^D^l>pTCzYQkh!FW-7R z-3jCPrxyM{?k=ea7dJo=;i=B_53cmrt&?rS!4YY5wr2|Z>pTCz_`kOKKXdP2+x#E8 z_pj6Zf1LAw;A_85^M4k_eg*CSFj@WImz!&=8Swwq0-PMCUt9kF`7Qso&Ho@({1x8) z-*>WqZS${f{wL}F7wZ0ny8m6y`&C2#hupz0s^lLLVgD-Meo-a=S+e>Sw0{NdUqSot zJoGO{>|Y`EAN}%oMl8v}hlG*Zs6+iGg*q4Wp`P-9+xdO<)v~M=x}EP;G`)F^A9}OC zUo@;$y87s^MbarTKkF{g>TsOKgpTYr?ZgbpXSHn!^oV3BO`3bd{Fu(AqY;FyZS``yIHSsO6kC`3k{*qFKXM%4@IkM62}S^0{ed6wNcU(Im5Xl>z?*xz~{c%?bBMf35)c z@~a|*r=jLe^DN|puJ)216~JMK@$Gtah?Ly@AsAGFk{A(i3)xJyMeqnY%? z7!muF-*#2JeVxShMKV!zk!mNZ=gmJcCJ7VJX-X*@pThssrIhejJa_$_-S(P!o>K}L z?k^y-c?z(Kbw_!bca>@T1&@8Urv_zYTFYbXw^k|XR(i`NmCVWh$+a9$u9XL_JN(zR zmVAXH6%vCC8I|VZ+H0jzf~CV6NpGr$au_7*kCe`L(s0t+v;?J2?b_Pg2ZJ%^QPVpp zynul_-+caSEB;*f-(DeM1};}GbS~m|Q!b^}y=`pQ?`m#Ov5%#NCyeFzO6dRC*)22~ z4p^vk*;puSeGRAVKQ~MF)C2Qn=;$r!I`hiM{?ZV^So~Y6RN(QWypT#IEqja`=4Vv3 zjcJ!iJXkhl=g2C)&AeCX<)7R2r%#@;*|Nrf7cG4n3;O;3ebU>Nn||8bPlpZ)1w@V| zlJ?Itcj5ZyK$d!P4`16NJW+bJM`-@r9%YjAjYd1xF9UTxEzlZeRmx5^ZX1V09vYPy-ZKL6g!Wxasz-9TdX zQ{vwezAP(FzLeSx@b%c++d)x8fswiN-!Nkov5l#8grAy%$ZxGwLK>p7uj+R*YO6xM zV(0EQNz7jlHVV3XTOcBMmgOfjGxG-?VO1nJnJ}K?{zWHK)uTIx-(KviA7n_!7IV|O zm(a_TL|i0z=!7Y^EwL`tUAwSQ9#I!%dh(TsBexRQ=Kk$PaypOfs{;QBf^a?r_WSi6 zf)~GU#jTrBwI(hHi;at+T0;qJZnnu7^TUIG9l4dBM>bmgs9|~8r!*J zA|crI9T(dPY`!77))d;rx_*!>5|XaJd(}bcuskRHmXh}Fw$t*hd8&cb-*2<3mM|oS ztQobdl(zJM6Se1PMl?^^BjAKGn~tHZ$Z6u6LJUr~vKDXTWrLfI)&>^ewV9!A{|_g+ zzzQ$V)j>}KyYL=n<=Rw(nrhY_)k8@)q5ilZUQ(b1Kd-LyyWb}9TcR%PpZ;f|D2Skyh2nAK`x30BOsZfvx|CuCI=3vTgsikXuwR z2x$wDmTnM~?id|PH`2`nZbU&qT5^cw=orlgQj(Jzz0o*obdAA#q4)E=@8|v9zyHQR zz;#~dd3@ve9y?1=z1#RqF>!0|K1P1lcNO+VY4Z?DZj-yJLXLN69W`J)#>*h9Yi)YZ=1l7+3xN=?VjR0C1pa|dzLb3}*ISqjqGNDqb9EFp0b z*0bG%bQM@S`K6N#6Tx1cI=k@~?Fx3Oe*fzZTzdH2diDGGZjYWD*HWnkzB4m$GO11L zKLzob7QiK9H5&dQIOp7-d3~ggR>NI8p4^{rCX*@@R3*72; zt0mYd1yowft`8M?o}Ly-e6v{VWSUt?@m_8pbLnPy7I!_>Z@-i&D&PH>B;EbIu5KZv zkcjGN2I<5gb-tQJgvV+iuCY0RhYy!8k0EGW)J$rn9~XHA#z;ENN8_CDN6~i6C8#if z-E{+2YJ^D6v^*RTnzzIp!b1oiANp+kaYgdhS6-nU{avQGc8`bu2lD+Fj1!sP;(*!D zHh=U${rZ2XL2iXU_ivM>z!u;d6wL(6KLDvj<+EBcvAuvn(DPAoa1!PPN`K8%7^M5Z zh*99C9P?1#6+ay-TcOi67)_-#19vGy;`e-6N3>vnYaebJ7NkMO_=1%tF&(|S?X5ze zBJ@}W%7#r8FdK)N<5H7?m~yohO-AQTHs_4P+CvgGD|{*E0;3dzMmdD^ZYWm(PhP)4 zdi?x9^;zzJ->C)9f_E8xxZ@M}^xWTTX)+Q=GX1VK)WE|?{`JoW)x3hu?HfMh70jg5 zJ_)*05Id%O}>S(o3p!5Z2qHyf!3*V5S!Q%0cp;LTOH*w?05 z^0W?wKza(&kDqiRi-sr#>_#3QY+xjT(!nt0h!&rOjX#$(Ea&g+KYD5!05%op^HS?G z)em?#n^!{9HEeS$LYGzmIuy{elpV<;JEg) ziiM<-n+L=HJ3MP}T?wBSX3Jz05?XN^kjV0iywH;8S;Pfp~wXT;EbA}jX~f6>|C zFcQ1F31Q%MVn&+~qE4hS`2{KfilHk(B+>%ALow+0O)dp&$Zz+cKb8NJt}1N@>5Ngw z*mchjw*?C*BO}*>Uz|g3b3TznVt%2qtcC1iV++*n7c=?;9AkDMc%-RL`T|N@O zTwYA!bZwH^1&|D`^}QiV(V89B2A0@)Bb9Q!=<2`vg>0%1Gw^w)lA;; zd)M!q8GMNk3z2M?qPxW${f;II=B~$@ikB#f1$0HbcD!kZx)nOC z#{OovI#FLWl)0P8)`u!Jgrv%Bt|XQt{=?gtdh;*AiNWFzYo@Yg2gkS!Y94DpTG#=r zJzGr;tZ|~M1$(DupCno2w>dM=Bcn64-`eHlh6S`*qILCOuT^Dov*{NGM&4d193~c- zG@ex&X zLg6PJ`^LE~bc&KX$gXgHWXzeyWF%01rqN*;C(TqNQRc(csbZx67CRQ?UL@?el?Rm1 zJKHTk$QF(o(QFaf0A#1UzWcwV#vVH#A1>By;c#WL75{LeJ9qVMt_&}8MBiZKjKI1l zzY4v6SL|?FSnJBOlGQ6%akdQQC5L;-pqa76--qgTAjO?$mGI4U-`^k;YrIQjMIs)*o{$H8o^Ld{xs(0M z90_Kk-U=S}!@<+6RR75WBHWWOwT|vcl!ZtQt9@PN(vb^DThF2Ivtb-7@?>zAM_OhHJcRS~RDn*V`t* z_^kn0GHB|^xGV}NF>{D3hqoXMHfQ@Y*&&AK$ML~Tp#C?&&6EFg#{|1k94+0CpQj>= zLN+u}A?fdBWcVhnp~kQcNhSK!Wvc0n$WIyfkxhIo?xl&Gmkp7m{qyfGZ&(2MWzj;( znckaJ=MeMh>{;|#f0}QZs9S#jeC133B;m@yp*u&?)zEeE` zzT+tO_C=i@Z+6ddS&VfU1;-Fo(Cv0%HV|pSIijK1DdpI z9jojFq;lY@7bH6S>wVarE+kQ)yyE9=T>^x6$cbMM1mz0*G zIaySW!?;P@Il#?rdaFIB>GBir0PnPM|0%B@{|3SZD_5|-yYxu4D1gp(uqf-c%SF+) ze|9ceXbIl zvtsAlH7d6?V4hpJAm^DbqQfUzm>Soi)QNtwaqSmX*J7?U89z>Z1Q|~6XzuPr^R`bEbYXZL5SDS ztL9`u#?%1gOnIqZ;t{N4upYx5a7XCe-wl*%+MtFolDO?Jlk{*qN}pK1~OrntDhHJ$g0 z5zH+Uf|eCNe79M#rps2ff~TW%U0aJq7yTIX^LC_E&yXyDgVXzB{+y5kX@Fk{e6V}= z2O&tWn5YHT{2~_C0|yozb^cU!@SITpltLkoK9MUyKf%nh8Yz~krdgd~t9*CI8y3Ll z-eX>>4kdm{8Jsm&T*U2A*$@1-m5KDY8kMB8{U*L%ETY!Yu@sv7SCDkir5cC)@vM`M zmpf<&G)u2}zOpITgSwp-vf9Yj86%2x?|P5%lj0Q%bhedm@^Fr3sL%P1Nsm>xXG64( z)l<+z<;1R3A?F8$as6IKBhJqqGPuV3|LeN^->o`Q{sPKC=o51{U$Bmv-!+1Lj4q)a z+oDG?I0I~;%Q-pfhd(5e49y#j_S`juDA!|VE5p0GJkL`TJX2VuXBpvTLoe#V*X{iR zSyRRh4&k&wjWHd5Stek5^2yX!zwFmYo?;2cA+Sjr;~q{Wott7+MHjP2gC~G1HiSD>@NfU z@r5EY)jh@Xh?QUZS4U^mIMTmtsEq(b`Ed+kH5;|ChB=;HIFbo&3+E^B9c)laoy}A3 z`!K=8U^@Ae3ayJ;@d!lJcKG)C2SG#OKZA9d=@cca#jZ4+)Y z7@z{lLv<>ACL)?>A-v$+Td&ukxmVjS;0d=uN>{Epy|YoGoT>%AwcDOnWxfN5w?OBU zQ_kqHNWE8nUuNo6nq{u=eTmZfVKja$4R3`Bm)Z4 zG_VQMMqdwcF)uZ=QBJ!jYjB3HL~z18Hm2+z)X>DQW|pLts@2uPdeP(;CurfVld;Dc zWF@^`JIRghtp1*!?-4X7fI;>RQ{Q&eIrK$r$SVl;dBF5^-CVcgHl%jNGOi?V(FHSM zRizS^_WriLh_acT_GsGgv$6=hzxlR5{PeE?x!Ke%=1z(jOKy?$NPCT1U03WQiEl3X z%L1ha_OnW?_Z7|`A@mSlJOxI*`!R;3f0om7ZVcyKIy|iax9gM^w zBg;;AU?F(dDa}B0%-IHj+>a0~vSe^ZA*n<{z#G7r-`-NZdgtCVrP+8U_W5^fW>G@< zrBe@MLGe@H)YIw~8mC?@$lZ=eb*cCEJMA~v&X__!tLyuJDt7F#BQ9|NhswJjsZKa2 zU{%31zd3+DBf$8I$r>%A|L`7qlAQAai5bEaFN`zdijRlCQQ8?tJy z$ikaua(3zQ8SnB zP)NcD2jFxu8IvA?5@i(@LH_yq(RZqgS|)-ZKMOdm*{(OGT_Fq!Zy+s-O!5K%7jz+_ z&99@gpJsG|YGj#3N_z|4Ho!nPGbzA1UN%CC-32e)k2fE$tB*VY0W3QoUQJsS1ErSlgpH< z*3E=5yd^mBn4dN^@e;&pKLA`ktKizJ{e(KS%GxP0I?_x^f@v+F>4m9FO(9zSDX2-E z;)7|1FhXr572222ChVC}SE_dR47Xf+W}}q;|4LuVJGyt+9$z;(GFJioP@A#;*y&%s z?)~X(c(MV8rrPmL`tN^qME$p63|K{=tyag<39?Nzo7zl~!+Hbgj4C+NfUuby^Ncku zfX5{u z>1Z40j`$lwx^8+l@!57PdlOopewJL}dbm66?yL^)bGZRk$uSRMM}5zO301qX~$KCRDkpebCqdW`nDcnvhU z%yZ^KJhI?MnY3WBCnH+VH#&eLp>53_y~M6(eZ_UMcKo^f!?)8^bR;AB&Ae3qgDwPr zzal?#W-WfuDeo)Bfbd_r!2K{{_OD(4RJmQ#*ti+-z|JgNnC1-AHMTd3`|vf9!p{$!8NClCaavAmY`rLSIJ!=lgt7@;8$@>!=~Gl-}+4 zjf?R3&kbc$Va4VVI@rGXJi5^k-7cLQ-|AvepN^HyTYS06*8?yg2^L+PMpWzhf23@I z@3Eav(yM@OAh2*mc{Xh?`^puC7r&F z<@IMPH|FsKZq6Tdyj*Zs!!19ljSju;tX1aq`hrJXz!E?``g-%R;Cye3soLr1lQ7$K z-x~sic!n>Ywxd27&B9{ttwXlcZ$q;gzvolgYTHdYTIt{SiBT1dkXL8;GEtRk57@;& zeEPrCKIL=XQ6#&56V`?KLV>B508^vyO3~g+b28>jIT#K|D$}PPUgrAPT}9ae&{p2v zO#F7G?M(5Y&4KX7E;A>s{F`tZSO8wK@U|(6U45(4pE% zMuw!fqDn@UFb)6sLqWUxHy+Nz{JVtovfNc}qqDvGrod9XE>74ZoK&E4de8d_VJ65* zW|Yq-_uVI*u%)$^N@O`4%lK-xo2p>G%`;PkVPQiYv>;FzteDfI*>U~3>G6-h>rN^@ ziX_LammYWOf()Pz|Cd(%w>>H?y*$H|onP!2Z3e1`d{KZ4!)H@YE9iKqBvr64oxa9) z@_DL&^8=?;fu3uZa&lf79+NEI6lAMjO`uyP_V*eHFm;0^{Jo~m9rRhmrbn+iXnze4 zc-~|gIuoF9t$Q_iIdZk{UL#y-9ZcbS9MvF-^WB7p#bYULB!F_v&mVZOh|`f~rN4Nd zEG{CTDU=KM|D%2PU;J)0jz~pC>B!VTISIr&LgCIdT>)@yW`(SgB=)U`MAz04Cin}T zUSSb~a%g@hxphi)q1h_Yn?mv~_x<-B^j3B3o4|gTkI4Y0kMQ-fUw-4j>IAf!0pnz} z_bd|^bm7-iom-sd8(_6(J5unTy*?}U*i?%kvAkJcFHD2#^lozy5q@cHYx7gg!eN`# z2dU;gX;dFC)?Fw^zDO8QoV<{D#|>N=U^xT6|G?DK1w8mC!f@Bvb9m*p)iN$R1-xb6uKezlr=n7Qu+cIb87ch$b03-_%o z^0b@ce-GjFF;QW{NnA+5(^E#A%|?7ksn!;#Mt6Ah8LCO1M6Dh_4(BOX?hTP+jW>bz zxsH|FZKJ~-^u`p469}TU9|CKgl8XP zm1W@^)CjRWmjJso7-L`ZN%q^G&GY`R^vb4w(6?@#W}Tf<$^~BWl~glteQ+?g(9`2w zviTgTrg(Q!Y{%jgFSfwwBC;<~kDOb3hyZh~ZU+-WNZH2EN2TYwIQytr+2tE{P0Y3V zL~ML+4})q^@onr4?G@bg=Ey6OT#J1H9gtYePKNH4z7YLJSj~ch^cZ=9OanIZ24!as zN17WuyUj}R?v)MWC_rxI7UU#cSb~MzDK-?;c&5X|$+PDgW^A zTdD8`$%Eq=_u=`5E*6SJiANaT6GxOdmw280`jGpAq0xr=*^ycvzf(;T`}kKBb5&3~5i)6|P&(;xi; ztvVa?#NG&#Tn@Ry$30axn|BKD^+S_-9P)Xm>L(*tNIwoZrIPRUj6MR93-2utZZD4v zK|3sIxtp(vpRTtKR@!a4^?@>eUBnfp0NK3%83sMn-)(RtdpuT4C}E8>?^0~NsV5#M z=03zHHS?%uUBhoQ&m3pwh-fZjp#H{egS>Jm)#%JXMc=e!_>vwVu(1pWWkp^`(ew*| z5f_-Jgq#|diguaiZOFk@jIQ9@3#a5E{b}G6a1`WNaXp~BS0veE=}q&zk(jQ;9jA_- z5Ao+mYB>lN*r3+yl_xA@9Kw5}F{*vn+DeLQ`j`6`xVv18N?JaU*BwTNciLp^ILYsS z48((LNI)cg;|mdbN`~eqhv_V8Q}7&zN>4SgnRa3(d2u6$lV5Zan@vVt7bTOGWzoUn zt$$(Vlvf}e26q7u;Op#!Sz+Rb-MO505Nj}~Ch5v;Et%x^w^5~w#+0*bukRMx9{}-z zh5diLSNow51!VEAghg?oUG8qN5ZAqDTH2ZWZ_-z(AcBSb_5pM6})f)VIWEQAi9#q<$?1W%j$aR48cnk|Wm6bYx`U+Nk zTvQAmm^HBBl`BFUYa@t*>Sk=-t4;80=|*Wy4PLx(8(%x|DPbWg0TC|0ErOQIcFI}d zJtdNRhNJ%&;S*gd!5sI?PK^_B$qb$Jd%cG-Y=dZY?x z9ZE-JF72gY6u09H^N_S^B%te-dJ-!FWGzig!B*d!>Trok*>9ruX7oyp*(7ZmBUKGvf{u@8HWH@fplKOPl%s_egYIuR;$TTU62bkM}> z{kC->-;ps;i~xSE$m4F>j3nFi72Blc`IgfgH#cv^9syte8h^dLg{!_OBIlNQD3?#g zv)Vm^|F2n9i(Xa1o@>62<8MRzJnbaA*+9n%AhC!PvwT6-`CRvKOH?e|Yl&mj`j?}@ z>|ba&)=-*Zf-;S*Jcr}s#AK?~*l>VWi#q5maAm;aq?4*jXO_+S>d?x`z?4cD9hCG(*VCOX@m>^nl^Iu}h|h(M#rRBQ{>m3dPJP1>zNzou^h&x`kuF7oAw{ccFA8tm8Kz#l z$e_Fl6>CpEsVVYG+*;&WS+H^iY%&tveuW!X&4g{i29^Zoh>iELjxaCi~ z&xAEDF(28Q#k%M!{qk(Pe|+C>W_!lT9IysPw`Ok3|At{GHy}k%-pl=o15mJkt_;7B zQm)5REaU;twEx1MhWfPwsSBL~BSk61CXiOD46m}eCFOu{-VT+heP-9R>SfvxU`4l^ zCiJA4+=P}>!lUsb8fSzP9A%Ki^r9ui_#<^p13>KibF-`Q><3E+qJ_h(R17ZjrLCM4zGt5ElDS&$|7iHv$OI zNd+J)GzY@^VJ|Pll2OZWc#c>|v#ba$*4B)oV?kK-Pmpib~a~%ba zimLQ9*gVPvz(KpQMqScZ&uM=omhozz9H&w!DAnu15iSnV@jWIGnAzcj$#0D5=Q5ffxBVO|HFIqCScA0|o!2T zXuTVvgyW$FYry>nf88li1*9;n(82MK=PrGP8!p8SByVKIcL&nh%0xlXx{U%!*=7~P zKGh3bPZ$qhZzUpj!8~#n&-Sx&j!bETN@80_=MX^>@jT55NtLB8gL#S8OEg-AHH7m76HY}AR>=b@4HA-`9Hy!{_P->~l93wZs7Gw>@hKf6ziF@Vmj z&heFJ@-B8C%U9!aey$roao(V}ka%N)Oae;tEbib4pu5TZ7>})3s3*Xh6g|!n&1(2S z^i|wafTqv+#$;C+VmLnsZ@HFk`cs{Ib;SW2yov$WnK)`3(G!rjcBdQN0m8W$G;zGh z?fvm8v2&&Ah6}8SDDHiz0 zcV-><7Vt#8_*^_o?HaruG(bYjI=)+DlV=&i>joqD`4Zyq2Kz%XxPK_?CGz5KQgX*A zGcrJGhXzNAyrYakUKeiAc)$X)$VwucWG?Ue#H41UBV{t(4n{%+B(Jp}3$59kIYV1D zk`rc9P=Oas>299m(DjHE^_{_gkwwH=_SVpPwkr`4YP~)EQqLaLpxT%a} zeAse=F&k6L(9TWcy;*!L8*B`ujD%=w{LrOOlZEuBcA@CgYz9t5Yq-*%7nClq!$J2I z63K%yzsVe~J~7=&WN_^(&4>tMFojLL>cd+atb26#5?$3}IGXc{MN1SU)a)A|sZ>al z1~|}&fMlgdg`=K%*Q~KF%Ro2soA1K=6h!2d(_pqP&hG7>O$q|GEGW^P{*Sl#8T7E( zF^xCz{Gi^Bi1DW-?L&h6-V-V62S#26L0K>1N>o6WQ*<|EtHG94VbKVvOGC=LVTMrD z_C|(SXAogZz6+&VDXF5oKYWCCPr*^YLNQ{;86J}Hep)(Q(|eBfcc@j^!sQIj-2V3eQ~-=-!2uj^HswQk5C#ifqK~dx{odE4=Ew(Mz#TFQe+d zlzJxmA35PF^@gem#A=CBTCy>KNx>h0wR!m%1TIuk4pzmA&DET%d`uvK_me_kT>d`a z{i+Zk1K=9Gc<#TxG+Sh}YSI+%_5EH)UwAH$BRUUe# zIhu6kL0N!@%%}9{=X{Co7xbp@Gb>)5hm*jS1J>%yvciUD|QBxtnz0W$DQ`k9H<>@l}x zGojWS&A~-5MAps6-f&gDZMlHQ$J9j9H&93dEB%u6bjg9oNo{UuD{OiQ$zld`AlH1C z|6j-UH|_h!S1x!M+)CB_rFO`v20EC#bxIf|Tc{Jm^!Ro=P~9r2_}eYF&mnL%Ez@=M zd;5Q~0P2U&vb6$KOIe9R*Xyj-RF+nCE(Z+jCeq&7xd;{fKMgCD$50CP$AXwTR}beY z{uEYB!kLEnWOGRqKvSt>pDJro>^ZUDQGZb56F-udqJ(iNIviBZB;ta^8h#8In-?o{nsM zGiqE-WICH-XY9P;Z37-}QbtBy;IUCpHFQKsBxm(rSjej(uz41mjQ{Apr1Z&NC#A2a;&y&EClS?s}hzVv!Ogy-EWzOOfHck>N;J?53MBi+$1c_ zABaoeD&kVIm=0J(4b~2W332`nEAbZA9YD~w^H=vx^X|&T!ScE5+iBlwKR%i}&gPyIge6+$JHYCWTrH3x+lNornw%bhp;Y7v3^{qk(E7=x z@PmjaUaR^E(qGl=%<}pS=&(z;y-S<8vVl)J$N3sXWOyUn=(YJ3mUC=Iy7cY+xjAPN zy57S;h$&p1d#()4b&AKk_9EPEB#N+?zQj;+^ou_Q$dzngI)Wb%!iT|&P~SX+qF!LL zUe3!f5T{i!&B5lhkI!D8&*n^hg)KHhA0A+JYFWm$O_&k2x^hb^OB(LA!V}21G^T8K z4p6iOh%Z}BPQ}pS_Y(Pi)>X0Pi&oQaa&!9btPtkV{p;&ioxAMxt9dPTskW>di!;;^ z%gV6aRYUC8pwbtyGQ=-{NaF*uCSLrbA#bT#-)eM$rZN7gJ!Dvo!tz2MGzE*DSFJ6( z4z?OC(>nmMsf_wZAAU)UuV$b0R6&eY5HuZT_}?! z`++HJuU$PussJjB{hPx!K)NKY{qDtYX!)~fS<`@vn}8=ZV*GvD3W%>T4w&8ZmXMOz z{eHe4)*&ITocQP>znim40dhKKi^y5uV^t1xsx|M~&a0okFha8xT`%8|Iw4GLk0p-{ zWWi4iwcd5&qOnFxOS#0OChGn*J!APd5uybzPj`%(`9>uU8tg66(7WHbr+Ic?2kq+? z%y&WIRE$klIaK~a&*n@P_p~;H#^8o;1MSwodCxSxM(q7`*b3ubF0qVb5Y0`l9d6q< zH#}*c*lb#-#_1u>FhJz5M6kpUm1=53idV@y8|^B^QZC;`-5Xl!i`9sg5}|OPyuW^#7D!8mJEj(%RO1B7mTyrS zfCx{=^cSoH?lqc8X1`ASB2cD&o|zEBSViy$kz7#>evG}t3?sL62jo?I$$82S_Bj?K zp*Gd%aBI`FZc=fOQH%b6ra4gl|DA$zr@!b`%B!6@F8wm6GI@5~cR$~WvCvIcwL0M~ zGf3>5aFcQImWm>B@Wj*kE@rAkd%e-AQ5}*$+D_18e3DU5*zUl(B6o-Rx<}n${E$7B zgX~a$%X>?U_8pmwAS?P>`cW1j5DRBZM)e4EbAhfOxp;;%r6iw;)i_^_vkmBE7M3I< z^~_YDrGY>9%a&9dbo=IqcgM*!Dpu>Ac*aJoAPKh6LxZ**$;Q&&r`8?m{gm1jXVp?M zQ9$IsjB+2~K3x<*v^^?K0E!YGt@qiDhp_q_msb(%iD}x@q%|F#HPvl@ROE4-o=&$J zV7M#fA``pi%m>6{6((F4c_#__WA}6 zUyTKtcnJ@~Wvmc-Gh5xjj8I9@(F+MH-O0J{R7Ix~!ajIX*S5K4DHA=mL9xZ`=>-YE zCFdz{=tWMKX?TW^`v9{OCmp3mPR2XphltHlA1mNqqOsenC^m-EK`$bFE-$vw*snBc zIh9mRi*?F;DzvQs6YkturFAnG?g&45_n-NmT?Q=CG5O!xv`&GOm`QywsPlB;CgUqV z9!Wj_{gAJNUzZxV4>RFY-hdtJ8``SXBFiQLx5fiG&iNVY2|j+1&@3m!^+e1=I=DyW zq7R~WqJ>RK{4?EPduIRG&N*Rz458}Ko4UPD)lVSamcMr<$Y~49@a}U8y9kY z=rZ=g3_NZL8ueMBA9<+$@*b#KhnE+pTk{>u-2FYE1u9gC?&T{ zD55wqVcWx}u=NGF|2o$*WaCEz5~5$!Qq&edoy%>*OHDaEB;8?v_e5IO8G<(Kk0TO! z><;E10M@Yh^PM(@uaCNL=;L7yee5A0p6^XF2fVN#NG#_B@9WGeM zs&9_~-Z3??Cm0uv)JyE6*6WU15BXL?d-Y2DPB|5EKXVXdU4f7Am=9XGuTmHAk$?c0 z+YZKp{m)S9zS=A!usQE<`V|!rS084KBYK#mcCEzJ$sTS>RDeq+a;W$aZw)r7vWBCE zU*ZtwwvBNN!k^AMKLW}A7}I*uyJ=L*YV8L(5ILx| znE$HD7um4Gr0_0n&^V;wkYHzZL4}H-JoZ;$iHbaOvdFKTj zH#oBp<}&(lpwc9U==_ICDB6+?MOJ$2+?OSeoD#FA6TveMRw5VE3b@|mE&vn|m&Y?!^oa>##A6gTcuY&@2C? zrmrb|juVDZxt%h)(brliL;|6##z=HaHT;f4&7P3?cM3?aZ5W@v5MLjz|Jq8rJ zwo=f35^s-_=W==aLi289MwRYuJYXTdI`Phy;UtjG9Lz{w44Z~ElJ;puBGbvbxp1j%5I)>C<+EHfkLlnGnsqa)kwrP(>x4m~X96ENW z*&brZTNMQer2Z%q0M+Z{f9bSID+)+-hq^qV_?-nP|0fHeYJXD06m_@;3>qH$#)UMy z4n_|$cddw+@fOE1SIDh+sAa~fCTbN1joxA9(Sc^hhvZ(w0pcGNyQk$5v9ILh=op>x zgSW;#F{||9n;LvJA6RYY@ZQmdD4e}$mm)}CYw191YQd#10ymv`?g$^@(LPW3dwmnP zUz7-C$Le!s4CcOa=(ui3d6KE_H5Op8@iY)*(D*wEkkxd-xem_^|2P2!wO6oKlP=Rc ze&}0!c02WzKdGw91E?1wOUO0*0xS0wQzEn|J34r!VuV7yFD>)pTRQ_=f)|JX1{QX-O%~t z$>Wikzs^04AqTZ1uu<#tgu4Z6cF1M%MMJ8@IFzv&57`j6RJ9&m#juT8v6+AxTx%Rg z@l5O4EVC6oe&gIxdq+2qa6AMmOYpEwFpg7)CowRPH9R~&ROycAoDccq&n7lzE5X;n zYit@%trC?%vXw9(PJD+|%&11UZ$)s*^H{R?n6y9h7)x4V6^D%oK<9%m?8I$--am`Wxd zCia%dr15|i=>ogbM*Zx3_owpSMSvla1xnn0`L06-BV#TM^cHy_sg{QsjWa&4aJJ*G zozlsl#hE@et#YbyjXzpFNV6Q!_jfv(_(Z_UE^Q3-W)zfY>{PQks<$c+MQ+=iS~=#l zI|_^r^^O;cws?C@aA-ZwgjO$X2C$!)q%|0jAQ0=u|4N@OCgiW#4${wX$|rI{smVE88N zCVc2gG$9~gX6E#FLXFKBn57E?U*hBHHPL30 zJgh;nPb_>uy+s3N)#5>SXPQP4kJx4rg*%~75q>)H^ z>tZ%KR;fB^h?J&I@j}-daI+>{cKDM`;3S0Y>hy?6kaOgMmm6s4YMt&!AdeqoI|Mi-^n3a8p^Rz?t65ms z-X))Q-b}wCkHG}^Q1AM zBsY92CWE(pd)@){xre=OMfmtyiR=aH92~Cw=H>~ z|LlsFV22E7U3>JCj>I>%i*XWjgo9ZW|4OPONVP8^t|nlka}WZ*V|?Ge#~ZZ=q{JU* zgy;4AY;+!F_F8LpZEXn!lvrd~WU@`hWDdJjvcR)kW>m%U18ll*KVhgmZDct5!$4>VkIJ-?M3Hw`i~yk30Oz8nJ9x zyr0ejRkXV$hWU&niHeFRC-EM~Qmnx>jur>qL94%j@sT*A?N zcrs9t8i3NOH3R~`>rfBd=SpjaXciTLwHWL3?MaU~-Kyd*Rt_|~3zQc3ki~Y_IRayR zIXR0j%hA{_!m!`_nlAHXhCtJKmsNZp_v|j#r@5{qtgJ1Y^5P%l-_lK~kTzra3$Jd- zEvktf3Bu0E(0~NVBBp|VCA8m4bD8Mi%P8uu($n>B_QG6`<$^;vciWg`PODIL(M?zkEL z>^f+wj%fPHGz7ZyiMigM_pS3dTF+W&d$q(fisbo3t_TweUr}}jg+;A$sTVk0iMQD3 zq(@$@o8e_3V{L8eL+%-4mj06xXE<*ApfU?{j*ys=q0u?P`WG}(D(yuKfP{U9H4^JTtsWl2sBo_W(wPm^I)-+=$^$KO}qeCE6M7kM`|@}uUT z3##tuUY^+#bDLx~fG;@SSnN!U{Gj%QC|*t|2KOLyeU(>@Elki7~DvGXfae&6obvpHLnUg#q394b`IjPb}e zVVMr>cfPu_%(EeGqc_&WIAa<^=I3@+MNHe9ds``Yp%(h-Yy8txq8!V)rc8#E>uNBD ziWgpVypSyK)jo$ian^x@>)g5ja^i`lJ!9eWuXAaft{jerbhPV*rXS5+R{yuLek2;tezBbVg>L{{D z4pg41jkb{7=D+DZ<$IJ+9WW?9{-dgSCMC>^l6`FrkimWf^ocO9b`@>is=rR}BH#Xms@wU=nQg&me2|xF4x^rAjU0u%7 zqtrWU_uIm5RIakTn9E((wQ9YJV{~Ebpx;Dp z&=d|xV`#KZxk}BHddT}(WN$7f^4Sdh!0PazZc;jlu*Fku+*iG`lGp6+eYKt$`YG*Y zTaXIw1r5{_U2H`4j=Gd zLKVLGdwLq$G{R=|F<0s(ZfO;OXc;)&}cQxL0?qEj9vWG$gbY6UEmXRYm;T zpHb1(pZV#or<;>^mokDdsDY$%>av&HXWEzjr*f7qlUGW*7zH zbMauP9rv17VDW9JW#dl$*j{WhQ~jyQTRqf++ra9ij8ao$nbdaaJ$FA3&84j1ORa*Q z#)8E#a?a0PAgXWqE=lL7_RT-}zInS3l2Ckg7Fjxee*%9w2;s^e32gP7pYpz4lJ^|$ zZ!$&lmucs>v{@-gqtQuVEqe5+V=d*sJ^y!SE5kAfspZQlNU+4;aU zcJ=s+iHg*k;XIo4;dgK6T$^Hc8(5Kv792bAbR>q$1WfbGkWg=sG*$ssu%;@e98Z~g zp&(H^mYo@A)|h??ryy}--Nx?K&5x9U`B|uwL!N5epG-XU-i@!B?qH{U59)r>A=SX&I$-D4l2tanCCmXyo+ZVik5@<%O`*7PWV=QQfnOhaJzDjOVw(9M9`(ug0 z^j|-Q7E>X8XeLNM+R5N6daQzZYABFOx4)D(97kv3l6EXpU1V9%0MBXF2xW1po(Y|s z($2;RovfcC5GN+|tTA||8+^3*y*WRo4I46hOV0lG7&5{i%J<; z;q!=S4xaG|QEZ{=Be;@YTyOWH^8)$4FxvdNBJlG9k;h{VmTM)c*6ffSOP%qaTP{)_ zJxB7e8B6E#_&An0tz6VD*a)4L)@>qTz(Q0c=>Wew>d04YG8>sRH!@3G>qI~=xQ((? zr}2+EV4rqoKdO+EqZR6ByYzT#nsPLto=5FL zs--2J3lJVg2UhmB~fA`uHn|ulHba`9!TtpECsO z-P)k(l%Xbd=%=Lw^$6rdyFOB4IrqFfyHgQqxinCf*mxz)nrC>0V!T@G0~c?q0O!lH z(O0Kw)<6B$Vm3O#&Y?F2+CB%l80*v~tl`{D{tstg9Tio(wy&rNA|N6lAfX6ID=>5; zoze{|Jv1|PNGKB0-Q5h*T_QPj4K<+B-8sZ>gYQ}Ayyx}%*0+9tIBQ0j&9k4l}+^IRHC;*PO`*J06XU6WtUG-FKtNCbb=DpOA z#WvrR)j{HHGWaQp({h%jfXz_u&?3#;NSz~RC~V@nXi|*pyv83MaOc&7Hvqz~J0QsU z&RgI>O#)^IV89J<>gSm3z2u~JW(Sadk-blA?P&Qldm%xfb^KTU6@UgHGV%@Pt#n+HX3kGOS7ern4nNQ z>S$L|haw>i`m)HtULPeLE@-{Uj^Is(XnANk2nobiC<8uo(VS6BbKXzyjS`pVX(m`6 z6Sj}WbIE_;{{peM(kOi1aPgJzU_ooxP}C&|abl{@Njfha|KJTiRXj*eHW%|=SuOJD zr}W%}b_I6ElvLW;WurbsjtFVOmPn8RO<0<&^u3*H_xS#zN`9TwcRh$;`Dfe^h{3^drPyI4XtLa#}dekFC`HlZgFZ#LhQFKw-_CJv@$f0 zF11`GjbWf!*>%#-=x=46)AI^kt$0jfrs6bp*+=OS6c-T_@)YQGh0^b>^QfvS1QPg> z=J?hIY7TX4EB1AV2TmP9N*65;jy$WCJj!EL5^W^hYZ}ESu{#_zSMKVoE0b?(O!&Ws zJgi&F0UNg8TkDdYT~ef7Mn=9Sw-sd?3=oo+D-d>`#0JhrRs)cNFQ&5=)3bWn(nju>Qz zc0|t8G1@||$>5R3MCWELNvJIX#lTnwJLw}EsQ9#hmO~#9T{4z0ZQRSaEE-aVC0>>= zj@Tl=))?q^88ut%!?r0549EQYC76R!e!ll8D@Gx1M@(Ig?5?c25`UZo5;h;L*b; z{(pFMoQ(hm-*sSTl7yVjqlYPj!hRynyWGhV5v*rSpuH1b4$QA^9joZ(NXde^iA2wLO$<}LgRhQD`5 z4-wOHdEVsknBdJ>g6gjf8$PTYfer7MFmolX3s-B=9OiGQwdls>WF^hJB*qB{PhIB$ z4MYjJ@w+|%1Rz#BF6zYyt+}I(lN%-0Cw>$ZFK*b5Ry6kS3+k?wF%XC`A=Dpe^9AWxvBZ`W&4PP5BI9p zbJzW(A1YzrF+G69lGWvu@WgVoh5crDZS+IV7vhB;xKM0G1X@u%GJZtYlA(J!!5j`3 zvwt$kP&#ft^1cgU4KNMO;sd%O&(bID4y^KKILI(N0zkbka&mD|MKa;$!SF8CB zvw`~0r=jAs&)GFHMU%qC7x%(H?zs#NOC{8-g>)uj`xza6qKYO~KI7fE&b*XC3Wy!S zajRQ-Q=W_pdgTf(9+i`j`wh&+^zL<;Og{Mu_Mn1lRr`GUbbNFcdE~sgy+Z*>o6+vv z8#`8RRc6wBCLX7fL-@GoEm7xCwi+hYRG{bZeZZ6vj-2M0#3vPmKjb1L zTPn{&E!6n0J%Mkm*32GS&|89I9@HIWT!J8WQe~m@l@s%4`|4n$Uv6tsT?p%9T2Ap8 z`*-_iF7nQe$<~bc3QqG8qpZ*_0fhQUj|B-NZIo&~-q>mQW0O?iVNJf|kJ%Wt)S){G z>34=qezMjrMz*2m2Nimov=5FAx}VqGta1LvI@qIYym5IdE`!|tXjwOiIdWG}YvH&C zyT`+jNGGkS7%Wi{<>ox(VNfK%kk&)K)Xuma>*L?m{>?{qcvV$K--1jQk=$Pbf0oq* zZ4yY}Fc3>=XuO*kubeGZwznaTylec+o_D8s>x=e#IGo^%j7;EaTb4#kSxRwhhq58u zE27lgB;Ad}F=oxexzV=CQ{fhTT~D5)KKM^(^eu*Ir3dn*)#y6C_i}v{OnU-U95n&D zsoN3L!&ZiZ%~5|9>OXO5A3n>5bq_}{8t8$JzLaen{l+)kOqH7chgWk zK1|B+dPef{*K9djFf2j?l=I^Si*c=d_1reydrNf$I^=@W=1a4TXByvOoIjY!hXfMx zK~1W^+hL6tzR(X^DR4!#XC`bxlSZ0WCQ(zDCS;!pR&T`K!t5%2JBukLkVKCPp`L>I zowVnz{fIy7Zc;iO#HV9*J;#e{v}ig5veFXTANwn8yZZ#ALB1bJ3{uyy9X2BFl5oRt z(UYxXbOs$Qmc+9b8e9{bruLnR5+Q>!SWHKLm7b=fX(Rz&VpBVAF?`G9DN5)a$$%x=bN>IY7d8mLg4 zX-d0X#(b%rX8W<63QwIr9FhZ@iF%Qv)F&B1FJ^f}f7@QJPx#8qZUedLXGgZa-!2!x z(rkGB%^Zen@ycD|M3nMv^tuvgW9GY}WOnEKs7XvyqLyOWKV#Sc}#&hy9K~?7iMFqi+<)rcnhJ!p77VP^C)YOYZ1T zWvHs~&Y?T~gFz>@fgAH*thU{clF{wnAj6lEBbW_M$4GkbEvQtqIX-RBzgz-sE)}tE znxLcv`PFH`f}XiyW+xWzNV@T6x?H+(me#~wH=l;49I3`p=1s|15AeO3IBy;uhrJ-oUm8Tsny%rVPIjHayctrbnnmZ`EZq@^ER z2EPxrhuiH=S|0R4^ZGd*#?ksy-WI=SSo)CH3#8jkM44%tTQnEyib??Okt$M_!Igk+p zrXTEQN!GK4t*sIge6{0m?Dq1m)=e^`u07T2HEN;RGk?azTn2TWbrMXhP(D?dG~hl{ zM7Pn@{Z8+cue%YxE7!k2>hE^WMg17MC#$#eM!mKpGG>AIVJ4hGhVdx%I6p<>;6!&? z&lyOxteE2#A0x}`pK*`w*Oj;@g{Mb3xyd0C^^YAI&Pq-#C}TYQ90Qpd6dd!7Z%+?g zuSTgjAoD1+UCRIcyjVlhn^h6`ar_3@#>WRWgu!vhq;{j2r1swD<+_@dNx6gmG-d9C z4smK{9fe1ll=@b1DTxBqFyo3AUY5;8IRO4FEC$#H>PFqGpkFD9xS!O)RGF2u`#RDT z6k!mLe7(Kg*A8f*-9x)!@mL)rQR*LGRi>ozks^6L7P(Ol2G+iQS zCn2Sl$G8cG-eP9fy<=$$-R^Qe_VL)sO98?RLE}*vm8;+L3u}}^{fY&XMnt@jE{R(xfh8(U`5{}6fZ}{dPC}htXm2O~@~;of z4`V@P(u&p>jk@BpQ--Ls3X*_gJ3mB;=ACbGasw>WZYr>Ig?faH;jvRvIikz;&(E)y zM?=nN(C=}Om+-BxW*Y^~8(vsW9Nw;ZSAWhEfsmUJ{Js_AN9N7rMyga0P5&YuQuxMb z??y;9Q^$N1vG!}u?Mcvvb*s#VEaK)|6?La-@%U^8jLJOwptNnMxL-rpaU*%ou0(3| z5%PIq!bj1;@^^=q(ecIDAK4V=mlp2M&)^m^j2v$kX$RW1*ZTO?t7J3AKY<})!1Gfd zU704z-*r(R^s5C)NlQg_3XFNZsrA>7FxJJ`C~$Utji@b}3{NXj>T5;us2G#n7;*TH zMs+h@b@&5T(Kgxm`WOx9<;vV$l8@%GW~a_e{ba0=ZtU(7#P+z6ti{*3kFPLr#$KilQC~qqH_*7{D96`t{;XF(j>Aa#*e0!#zxEt=)qosy;%xnIGbM=@EJr_c zn5}O)S=Y?H6ZtylzSegdO}ZVSkP-%@G0%V`jmx2UE6UoHL$1nfD$nBU%+_Mel!+6o zfXxT)l@p#{#ydCf;zwkyfp^5sc6+Q&HHY%0p>QIc)gdc-USBG}hwUjUa_;p@oyNr- zo8nK+7FSXcX$i2LCsr1@A66;Q&M_P%n{g)=7T)`ry*q-XF42u2quPs+Qvd|vamJi% zV3Ov{*^CQ087DC4ML~AU2a-pREy}Qdv1y+%`w9g+#iagX=xSNDnKUPXCoOPYvO$6ZdPFivfBXA0nUoVkd@_g2cPAtun z;d?{W_ZFjcYV5iw3UC3kcI^G$mOf&j-H{Ww&*{9%l6BQH!P0N<8u(h_UztQ;9X?)* zkfJjQDya%$WbW6~52w?B7%vQA540V|ojyYWMf)R_O0Uvh{gTRZCOQb`jxXLCx zbWZ000dHsWE*2KrIHeb(9%TZ{-8|Mc7 z%{;O6oje>tQCA+Y!z=V>*6+)Q;cS%+(Xt~jp{He2A=eAXON86Suw%n)vgK23`Z#se zOHyUE=k;Y%Hn8{QGAcR6sD~Lfulm;CTe+LmoYl0~$9G?|L%goV7C#or6Vb#*+}gIs z0Q`}h6Np8PFS0Y*Nq}_O93x&weku`LV!oX-M(c3RA){f<%V_Qvbf)&yzKoIc1l(p` zq>~iao2OMDY}C^Woe$W{(3P}V^biH-2e7P&jl2R-Jp=UjY<=Dh-A5Kwkfq8$ zcSn@@FGHuIB_^?rp1z z`9_?mA?&x^zud)b#0{{-ethvD)W+2}ICZ4E6zhN>96Ea*(@T_zc`F{s3I4VgSB~@F z@0M~tC`Z}o&Y}(`%7~Fki|kV)^iKh;#R$I2-qW}y+X>jOI-D7p9pvQKiP27|hQd*7 z++FZXUv#lpkS=Njq*yFhaQBI%WOP2>9j1tN?mGd~!?1Kb1N_zOHB+t8mco_ziLsK6 zFEQCHNETc-!{kUw{i9+G^3_+y9T)2yOKVO(MX~rj4YheaMO_%4p3IZ%c#+D@32G7u z`js>*2Y<4I1|m+b#c7w&7;w`O0%!2XZI)iSiXACi*M*&agwqN~YsczoJk7S!m2GF% zTA3*X!E{uN_pOqD+Hyg+_jYgDZQWgzVxFzfNQQvl>b+em=^yJn3OwY2rYu5pJ~TL9 zOg|OPi50wT-lB8g;#{|;Xig*Ilz}W74Kx+=JO`NH>BC1zzjV|a)9ZPQV*%*l#RPk? z-wZ}+#Rucm*pgo#>Ms%UC$8?x9D|d{a`{+uP5tw`<-JdY6s@dEiJUexXc>(g26lCb zE$j9~-*5}lTFo?;c_?Oap%f-5Yl){Uhbgi3wHp}Tz+8;FY7DTGQU~oC@Wgg&Hu`p} zGia59q$Zm4nJ(~4d0Nu&$2L)My6~49x!$IN+?d39ip0rt+-4&Pq zRgAhD5T@C+Q{u<78Nmfd(Vi(?C0#M8R-9rjJdK%XSYaRMGF>oa&!Y+XLW|rgFxB`aq@D*0Afo{$5!DVNr*?^D}9!Q4Gs>psGGi+i9wlarJL~LD;8V zIsKBCQfp$jm=h;i8nh)dx)-AYDk*rVU$yr5d@P^6%GFF-zb2=x^_*U?fv@3HAB^rv zhwaRA(wc!vJD;f0E76uNR>_rU>sess6Z>?WiM3Prv?j7Qs)3V-W=HM`no@8;szsyT zRQny0h-Mqgo8i) z0@tF#3lgHE3*sY7&O5BcS)O#8`M=oBSVezyv31{R;wZDJJ7!nf9Xl0l>=)iLj z^fNl_12xEPqq5e{vaD3T^_BbiO7U3t_Y*VPZ3x>jKywb06-Pwz3+ggmYMblPwOQS$ zHBF7ug0Ojfl`C7-=e8dtwu+J)4QdY+VP%ZK42qlg%)?fz2z1O*dX^wJIc#?pUodMo`raB zAS`!>_Irs}L?F+aj-p0=NHQiIQ*n$PF4Z1to~~O5p<7oz;h5baUiA)Q{#u(C8<|FW z$a?}1@ZGT#RT>UC5vajY(W8X7oL1*`(tz}4h^*8>MVn5GGGrldwrvAM7z<>?p~8r< z$RRN{a*UO_*a@^yWx^Ny>Serg+NJu@8pK67o^^SWb=8<09##9=pe4lCrA^jZ z_41=|ENUH2l}o>& ziv1B?745(Iw99mOHfYO*BkYvJi=h#$;_47>WDta|w(KZTJ=NEmlbI!E^h|D8E+l%yrPacI`RR@F*3Zvj^{cUwdGnn`P=mE0Ki&J=l?gQ~K`O?GIZD zOtfzF)T+Dl-PASqOW`9UY<8zT#9cA8RgYL=J38+x0s!)y0=D?T4ZNc6Fikl&dkO4h zA{Aro%U7$$vPtD;Kv1%j1{P1Mx)`@Q%TaTXyG(O)tdrhdrjBCU53#!hn=bBqMQ~_X zaf~v~%?gaYhLqVFb6oa3uNB%(nOY?=$^(EQ78eB_hFRu)wQ$w`CP3mvwf085bbal> z+LhMwYk-={<5?t6LHjfiakc@hVR%s=v3mJ;mnIlUq%zLsDC{{^S@39PM(y-Wtv8_k zO0sX?x~vdI*Nmjaal{-?Jaz->zVY(GX55R!aNN)6_#6E3&+CGpcQpd6q?ZUa))HG| zNWrov-kmRkpa(F`ig$4jAz$4jysyMh`)aMKt5cWn0PV5uDouYA?x}Y6gcroeLwqvX zV_foIS^$6%!()ou1fMbz-k0T#;{ymCfR&M#YZ3)v2o*p+mTquCq&YSOpYmYB0w4J2 zC}o+yzPU1#8T36^!+<0*lsNAD+18s|{jkSPTyoS&r0MW3(kcWN^~apXa=YjYq^Rc} zXnEz7(8%a2zV(FdZ@u+X$n4z7RH1TG_N>BM=ufheh3b9P`^rgZtMw5^A4>ZGNBo*5 z|NBelAlj|QSxO3x+eFn3C4lm0GnRX%i3M9RZ}i9-4y};KrkV#p9O9fF9oKP5w`|oa zA4|Q(el1*m8(4t&j|1~)rsr%a)+>)K9Qyzd0>(o5Kg+f1Ad0)D1TCE%*UDUyxNUccxqI3hqq6OD8&dGY*Q^%sG zA-Y;^qsR46AiwFEMPX@%PZb;w!L^qDIr1_t0F2 zbQvkeCigwo^V4oSaIBPM!;aF*NjUT&=gePqCsQNc4h`n)FOPCBEu#^hWfywGx6bj$ zok2rFFj87Zds!z(Ym_wWFZ83){Q6*@xoh#?`V~P+_WOk1KqK-M5@+jh1c;DU>s;!E z03(ZAu)dgO!C0t$x$!+jSLC}pd(so{PBP#5qYc%&yiypIuIJ-J7Up}=NBlbMI&J9q zq6~Vmu&B21rK=bNH{y*R4D47aZ(fA7*IA8bu`ulVYR*$KyOaP8Ua*4t?0*?tm`@92 zFgeyd2@&Ho_vCS{iew3!9e=V(18zdUTQnYKz_$iDErO*~xl`I$PYf zD~0o%W>#_B9OSQVViDCeMlMkGAzf95d|g8m^7HxEIHURQLVpmMjJ#W8nv?v#t(s5) z58uv7Jo}SV_XmRP9EL4+4r3(8To$Y1gCBV6vhkOR5G+P2cxH-Swd_@hrCJMD?;wP3X+*+VZhO1{^sH4N%g)E z(MX=c9FdB+qhD-n+_AQ`cBKH@(t!@#5vZGuQq&pF!|h$IxYQ^eDb~=~oyQrZkLwjX z;8eXORv7P*-EF+R^-BJz=eG_0cj|KeZYif3C=H)+=r;}fp;*M z@|g_&(8yDKwo#j8g+cMkGGfjpyjWS6wys!|nyM>0riQI|waGjNswnL+^9Tj+_OUxc zG05sdsj96o9EZ!bs@1SL!N#OpNdTpJp0*XC2$F-0zg0=JY+J4maXSUR_>x}%e zyP3kv$a48x$Hk}83dcLI)=e0At!9nX6htl!uF17q%e)*5bXyyjUvkXVR%&#s_UDpo zx-2;joxa$#gKs7`l-BYMbMF&~)i#uQ7@g93bn}%iP&^so{RPf*Pw24gy3bllcYr_KC-6b7uR*pgNG*OMD$l4*?i||swR0ncAQE4$@D!T&iI*; z{KWnG%zzQ*n3BSs3G9>CO8N7BOzEmhc1pu?U$t=(*`>=@FPSyDiX)j_IF8fp^1Orb zy|`EOL_uRzK%UdHCm-jQ%{W8La$YS~Nx?@aS3pRR^yH}Y#lwJ|T{qFRST@vvfxkxt zhveDVd!WJm^=p9 zsH9OhKakmbh*jU9E%@~cd!O6SY1qE=Vn`&zpAwMy*It3*9W^t}WKNzf&tu{UoAk*~ zoucTXtRAEse}*n#VOyL4dZ0n9mprmBVd;X z6oB*}kQ%d1@tfD(00fr9*bP>beq|2Qmr{#SQsv)Xhm4nxe&GZFbTV<4a-gsImtU+4 z{sR`rW0c8O#Vi%r4|j?sGkk{b5mKBt!0p_A$4d+gpBC%6#eJTzkGIGTSA6zDjD_Ci zQE||TxWBG2O9}w}QCmb4wEMe){qRMHbLQ$R-BQ&~XPc_wK!phd+_x0!4E1N6aADMq zIn^yUljT*98Tt@ZH>3ZS#OBv*%Lf=Jb4gJ3jX*-#lw`B%SdX{BK3Bm#g40Rs{>rbi zso7Ek9{Ny84^7)<$?%#ZjnXHA&`FKDV*QfO?D;ycS0fo10r}d#if|jJL(XkYYFJuh=NrC!rxnnB+(XbhZaXHQuC|z+nS&1U7W-%CJS)3dX7KG2O^)zgEOyJ zOW{M7f)MFultIL`>AppI9SY3C*;!a?kcS?_IgKI*wKSLhBo+RwN|666jbs? zXDQB1qb!>vf<}Q z=g`lWs`Dk+agSOWuot>%W|p$qABf&!?TObsJnV7P@C(V292U0M)m#=|jIqv(uv}bX z_ehuCii_6yOJZTk9kV4F+BZk2O`btIc<%Gk?XCw<5OM!_rv9G)R%{DmG}4b(?Qow9 z%6ApYcFIsj3J1e&ilUVv^$-tsEcYPk1tAw*!X z<$lNE6KwfGN`kWNgxv}yzlla)e5t(lrSGe#{w6_em-t~oWz7=l;Msak0gI%FA$!?y z_*cbMqU%Pd|D(A&WPZ`(F`is=%CsA!l3m#{r|`&}>Ul#B~sa*pOx zvbp47e=aXr|5b*`(G{ax5uTtO9f7byac|MVeJsC*Rl6jpVM1cT_XS}2qH-I#GFI_QhoaA+3^8lUq8_QL z2R9l908~i&DAu!V@UET?XNms)kO;hR7f^6?cHpwJl9f#vQ1$kCN+bl>L5GLhd0>F1 z?g;fT+;DCNpVe~3>RVti=D>`TmEH%cp6x$s@^EsNfzM%6tzIDbmUb(u`dhx;FhNU zw(Apj1I~^i_Wt==?CY&-f@VH$T;QA)=;Sqm9es#ufzd}hr1$F=q&;GIJUuCw;bkw^ zgo0qRSSiZJ+fGuGU=bxY3dZ094}1Ddr-R7Qzg|-?z^yqs{ro%Y^mk}B?lD?zs_MzMjge^G2h$qWzqZG3nbz-=&&%^Rp_KqV z;(~UjqN5pX-U;Miw(j+pUM!d8VtOT*-;dAI+U=yym4pw-0wThdlol4NqPx81B={Y3 zNPVF&)q)$2h3eAfry|B2bwXi()o{Eb3sxhq-s#N(xU%#G8&Ir>4}Ia}XS0OYoQidp zZ9wJM=k$};oMc=4@$)sMAGdg1E&$}_MG}cvtllIH%_li^$=ajl{7!<~w!ynnpkP3c zV@vSo;o!%0F68mWLhF|-tCMfS2P@g7qFmnC?72cdSlK&1~SLrM#FZ$8>zOoBxd(xHd)62>hd@}iCeKV zwPhah2)%Qof-9-lLUp0{*!0Y2-5-mEL|W3lr62pCS^dYSoWFcd8|-+Ko9AGbYHBl$ z3`J63-EGOQo$udELjDZ`hf<>nGVxNfxK#1oG9~?8sNMjMEHSVu*aRkHJmJAvpP4ib;`fr$MG4 zywq-XN)9)Np+^NsmgS3OnMJ)5cQ|t)2=rxo(PGX#=!jd};@AlVWr_Hol^$t?_h@>) zHU_vrLBSjD6A*{FoVFad^c2c_=6;N(7tsUf+8$|h7rWC zS5VutGD|P2YOzkgz&Bk|dcP5791mVYy}8BDxd~4+D7FT`HirtCPkS4}v)6W%8A!8j zG27n$w}Ug}^cv1HnGXa&=ly-AZ0c+u;$rkq<@s-PcsQAvnDPlCvQ+~~sK1&`s0$I9 zfUr_E;oYU%6Y4=>zdh@}cfr-W+YB!~+SQ0r5zuIeeiKiBHPixSz(Qr45Yi388I^I- zQxZeJZPAB)>Mfj{Ym2YoUL)S9Tg}-?-lL%0R7-GAW=)IwIgniY=e_xcF^wT+kN*g- z2Re9<~_Aai@lnDCgepWrO#HJCV@xU$5;Cs2Zirm+`&o}#s*-@a{h+@fgIdj z&M6o^zf8lZAM8(8)1I(rr_;Uc1txPvA&YbHM|`CVJzoqZMR-5~FTGzw@WP|* zZ?6-|c(}qovgev6(XlK|Q&Wlk>k9wj>pF4J{wn=nS!zNGSs zPMmZ4=HC1p#F-)-gD)>?BOf7ajWKHqEXC+_+4U6>_V~VY6?0Zh-CYTeBlhl-Bh;w* zFohxOR0pJ2s}~O~&uaFhc<7e|j-N*xtZtJua8YQV8z9_bRRQV+xa_)LF*wq61FjX* z85#!ED8hepUb{~-G)k!6@O;rWoSL1dP1k8sufM+@GU_*f=osV5-L|W7z78gMa87PB zHMYUc9Me$WS^jX~^59E!?E)65u3W$rLx`u9Zi`e%o<`}1vmG5QI=vUgQ*e{x>dZ)$ zJcM=dMm%B|DC7rt{{HQXX@~)V)82&7mxUqpEZI7Y!r-h~xmqjy0OJGQO%aJJOU4`y%auP;UY07plpK9We zDG&m^f>zht?kz;IitS0&ygSK~ib@xE-VKVh?8iBBO$!lbLz=bomQ^k+9P-U#onwEy z-VXnD?Wd2pem4M^8-M=-jSIrI_C968T5rtD4M2UUFy+-alVZrY%zWM(1t9610rgH`W-MB`Grz8g>*l3+0Au^i$)k4;L z{yn)VHBL9am2!5Hmk2vi7l@(l$${ufX9jIUtEti$GZch&LAJ0T{hBXWqnvwF;QmGc7_)l ziO_q*{*ih6?yXWciFC%PWXkx^9%fA49dG9>;jCvv)pmGP+!JN`IgZ=9);dQ#fET-i zfIazq6=6Qm16KUon#|UJtqSl_NaGH>a+)#YeT(>?3_!4>Q7pIg&)))$cd`%y5#5>2 z)WK+d1}F&(ovO5mKMnT60WlyvShSCHK)jwbjSd2UBRFZSip}zR? z(S_Tc1_5Q+i7J6ybuE*HD~l(9Rt#5@^3y2vw-2GLz7ZD(#+5m$N@8f`^2%?Gq|L~5 z+c}f>(`tnEc@_%1xU5-)8uONEp+Z!a?h&a~RepgDo{tg4`BigOuiVCzHw3W&A~)hYu>=ZegyxbV)m~{t-GJ+9a6x#EI-mAk(Y` zG+~?}`{z0e;SgRGt)vHXd2Q@|!rSe-^~G*G=@sNpA?W&x?WK58I{=%P^UEe66R56w z+xdyCiAfmd(&pyl_(P0DjsDqXO;Pf4N()C(z_#VI-idLGo@UcgwHfE{YHB7jmNbI< z0?7^V-0B}tpX&EZ&wkN>>vaYHlG57R=RrD_Kh1YA7d`9OdBeYq-K9n})*N5f0?zD7Yn8ii9<$ z{PJs;{(0v7&BLC=f&+l3PmF)+O@Yl8J_WADJS=*aPVhuzwA}oAbxIY97w??w2*?|X zmf%smc2uW4+~1^D=yX<^FYI}7z*ckT=6KcJZ?r_;y+esI`f3X2bi)Bldk&b zf&vG#wE6pqtKt5BZ#t=pyBx#x#V9Awd;%buDHm%;8MQjvZILVMo$6@nLipt6I2rn9 zom4z@D)l|IJepMx9wMLA1DywQVEqlSn>c_F^>IQv6u%NZ)c2Xqe9R0}=9`5doZ-a) z1;Ok=*LUE)>{aW34zLdJh7Ee@yk{qTNeRk0$)!#y_4{*#6CT@{>_u8C0`p5yrF z(T_ddXA52M0unW?oh3xnU1|lG&QO&Mw5H0PI!9*Lj>#Ln+6YjSkk?l%?D z0M4T0RzBlxhx{W~xw6u$jKxg5F0*<{-bBMN{L;LBATbXVQ1I*OG*L~C(wW_c zsnWk`#vmNongqILlx}I1ntljYW7)-Jgt*?+PyIYK$IX0(W*imcrf#H5e|DjB6S%Ey zdZ30#{Z+8UKVvbG7pcT|E_v;9ObOeO5)sc8n8Tt$0dN3ik@OW+Yy8~}q)bkNtEu3W2K#H1+DV?i`~R4$z= z5FVQVmhlN_QGbY8->61Ne7UFQKV`Q+4T5Gk0&ZX)V9pl0+p}5;Z$AoN5>3IN41IO8 z$YTTum5JA$zc#^t@$gya1BYRt!-Dva5KZl?=0Oaxw7%*Y*TeD(x(8G!zY*^H!>xKh z8c^Cy0x&||{m~%5s}hOMMx#+c;S?!9N0{ZRNmNi(KdooSt?r6zH=lUje&_GW1>k2F z@0ot>yaj+!`fB7o(^z+PiuzSY7xLssS&i;X$Y*wLQb~o!yNF9a>SF-f9AT_MBY>=a zxocSb4k1vGpi_Mv_wvqa|I8bDlUqR_%}6;bIHQs;7W#l;q>^~0_$1Uhd-Sb&3)A-1lBjs9yq1`N~1tr0i^Sp*@5GIV;I*sYoy3{OTI+eC!33 z)eWt|LA$7Y(U!#IH2ZzmLu4JPgaD z1DWBS81gTJ_-}~i_EVc@bgJh3msN9d2oMVcz?;nilz~u$%xX+HP%*eZ&UyFWzx^NY zLhhMJB4y%8@?LaO-&7_}bJvn89%Rv*$`nnI|1B}{N z38+3$G#?_my3pV6;)jv}pnEWT1xYT*=w*GC1QcxW^!G6rwo@01$3S|Wdj<)~jdDkmU# z^sbtd{paDOH1)E)s~`=CS77~3`BG-iC>VM;ivlwh*HzX7@2T*U>!+~Om2=L0Z-j8S zr~LCRY5ourDV0rZ>KN~=x1GgQs8Y%lA~(}6@Ga3iDMiOq!QRR2XaQi-rc0SIM=^4|F3Jx_0WR-Q<;{j)G>$aClurYu z?Yy)9^$(a^rvO^KSHZ#OJEe02bO;}MpdZcjV5K)BGMX~JrYqmwNncdDtWIq~=%2H2 z!)s6KD&S?DaFC7Ol6-(o$sHTZ2na=}6|BPQo$aJno}Fas?3`UG-pX_JWV;EeZk?X& zpVbiEe$dJHr<9AyJDD~{vP8rgDA96Lf@ye|DP`pXg!gwzUom*iZxu+-blfKTRS4E3 zF!gL{<}uZk-RgS}tX1EfAH3)v5Qoqq;pZtkS(vk>ROek{pgbHfArE#v{_0%xrz3v# ze?PwAUjc@!Caqx*&Kau8`k*bnqi?r<1_O@M>k04-Q%M{r$@_T!_bM%zgZ$LMGSTlE_EDgnw&LrAg{2~_mfVmzU_W}a(p#_lk~u6i0q%6=ZC5R zpf=nEYP1(rr1KHV!(^A^09NdYAR*rL!};5v_~!s5`Jh^>^)II${Q}r=X^Y;6l-E;c zC<@GA=}*zOlK(ikHa{`fd8417(^`g_?V#8odvL#s=+DniSV!*C{%$q%Unl+FA5+m7 z9G*)9iWb>dV?h=SZTA*$-n$wSCFzyZ!KSz;D)QeB)(cCW(nH&O`eep#A|A%19*KIOWdI9YC{6b1)XFnWQvXQSw3_lhLpd;N@Fm9_>6zBE-Fr42j(Yb?Ws~y}WXU!Z1#2+i03zNo13xMo!YG%%N zwh9hZe-4oi=Zo9M?Y>qCKKh9ce9LQ36rjL!+0m|qw@4!61a$vn*tSZJ`Os4SYBW*(R%_I5gp^&G z>y&yp9_PrtUZIsc*DE!Df?oDru3%|mccQ=vPnP8Q;0o};t z|9unM)i20FoC?4+$40++ z0H&@c-FAh&qYr8G+RDj3Z{>tyc2*L=>TE_i@BTg|jR4peXWd8ehuKKI0}=6P5Ad-w z^HCbAbdc&wVpxW8{nJGJ=hXb*BRmGQV$PduP}r8uHWak*GuWiScbz-_Inc`|{2a3@ zd+G8*2P+RC{?{70ue+^L+5@<7myR{;kMY(&Ht*jykkI&@-x}*h*IIeQI1xM7wR^i4 z`n*8~cm8C>3nDvKi9L{E^i=leODq(J= z?=;pz52Z}5N0W9H=9DMz|Nq=TvGYntFDrn7J|*twSo1_*UB1-yglO=$QZ&wd7 z{Qq2B{K~8W;Fp@(rtEzeSrD8sT$82z{;8Y%wf*RP0vKS{j<|xq+n)c!umPK}aJ`TF z<#nzY6TXmzzlyl{QaUiNFA#S{$u7FsgZ|*f|FWI`xh?-@L`qezLNB|iI)>MmkSQx` zU17b(tl+6uZk+2~f_1eUMcjTbbe} z>$#Z${=kk^JMsT%rvH9hiRx&>F`F@75uDusvf79If9!pASd{C!{t^{MN)bgsz@Ss< z2BlNFVMMx-?hpZy&XMkJhK2zY=@`1ZYXIpQ_Ce8x&%gV7IexChOW@tbFl{|Bg!K*Ps-T$Hz{Z=pEd zxo3*kG$mB*|0YwE!Ba*6o#)!iN1mkb_bhmp>yBilN1c~RIhGgrOeSr&4)yd8IxV_6 z^6d5bcK-ul{~d06WTM@soHL^N-{8C}{s2}~d7p~^A5fPcxOK(|_y~uwkI#tz0~YZk z*EFO61p5B0;eSB=esVU47~mrk%KukrvxpLC?+CHS{5Q4m=MVYU?1;q}3K;@?w;NMu5M;?D?kiRp?-x=iJ*ed^5GRVKN6n|%pU(h)~H2-ghhQ}Sx z`C}nQcWWPoTs0y3AWWvd_nd!7DIR)2N0hd|_s?!f{42U`Nn2#7p5anySbtXa?E~(8 z@CdcmJv@3QbF-_2zh>-5GWvj+5rI!f^v@jvzlJrx9xl5BEl9X~f*QSCcO?G2+K(q) ztH@n1O}qQo2baM@e)Ltw;6IZ6zgAm;CSm*IB0Gz6jbpXBdVIl+E+@p-1RUWc(ZwVF zXA-}tE!6x#m+Th*a_Fx=?~hUS>k}i@j2UMXlLOthhT&mOOq^cHCo!_`f1cd({X-%m z0X;>JOrrkZ|4bR9Yj)|&?HmlG?3g&R0aRXUt#nWV1p++skG~2ii^x*|k7IWQfB6Rg zY!v{Jar?1nYL$1N#SXO?jNXzn_vn+_PQO5Wn)p4E7;gDLNG-v@c?4hQquyWn_eC6! zikrB&k*$k`@{qbp1(zplWxBM{dvZhp~^{t%J>^fg9!H$-sMER@wBaKfvV zZ26AA%}V!Gy9Jg!K>3FQ{tN)E@QW|X9|YTf9*EDVo|!DZz~`{e?N^euMOj?F^A<;y zra^a0;g6Q!Jm4y$HW1CnZDMm z-K)xwcn`0q`u_2i^1zUY{hwM?|LPSS18~eOENB0(oKE@1MVnKmM!6 z%>}&s9qEOn;&R48jWM>yCmnn`b7Zt5B(H$Ig5z2H<;^!Bd*&#TrFwyBqld8}TMLRQ zquJfG_IoCj^982VSKJh#Zb6cl`7&t^==EnGvAn+aKUnC`C}d<$8mFcy-OV}YEF(PK z6c@KzYD6L1kFXQa1c1?jdn4dbivIHvI>_CAGK#`hAI2mpBFFg*;ND+yPc0YmcBPpm zd$!hzG>jT-zr1^owy;cSG<2IIwNIK;B5*XuOgWQY;Q zx&5oy>;3%A*rwzJ??t~SmWGc&CR7?QL>ig@F^&F3Xhgo8fu!Hi-5VZ{>)!8*9d8}f z{6gb>BV4dy{QBN?S6A6-+0}7Ig}UkrFNN$mIFsI40(FwA=~$2^wRaPlw66-o$aIwE z`$vMWG{u^c!`2SvNOc>^8*u5a`KvPb$0(r|ef>EK1OMazHS-mTwQ4{>WzyU0fgvqV zJUmoAgXW{eoa{XN^;7B1pQO?Uv5vFs-+4y6y?5BQ^Z5(HXKi`_QYTqkU3Kkxi00SZ z4Wni_OMALfNP*t%{7i1Ud^TV-wx+Qr_dH>t?;Ze*yCJ13%}JMDMl zUufFELsbZuM7N2$^4c(#$M+MOe@$!uuwH_s>w=+ihjQ=IbQd`GvhX$-&=WVP%e>B> z0h@^7uBdcJB)XOsouhXXy~w6=*+d} z&wD>KGyO>3QUSRz)X_lDM4En%rvDMR3g(`j^WHmcVG75J8+~8cZkhOno|4>ZJNLedbR9mO3$@* z=qx1f64vC2T)&og3yHZV4$g(;yVeKxz=fNEkt12J<>6TY$PFdq2>1!JxlOtxf2?6r z7`h9TT1pr;rhlju@tEg{-q?&%q{fHUfANI+o?$qlHN0b_^#yzz5wqbnV;$GwYfAdm zOF8Eqi$6&~n-?~avj~UQ*{inw{A}2i{~|jmD0$ui zncjiAbg4S7?L2S{=|s^wy~7a^$)Jw)5F8P}olUer5n6UNO|vluXGM9?Wch1@1Vhq` zH}w|mw1pSYVRP1GWuMZp&yzIE{zox-M$ThzNA0#NaqmZpI*58OV`7I1+oDKdzrr>^ zGxUkA;T6ZhBzP`wzNQKy2Ti|Zp1*f#-Tloo9Jz~Cf8Qo9D*Rr8KJIa(^?4HZoD$}r zwr8=g$p6Xo*DsN4N%d@L9lo>Lr;xc{@DUx1Us3*c6z?9j7C%f(d6SzOt)@g*-Z+R9 z4m+Oq-#od&wfM!(R&OmtNseKpe(nbBCba_p)u-hTTZ^yX9mL2ZSIkuiCGIj7D-+*F zsY8|Pnhv9il`GMkKYutH7c>8?Up#-!jq&(o$w~_R;g%)?$n_{M@`libW;+cnE4L7) zQ!EGf(EUzLa)67&4`if`Q!ll5NBrP%I#<-#%}q{ zmde|dcRdF>xsRWJ3vq!^OR>UX&k+F@BrBBAm|@n*Ak>2EL5ug>-Q7mX5^3KOe|0c^ zMKH0qxUJ$BoV|E*W#IDF{5L!Z#E<=jSm-yt4NDG$#pV_zRK4i!JkG~k|2*z^DW2(d z(>uZoq0-Cgc!hLMPqg;xsS?PN&|WT)6BR=jcnvhG8v`B-iy#^SNbM}IRn*NXOM1#E zEjm{0qD~%2u02SWBW+8yl}12D_NK$Q_;eesja};zQ1i)Blsw>kU5#EYPdY^0w2L=* z!YFP8EsD8m`w0%BMP%&1YiFi}H}tw38!x^%wo{BTeZ^(Ix2|15d_^gJj%mQ5D}lCZ zHh(`XTv<1(PRZ$fY{|-b+Q(;I{?Z`bz=p&CUAB5kKfa<&+x2s4&Lh-oGO5X*R7K*VrJ&6xbWeATIqXt; z%&Z-k%ge2VGM&uVw+4ckkKPOx=#>A4fzV(nP}hv{z9H7w)4=GMFe4Ln5RU^Lj$0c_J_g1E^IfS0Jvalz%f+`?QvFr=d)tj&y3&J^!xfd7}Yj7tX@vV z)xhpgL?mdww$CiEV4dNhq4bVdeyd?c>nhRQ zhr7;i^!Q^r&Ed{lp?7!AU^NSwUj^(=%>eQ=Y=;ImnSYvQ<`rY^S{~Y6rxi(FcvdU| zutS3EbBlPbE|ZRk7jzQ@g6W=*PoM+*x0n|Du(TQHhsudBUe==Cl8T0?-?xA)PCznI~8Sg+I?2SI+_1Mk>Uoxb}*yaIHv|GvrMIulClJekF9cD zz#;mTUQVCScP7fZ%&utRBG<#SodV#Ui|W?;mvsk754^ON+bVT?@wM%G?XptObFzhh zaDl%>c?zta_2EISbX(>&9B^!N3ktB!O=}?j^ca#nF&Gf#HIgK0s=VdU9(5E%PW2*( zlO5MFSTp5?n6SV8gWef`Ub>foti>7(WKUoDxQCJE+8d~D3Up~zC#Pd<5*t+ohhy?- zl!BVwz@}A+L4ofU$8T_ocQqJCol@rdR2?@lp10^8o347w@w`z!Ke~H2+ob1x zjC!tLVkwTBw<+bYTDgDchLqTzakWo4F470-EtDPYUNco?x+^$h_!<1=vV8xC8QGtb zLbc21gPoxLg-LBQ9^;Kj+h-RD=fbmPu%;d_;?SoovV(KTy2JXuc~?a2)acBP_0ozq zfVaZhhRZ_iFgwT_Tk$%uTJKJqR&dubwIJF21uLjPd|X6ER;Mj1Uo@%2W z4|PO7KY+n)9PJ#lQ3#kD1vTbx`vs$gu2c^KTyFZls7z)?J>98bh`S=%t*oVT@T9v| zEd8NN+`|1&5?t>`OMV-`+Od{!A8WJb+lpXY-n3SCr{Id=c_KX*`>8_-2+5O@Q$W$E z2oxE%TddqP@4%3c@kCsrvAXpQA^pT}osm+Dbyor(vi5h<#w4dR3qi(LoSIp0MQzXg zmW?;Zpv{6HBW>NfsAEzjYpst_cpE>F#@v{cfAm=*t5@AKa&;x^v96`T?0> z=&M&r?wMB7{mC57cLZ&5$6*FSw}V(?1~(4!^Z{?x?sE6NX32WoQx(ntW;Jx{T70^f9+nB^SGVaW`l zchw7g$Q=cWF&Z^H9U+ri==D#ToGOgQVi9P63aUPYSneFVDdx6jEIW%?zAl6K*l^d2 zs&N;8?UcF5CnS-VAg)U)#xRr@*~7<$;q?S?H}XkeG726Q%=@YbHf;TH!%I*y!-LGz zFtz7$zvt$`y_1ut`f@!b@dv)hOzz}GL`5=C;X4BKmxANC2?s|-%J}-R z*kp`bU!}RNx?O<0{}gK>!~BgC2pGgeP)gNtV+SCEp~E0=aMwx*DO`VB5Bu-6j>(D zJ3kTxFj-|(J-P!KN@Xy6@|1y_>p^q-@^((8rQ=x`c^`L*&1cc5&)iwbZ0Fr;-jT-7eh6^N_z*Cw|=mxf86ym zaFl8Cv^If6i|5RkUA1SsAJ_$KJ5s~y6!@HmSXE2u`d8=oOkW(@?Khn_j$SX{MRgWh zAH+PUxK0#M&8}AMw9UJ1+dtgaw~*=+eYe?`VHo2{Cf3FoSV;nqd1i}a^0Pi;`vc@K zndPjLHFEfTj7ENGgK?n17D2Exuqc%-0#PgHPva4hZl#crTZE9^&_K20wsF&O+}U}M z2}2SM=mkU^I1kjY*OXfO^SPXi3T;i_tICJI2Q^Eh#N}IFxheSU1#UX=rlM`<6~@z1 z$vcZsqOam|fqHEsi({+n8azV>_L!yw&UyhFJ>>;(YiHUDnL5=-@-4avhMGR>_=8!> z-QE+|QLAsIEItW^k*M~;xKM|Z57pbRu8U zwh|zI*4i#UVEai`ojl8&A!gYQ@sY_qYaIih>1d$$e!J=5)zAst{x{#0U1C0c!J2Ff z0a?@%46uA5^npEqs=QwRJa4s$+rkz%IaQGyP?JOTSwgF}8RJq>`tPVm5U8}YB=lnz zpI*X7_VV&mI%2I4k;#;{U;FIb&|tNPvQjcpyKJ{mIlb)zL%{W;6(*a>F&2y{>A((q zUV^XIjq2-kQt}XQ_FHQl5CP0LF$l{zh3q$HvJilnZwJSNc(b5J!ZE`>YjZe+;mO>(Ks2Km7i45|+cYDFZP z+Yy-V4C`nji6MRR!{f8Vj3Fzt+A3>K%*_YebH)OyD$N@ApB`cxEkW?kC_30n#J7Hyh*Ef z&rohg6gFf^$m?|ukHdNQyw}xR0iGSP<>8=~e%J7#ld-M!t;=f}h7r(I4K&`yLfkG? z{BZX5m!CWHXRW>5T1Oumn5HQ@PSFEzd|WBrC)=Mz5Q?2r4l~4pulED=Pj8_!U$1xH z$kkYXbrjp4dUsjHk^hCP-(|Z5&kP?fz%UnMX=^Pp$`-&oKj0)VJ@p7c<)6%*4EUgV z+0hG2cmSNh=EVHMK6>1Wo~pN(E``A(;dQ;5yJkY~(MSC|y%4>p(bo%HT=PjcIQ0*Y z0U2Cxk!g}B2tG<;-^zt9rxBzHdYxzsx&bJ9Z!%Xv zt<4+0P7q)6YG5~gdh{vBbsRoz?__>5O>U-oGT!15NhDaydJp6^g)c$|?LU@pW)fx8 zQK-*d{9T&<5=R_Tj6Jqizyq+u+GM!i-c1D*Pe7hY--UF>o zyLb4uF)g87^5rG%xH66G7}*a;MgM3>d0^i%q@K#v0?`09?_swzTi*QuKRhTR9_YU% zFwCq(rO1mE5ZZJ!+tCa+Qoz7E?>BLK8u6jpt)j-x2>T_dwizZ7f0cE>pI$wQ1Q-#* z{GcU^Z(nf)PLsM5`T=Ed0uXaG@bTzB>z%#J*O%zYL(|iw-%^3yr<`dMx{B*l3)NEE zEKO|tGCH!qcvcK%iv-AJ2kC2F_>^hYUL;G#k+4P&GRf#vbj9$fGH&meN%Q3*K>-pB z4N?b@)Hpb2(5-w`l?`8It9UyGYda9JMqeB{WaZTKI?rEk?VK&PBRNK@%bxyuyeB&+ zbD7oRjXnGV34Dc8U}xTP5{z$M2%>a*^kL5@d}PF7&U~XTL%U}=N})Uqz@^t%U1L*C zLgc=moL(>*=*A_}vNe%Cfptw$F)eY`ElA|s)s3{Q9=T0hs%)N9w)JgIg%F4Cj5JAM z6na0V#LioQ*F$33mbx5I1sS}VV zDW8dDPhat*APlA%IXy%Y#l<_JTM>i$#*p79mvJrEjM4h|O6DMQjoA7LOPZoCW>u-c zC+4EN7^%{Hg*>DT=hfKR z+#D~@1_``FIX|>=78gz+&0I{_e^(dexU)}ZIx1V6|FDfJ@%wxp_@u4S)0R-C65VvJ$8e1rLHw%qnVkT8Mr3nOk+5^UXHS5x&4#n&+_Y z?y%XlYeh zHh4L8#Fabh$Se^&YIWfls5>Aeb&g>BV(PqhNfsTz09l+W(+ar%qux+wdzjO%p{7E1 zHX8J;g5$?ksj2Y$g&T6O1xzs7SiN*Y51(ole33+ZZIApJ55{Onm|2c9)NC_`{!Yf2 z%pdy4zlQ`+DsNtaHW)QTT$8}33J6e!C`}AydKxO}Bc?Yg#ZIktjdo;5EBQqFAUk9g zNh48rQnd|};OR%rwz>XwHU*=%HuOA)HgIaNm)nB`D0D&1XjHV+p}CyC{`%$HOC^>j z8Qj~W`hYc|2mt=L$;-Ki34~+>+;7S>m~8`RU2PGPK-WflMXu=V%bQhhZExL$k;FvQ zQ_@$fPtOOXVKK?7RC5%xV4yhXOSj3@$l|eu%HoNPo8{s?S!?fg!^drtowo-I1G*k_ZoU>R9c@Bg{{>9IF@%Z$?D(o4XZbI66b~CXBct zAF!4V>M-@@2D?ms!@pO-|Bfaa!mlIUA^_)I1S)!m%_l$t!h!y_)`zv9c1t}|?@j(^ zT&LBk*xiHCl1vpk0Vl!s6^S<+`i+)@275aG7zPs#$39V3seZKdph9tE{&>s&o=m4k zOj2dP|GeM@Izg>Wf)I!o#91D@!Vv{!JgCt;tnIUoJE=uc_@IBHMLHb}1oMoYD%JduAP&yKETJV?E3a_Sb3D06YZg zw|g2AsI2Xx*IsxzWMG-MRh3Y&P$P3i9T_lLWsy$-`Dynd!4Vbj3hmGmvxzxSlLrnh zf1w!ih6rV!Sf`c)!!67RrF|16@!%a5Y^XQ9&7wS(^U+|e zpZ*jfO?;|AyUqvhtACD{Z&Q|eY&N-9a7yv2MNm5lD=P^dz7o+GQ89;4df?f2+)@?9 zBJKd&ch`9Ks3fl zA5a5EXbkizIpQ=Q5jAsQ(e|PzY7vdKvo7tKp1Q{1$0aDcJGQBn(*T;4*76N#> zenpTQ1M5X5IJ%`8TEH}q8Scc%9lXVAMkj%*3DH zZ_+!V9=0?$GU~W}Tr4jCko7pcz)1gO7rt3_^u`@fz16jjb%JNJS1mrd!3qO|pH29D zyi6cM_;gytHmaPSb+R_@i-C_jB@KXD+ajc>MoZqh z2boHDy{$aXiA@eKw4fih(VlNM;QcNAlc<_f9o@5(ES0oscSR!=itRkyx%8DOo!F%% zybeBfCP5||GaxFYs5nZNThVIf2d5inmxbFtb-bzc%g0bsz#bq*Smkz(n{J|(gUt=8 z*!J^4s83UVb<_P)H~5n_E|EhbomkmJ-PgLh)rGFZ9J0tthG9_ftdGIA`@Tvgi+nR8 zgcfMxvUR17PV!adLida}*{`-9=Jqdm!pa|PIy+oY;`0?n&z`7#OUX9T6%a3Pj`T}p1<=xF8W=NtD#KI5N66KubE*T@myc_ypzq`Ydq<2(q(O~duk-r3Rm zDVM0Pa1f-ta;cXyNimY%p;)W4gZc*k( z`EMuJ(w*7!FXeETmw+6es3J`M^LW-e(xg}zUR~`;9xaMN_qTbj@eY8pp7ag4n$rV5 ziyfW0$K!BzeO3$UmoBILBAJ9R-W$#DsiFLKTA_6CBYQNXlGpxKN!ObCy>9c|PL2rl zC}`7xlylVLcTCCOuni0W->AU3Fjj2Wm)ZM+d+-;jAR^Rbs;HinL%vSMPDY=Cxa5ox znmcPW(=b94Yjr1#CQI&IlR?1A{6Ja)Uq=`;CMc}+z|8(y0SZ&MX&S>B1JSr5^IhNG zzNPGRET&Y$9zQo9>HA%{DXP*Nk%ZMkVe_%qn zT$=M#5ySlA)tOOd=cjs@_63|^C}I@5MNEdj=^7rpHDTXVxeQF+NhyjH{c!!QQm#zY zWY3bd=*`Sy+qOJj*#x4I%wVRl5nl$Yj^#GxWhSP zFjYumRY^9o`3LSQu);X}r5W3(ZV({;RmitBF7&N%?x`scR8ORykia1|b`Vep`<^_> zdMQ^z(nS|x>kJWM`nsa;I2m(Z9$UAkf1io)B%xM?vjP!)Y(<*cpr2>m&k>~vIBUX4 zh98Dm-0jmfvRdso--;c3l|#TCL7B^OY*i0fn|6-!%ytGpdii8#vxU1lTHFAvU#}}M z|5EUOg+ob69SR!G>}c_qpDY2pE=|}loI{-7IA&Kryszm+$!z#p)(1;Oz|ux_a2M!; ztXdP@;2&8p6cWTd-VbFLFwpdcprS5_=JsglT-`u}|INQgTewp=t<<2)GG{%}| zR8feu9J5ggoKHBY48c31O7l=S5|nkv$1Q;5SsI)}MfcQL^kOJEs{1RiN+l@aoOg8f zmOW67fl`_M@|}>d3y@v4BK7TE2=|14)NM~4?dqUw0LnrbB zAdMnB!tjR*Fn)m?Wr(VY<%fiPL-JUF*Dh}7Tb|$KA#t7Gf+E||@>i@0a9P|XYX=Iu zjzfBd6xO+TM|4-GqciD)LvO$?E=Iirc*C8(r&>B>sx4%I{IqQ_;PikYFn6Oeq}zpP ze`Ygur#%E>K?~%clLZ-Dc~v&geBBq8*X9iSdx<=%uT(Aa2hsKWnX|8wTiB@B8sV#^ zPFSQCvyn*I=Tm*ES8`l>RGezLjg=R$ntt+T4I1n_AT zPo5XWMn`M-X1G$NOC72|P?k-o9yIAKOH&(0VRt3WW~E8O?UfE9URV@>o5tY=7RP$J zaW+9%R9zw51@N{FCS*T!cA5vl)Bjs~4?)}{zN(CJwEf0@@`as?je#>qp&8NB;3O=h z&dN^)7@E_<=-B&zUH}2Z?IGM}5&b$UvBJ3n7zgy0wy`Fnwytz!F$^OL_> zN}IOUyZ(A76VRQ5ceuUNk5YyBo(|jlhoD`J9XDrXlxNLYXx%9DHJv6Dxx%l10#Rl= z_U653)vsXG0uO{EO%!ZU$mfCO<*}oiy?H9Sc;A}u830E;Uv|p=;Wx_+4}F4O42&3W zN1!VuZ$Nm!yk8Q(MY3_T^e)ibUOAr-&j!)(7i{9tD>r+Csqc1#!A0~Yu-nQz1_L5M zB&)OlTD?%;2aKm9jBiz_obDe1(xMsu28@;y=377V1Zf>BHQ@>8tT)qCCxSjKfRChO zty{zmKqjs}lP2o)z`Lsp^uFk`_h^kf^`0}HO;Gk;Xc8|X4I~29eEHL=jh0c+ zMfmtwdu04tWJ?bwj{pO=%RY?9>O~E3AvgFYM}-v8w?h3crK`eP7bd~*6doQXd&QH| zn~{ZETQfNX?s%b5ujrrKZe}c-omlu#=q6}i`QDU9dX!-rc)#Dj6Di8i7ILGmcxcg# z#VNNF$m2LO%4ImK4oeF1OuGwnMkX5i?o{vp{3yh*Qw zJ>|jmA59zgF%l%>J+!b;&XM?d-?S}+qF&0*v3D@q;0{<){ptFF9@w~w9)6n~;JK08 zx82o?d{GMxGfn|_bSA>M{1g+mf5!G{PDZKDhM;~zJ9gOIl+LSKX~F<7-Bq6imigoZ zZ~3(bPZc|>q!UY_X2$Zu$s`ovC1ux=@tOLdq}ayIuBy>m8G*HKKYI6Lg0o$0$=){^vh)EaOUi3*EZ>5{(u*Lv9L{=>~VkPSh zHx22L9gXu9hmP1c$Y~Hvi+na)b^p}j5s4S(dYBV~!WR3ECpEhLs&Wr`Ky0BG4+d4o zn6p-GK6J}j)R>r`aALZJ{m_<2xKeJ%saS#Py&#;$fBLnsJSLzsJ3_IHlb|~d~>(;C{`S`Mp$2rVJhI=ble$-?|{SY ziMeMX29EC*2P^?i30Xu0UO5BpSI3pJW}h`VG$cz*vH~V1D3kq7JL$OZopDFu`e5zjvoXF zfdRG8#^lrq{%Rm6fm%l6$xh=Mv*VI4kq_*pL-N=+W8!Z55Ulwq4o!1R(oZg${&EI* zNGH25tcsdm185R89~@p`ARoa*?dA6jgzbm}^$6d4Bn;XM2l%dD6F2xeQD%8rk7-Vt%^4R7`A-9FM7ijUf5FD}YsOtWSoJ&f*NJOny<)VQAa9&)nbSBgR8#!2bN~5yd^0 z{ar;s?S>n*O!iKyy0V{2YHaTqkCslKOR06E_CI3ovAY{YY_o#O(!_O*+3ak;e>gs> zZepc%8t^^PudBceZP)(l9cn#I8lB!>bm+>M$ywp(15i?RjuO>H9mCUC)vwfMtu~Dv zki6M-_@>ctkE7C_JXZ$;?R{6((_~l$J7zgM%co3W9Ka4XCtZ-5FpTMD0#&EC<>D9; zVfYw@#FE3a?ZBiOlvxs{0d&g>xA6{GtUIy_%d0dbAorMeU4TIkK}fKTiJF9(-~$*p zlG52R{l-x}@OF&Ed0}xn4s`2ybh6k=Yy3hlF+0l&)*`mEJ7xn89^Pt4TqS4|D%Qp^ zoijCu0anJ|6ou&L?uq>dE^e>#;CXRxKuLf}rCM6!@r~d`F4Df4XFOc#-|@+71)w0O z$_@NP-Pa~)Ew?CQ7Xc@vkJBV;RYePwGM_NLdewTyu+9wfl8uWc)#E%<8~&SL9uZ|` zv%pGob8;R^TQ^Nabqr5xb>fjSO`->yKr1S>k+C5Ztx2+%&>JY+e$2r)8T&iwY`2aq z!_Ugki_}jzA$LV8aPeJ7hP+x9-+`54oubclJ*1Mg>h+RVmbQYiZ5d_cO)KuNUXfCb zWRiEUagqk46GL(=DXD3QqnSE6X08yV-EhnrQ5QqCW;USarOnDS+!2Mj+}USSioo3t zYEG$Ol&Y+bHdOl5u2s8l&q~E2LZ49GyuI8M>@fe+3$VPvndo#?T@U2E^e6WdAeX6* zDx`=@!lzS(q1t{c%TOQA0z252@{sEmhtZ{wsO#q7VFvWfT1ZeMa*^& z5B%Z$&{M~;MBhGYvh&da@qNO3wC(|Grl;Xr7+HAN5>)p#beH{IO)sW7H%00vsHgL+ z;#|M9rtNJkA-KkX{`o_LYrPnFF{@uxdpoL^c&3`P%N?HTycHqpQaEogP;cLm;~+|n zX#yNz30=nHT16J?q?Ri``ug25W0T8*qumAFw18)-uS;kqjQ0X*WlEMU;%cSc|8@rB z(mT6^du^oq!2hGrM+tG#!Xy<|lGac~F31Mz@Ce}w+o65L z6kPyl$H1sWYs&Co%m)AbZP=u0h}hQ-Jn$@1;=BhO$S->R^Dgqni0^bpaco-N8#$-K z{M4HQcaj!?Y}OsU_3eA&X*J+g83}vbE%&BoUVHuqYxpc8Rnk5(LGuwJlk|ajaY14*QYuR_ z4=pEjXP#Ms1^O-`yid*8IIF0g=A&{k=znT`xdrEl8uN{H@)9>`mZqxE0J`C3kq_om z_*B(YZo?v(7QeY&ycQYn64Cc}1KogDb%#VAxElFB8<%=ftx7bbJZLvR;Oj5Hn91U+ zQMb2cXswKG8h{$Nvef{z6g3K>Rc~12ZlTvhK(t(sg1Z>)?|zEj@~gmlRHahSN5qg1v3r&kH81Na})r{L7z29?lTU2#FJ?cKN(sn-m%<;5t~&myarGlS%%gzsKC(Dfmj~iiH@~jp$jzk7S76p zu72g#-^REc9%`5e24ucfl4Rar;G{|#MgCi z=Ck`y%%`MeObz1JCaBoxl=UC!Ny|$ma&982qT%6*m1@hgaf!o4ReA@9XmM98R>8(j_)*1y6>u`P5)avrv&3Vl^F>Pu$97!HbCjF zbjiyQz1Wg6OLL8I&7C-V%#;GFzeC-qyN@DB>#6AKP5zU(Ic32F4Xw)cj%ODhRaCthtYU@4KJMqE;* zsaD3^aQmJH00uP9$J7My!XgJ{46uSD0jI)9b#S5~AEf}pz&LyMvZ_Fj_1y6Jgh_|Z z)pY0$Z0zmiAY^BfS=N?xbhIi3t7J?&I(N~Rzl7TvxVl|G+f*i)Igh(WA(ipi1ot4l}+J(Lmsx_JEuardEMzeI01f%4-LG_QA zn!!go_HM#qaw^XLydtrN9Y3wIcmHu$gY1-$MP+bek%mopL7qQ_o(zX|0l-0?R6q@A zi)M7yCvUphiqiP9bybct0p+0^5S&)cZM}B-Yjo1(=U=M=vNCaEnG|c9E*D(H;#n`o z?I80h4#>CY*xVq_M8+&{v;J|#UR=0xUDQg^7{G%fX*dTVr}miJE1cTi$)q9xm9<-b-H6wqZjkpl&88| z40gz9+p{Pn(IN9=AnjMma+#QGP+}gt4Yf0mS}(jWHIeDJ!^$$ag}aPGV4D2mx-fl* zRmCD1(U%2;HvAmZ!4j7;#lT>lJb{SPYR!S!OK5JBb~MUPbC<^P^&>lD{ltrAV z_W2$hGbNEhS?F;2lC5*s7k5COhyXUe4!~BHf-et9UlhZ+ve#$x0AczS`FUyoy)yWglkAlBFe{r&xp85C zAQcDjKd&4cbv^dQd$`u5;q+oSTd(aP)oP1E%!fgUnfdY6$UP=jU@jYu5%bdyv4}h@ z{@7`k`DbyO0eJ#;PCY6nU-LALNiq!|n?eTUd{ex3!y1k$mxIR1E3u-n356QuphR`s zS~gOXt6EPb?QiVgchWPY@uQzF{bG=j%EBlE4P2-YnU_X?Ne#%ozP!_O%>M5WCIRPC zryG0sqCsz~fhgbfGgBDrXn8NFTL%JhSOGK4_gNKxZPWc2{)IPhr#Y-Vh zt})rUIc@*2JrW=l&+pNu5UtWl6;6+c0}2fbvxP;W?gH0b0Y}v0RvzlT)h>n1smev; ziC2IF6C2bsqR4U{^amT`uSuz)9>wX~sB5Bmxy_QXw=CoXz)w#5>8nb>RObu$B3~*u zm5Aq~SYwP*POWJ$6T1vwcK|brA^vX~F*N7W!RW?H-#FH>I_AuxedzU>obM-%>YR_p z7@<3;*21$Ma>=R~{}7zb6xQ6jH)cK#g(h4Bf9JACW#+LW;NP-BK7w-9K-9B7D zX=JrVxYtm}&@BP8(g})EF!%kGLbmg%^&IqyGtK3brfrqg@e-@J{i^GM(%&ntWh&5V zF#noH^D_t<0vi15H=~pXN+;+jh2aR{{GR9bAV@w8IB#+4R`hjdOQxs$Pe;^Kln z(0W}# z%v1&AB*L|iGZ-7OQ=a@VF#r16FXO=jhGtm42?M9MtMAIS)4mpDay7i&)KqK$vRPiS z;)k-E17=C;0d*!(jf*8td7U1}7%!bbx~qbekqTE&6k09N@Klcq5m3Ee;sz?Goqnsp zEb|p`(NkLKU&J)I?QiO&TzR>Epx==(@KAnPTG=Lt0yy7iQB;CXJHTF6wO~q8%CM{PRly-ym5y;5O6)^i-U?N|SQVB7tMvWdj3& zoP}6sdg!cH3VZs|G$08Zf`#PI4+tV=vpRt(m`2TOwks!~yCn31>k451G2cwPSPMk9vriVX4F{-~I}_C3=kZ958|4pK9)?PSydlLcRN} za!$MzeNk`DJ5yApm}~rUtpKuS6J0c+$Wq11`x2Pq4soLsFe5y#{^qVP*47%%whX@; z56+#Vq+A}b1IDs;&KS5By}S!fA&1;ufUw>URUfcpd02Q>131{$My6vbUL~B4su=If zi+L0XziKacArW=Jy9%tXjm=RG+fAAOSIGci@+sWHtW z_cBM>I@W6nqyNJDTH}kG)t)&oXsREl34w5?U1lmLyPkJF*lw1}@1X7Q8Na<@lNL>f z9N$Z3u0Lo_esKqBS%DKo@%py|^HWA#cG@#Z;&E*Yow zCa!D0?Oop~2v-sXrr~Da8VPkV_N1!Pq2rN6~rJwUR zc>5KH?ztY!QPhkjm}RVJP@IAe5OxfHIi0AWm(Y`!XJn;5^=2CcxEa~Cfw+jE^%Fw67!I8yidZ@ec9Yj(ZU%cb zsTI`6a^IS#RiU5PgRoiKd~jRvy*Bb?0eiNi8Bhg*nO_NsR*iq4H0LDF_E(4oBl#@P z@F++{-`%*0LwWm=!#4tNx#?;P2?R*h(8wseAB81reJJZxDa+Zvrsldm&dZk{E@1JK z$*rBWZ1z)~vC)UF%Nwf+U6Ip>3G~>hQ@5?lv8&7M^xm@}hNy|rtV=7p&aTjO!n(nh zusPuinx=>?y<=U8CX??Suj)*I{J)<`>4cY0##z zh-(@H$Md0m*nsid6dX$pcm2K`KhE9N@tpe=zDH!z+TVlo9Rq)g=yu@h?*{g5sw8ug zXS-!?n$4c0t5%Bj7#wv{G1*;`scr+Pp<|M9k?N0P4!Lk(S{rZBXU&-C?@Y_dDAf$zl0`K`&vMuYN% z5xXM$Hu)V-Ng_LYH@e%j3T2gIGpDDAn$`o!)vg@NuC7%?wPX9+kthmi!l7%uyu9ji z3>ze;t&)6Bkc<^@gU2aa#RQJf#JB&)+FJlswYL4kf}kJ)i_3vKgJ{Bz-_On__Wx9bR;VkL0NEfepy# z)F$A7>k->Un_2l(U!_S`Qs()NgM2G)c~$PAI;+hpoDi@2*Mqj6%=P)`@Io}_9)*hd z_QrL?jmM2Q9km?>82c!Lz=OZt|Ps!TR_r)U^+)50m2IWoi4x~pExkJ4-F zEyhyEXNf8GC#5fRw?3i_o7qYqg168K7_20B`>$l?O@V8BV^vwt@2t3$-mGcu*6u^- zW-BkR*m#|>s8MG;v;R>&SC*DK(yy?{`9l$*P&r0 zFD}ar7G$T2jn&%Q@mF}l3e`V26nE+5e(s&n-5VHlJ2Kj>PoJ#88ZKu!XgsmBP=9^Y zgdgr*%Cfa#xc{P|=A>cW2FBHVZ}d$6SDE(ULNyUW99d=7I|Z))jW5V}uu7Gc z{Eix>Gbvi1`z3-?cP#Gs5mfNUwlO4UHHKIxn*`!xu52HuJ&k-86%n~Csw^!+7}2;J zwcN|OU6xT5TK2>WBY3ZnrKf&a@?|*b)%@HSV47_ttA*Yva3+uwS&KBc`dfHj&IOIE=8kW3koj$n4@m2xjmZ`6bc|~jz%U2gj z`^TS6&ENUbIOCH4w84jL!1{?_BR$0hru(<8at?Oh-woME81R?+RX`kOSvA9mo9W9Z ztzQ#$^`V`*i%|=eKlgjxfG<_zJ^s9E-oieUZ&JfQ6Kok0i`J8-b`{Co_)>PAp3Fu- z_o2DEOU9TCwVh{^+6e_sc0jSR|D-Ehsw&afDF^YI`K}WETUBl5R}s9@&d-6Y3rp(qXfdY#;A%u!}|@&V+4kyTu_V znJFXa&48Z*-zQP+&@#v9)X<)5bG=85lX)7q5;c5khS&R?hdN9i+6T06_eQ__in|c% z`E{O8j0Ll?dTnepn5#=nexlxAY8p4NqO$s=Ysz>EUwr{rk;Hlj#%BFsD=YhMm`%@N z8b8j|XwoxFMg?lBcvQi$@FIn}qvEgG;neb1D3@8Y+wNMvL-jj>o!8v(Ld+q+&RXYj8`^S7oUKY*QA6B`Y1oHU;-QE|QLM*k?@#_&{KG{+ z*M`zCsz41$Lb@U~oU|3>K$PmA%o?I9X+X)qAV{)jLjY^%)jYMQ$=XzPGO*Z>xo@cG z6~ePTZzUh_J`4^)YOV5(4YtgepirA~2Mm#3h1WFr!v|W7aR0R8l*MpWHo7IvF9`I6N_01dKMUm60%FkCE9I#*8BD-%{$EX^Ovl) zU^x!gM=Jaz@29xvM;D4UD)J&D22+=x@z9aeh7|2cZYT?tWZGu<^)FOfFH!|X-(B&K z4p|IV;Plj;sg$M2P6-I!3Q*U4XV9NgTA=@lkgPx?|EO7dp47|lg-J;6`1F1xeO$m@ zU(+Xf?mSLf@~E91cNF)xb^;AFNeUcZi;?LE)Jf1DzmdC`Quzl~2T!hDhtoJP%!i<$ zp9|f+-tJAeHbT=${tN4aK!Cgq1laZ9CJO!q4EXURCQbDDbZN!<32=pSP5#1Q!ZD8H3-(P5Bt&G)_!+SlYRXH0WbM)|hQ=5j52M>kdms<>B~G#}&kD zmD4}Qm5KrxeT5&fvTN>)>5eRIU}sX3OdncQ&j zcb^sNknEWirp57S;`=pq;&-K;vak<|*Cvw!KQ}nMC}7Jd{2q>^+p%kdXE!^T7i|Ap z%(rojC|+4WH^?5~SMR7|NqKFbFtSH1f~2>Xs3Sj{=>%ZW1vT`o9nU45NSb{~H8HVS zO0$yXW@r;<{dCc^f3Cf}1R3x>OJC%aR=Dr!L@PRtM+}CmGS%2FBb;qDaFIqlEPw8& zwpWMhH@b$BF5L=y^heI$pc2TqT3)mwBk=q4E-znQpUYTU?`&DhW9C^+@b@?YJKUH| zjob+pqo2q$BDWuwKbCBh3^6b33qc#2A1{qT6^uI2eOGR<{=vU8)1Wz-^~iy$zG>-T z%C&yxWw&2dN}*a;er)}I6-{%X;n-73lBpn#VN->w(nCw@FYkEpkJl(6)bBT?z>~&P z7R949*+`O2%hK!6ugCm+y_+1fY}6 zFQ^kU+@q>uv`im48Z`Lz*6mZVv2@Z1dvTbi*_l6~&r(O0x>4yx9L-VYcjrbZcRkGG_WPKnJ^l;a0lW{pM2j>y!C|!@A## zjy^1Q{C78@d4gCY4ERdT?Txi zhnnX~{!+LqM;yq+zoh;F=SlmaObX{2-U_^oYGwz>fv&ey#Cs& zRBQ@w8w>18@wekn0(mk*lAVTr6z=V0uMm(GN#w`bWwNj=vlG0Fj-mq|+7TdiS`cvj z+V?BPB4Va!TVb(RC|;cscxaO7{Q=S04UoEWt}pW8NIITuM~G!un;AF7u;MRcjD%-W#?DKB5NFUpx~M ziDz$qWw5-)!z^{zgpvuzRL_=qZ!*a@?zr)v%QtzM5X4pUmD-S2b*R6Ztp=%0EN^r? z)SHmT7y5Z_Yh0?~>_^e7jKob-gK+SbulM}k-iM5U1XE}_ITu#CKYQXZbiaM&sCZ$g zEsdr|s`2K0u`oI!C7sXjisDa_7}Ps>Va1Y_QB~a*T9D_J@4DLF30w0>ZLYNN`*0x8 zOuy)H(Y7T;9)e1==+Ivqf_{T{aHy~f1JyiuLyqin>noQZWtIRyK~AZcj=wc=lx=Ml z)ORZlzi%@+-@2jWPWiZ5XPTqs-oo+;Y01D=is~~-JH1MyRyss-xFCy;M??Z5_F%naBhBi)zG5WR z=EREoA5&poxN6e)Qlr!RDp~rnxWqvG=#ibgTaLT z%|iequzGKwS*OqUgUz*F7REJ50h-Wd+g$WUFPW<)eVFnDw@M>r3`CmZq2&r-3@Nbv z(Fm(~O1RMR-ae|I?g~%-`@?Y$iAk>#&dKSWax&97% z+Lt!za8Z^BR>U~X$|;a^z1by>UbAk?c%V5tB!W7jO^ut(U@NxxHv5`UBDN`Mt2Em-uGcK7AMSIu`o8WU z`^#FK8t&s8AIT@OL${{fqPwVPHG2vk89Y5*QvE8gx){H&S&qkeRk$P5Mj~uCTT*iR z+1WNB06`wW8c5X|X}fkBW5wd-4HiGa4H-P{(Qw~)|Imv_MC=(F3DfkO=`y6!iZWBS zC`Z4-#!PFapE&-^V}TiWN71#o?#4JBZVBSQ%0M<>96O+-0JuM%Pa+jP`iv->Ef}}( z{m4_)zbTE9f+GFX)UfCC?NSHXS1}{#qc2#eijQ?4R}k<_wkF~&ct0W&%UX{9xjzV@ zmI6Y#VU3$!99?5ZOBC|@oaK~TmS_5**0ynQ03_{(Mi_f_R1NVKs2V+Tg^m_4oXOxx zD(*r9_LIDK9DI2p88TUMBsz@y6FVj z8c9Cskf;!U8ai9tfSgjj$!s?@f{AHsJRQPW>bakTbLWE9$XT$9?+17o=SS(bZifcl z@gP(*=(_v+Qx{lDncDvTof*G*iY>#?lQ<_h)R%7MFXdt*)_b^?_7xQ(Y^ALGeelL< zMy5oqRyVn<9I5tRL{|ke6fMyN{RByz@sNY)d$Ag*_$>5tZOgLluMqT1>1_@^Z$Hd& zD1B!4l{Ti5J_a=g22(p==JBTyEbklnW@j?XXp%x zdeK@(ebGGG=KeW6>7s?;ee(Jp_}_v4bCN_LZ1i}9&Rz(Ugp~xt89+HimBybnw3>OI zZ3(htq$}$EU$a>#exq$yQD^Rt(jwS9I$IhD9)xw%pME>oI1R12V8W6gQvv}>@>T=N zbR0!4Tn=jtGCaq;*Vt?)IBe#uPfRcn6RL<7iVQyyR*enzZtwo;zpuy4{P*zO?2<&hZWVg@-O;97-bnRD0|q+8n}yg606(WibwfX?`ZjI zUVM!FHhPfAYn8-JQ*~7kB628ix(Sp`?SBaoDJey*O?vTt;E`3)m(9X6f7Z{H1RHUw}cY~cp+16slT zqBNQ(`?|trah7xe2lxuld!)ZekL1$@*Xf zE(jBF^K9!K{p2PV^ln?j2Q{oh;aA(I%!j&`j?zhrt(sr-#?VYbQKB~4uVC;dqL zHc^9&CLLL(f*MXL%0?8(dfG$H^VOWbto@U~00atx*zg`A{LT!qCtQC>->C)3z>uCH z>6cWVF6>M{c+oIxo|m!osqcb>E?3}-C5U->z0w28wHe4;f17Ot0VA+@QBXcdKcf+=GW@4j;4bJ#%S&7JYJcc;rC0?# z!wu?SFgJFwuX-7BnDgd~Q5zLQM*Ja*_reS1Hgzp0{r4%uO2;6f<&@RHSnl#&yoN&L zb>6POM)25t?0#gWUUtO61Rvvr;wB5=4E>_|=e551%Y2_w^-^mC_K3!d74trZ_0i$M zaOoSP6X2Pj*CwamDP#UF|L*g9kbM*XaI2*!L*n17d0%~Y?rcVNczU#RS7GY`7M>w| zh9kqbq1l&bn+9Ft;d=z;2SGYupCQ}IM|QHGvyAU|I@s06xwBGe0B$~pH)HJ1PeGLH zG0kpv7q9N8ELKTl+ z<5T;+uBFc=U5oU)(j4=cQi8}mUgu_EzrS%uzM-!o@ge7z>MeZB1Me!oFZ?LIzNO4l z;Cl_jazzG(#-za}t%gm3m<0hvKC&(7$u}>2$e(h=4^(>FXWzza?3}ji4kaFi6-}>T zzdp*u_U*2iNl@Wulr~Ocj4+>a!}}pb`ZjOaZ%l? z4#f48h0z(}`LWJy^{L1nqD#|tJ8{^#m9y)6WsK#Hw+{(nDj}kg6-tCG_?c3xYz1-` z?QC>$n@ufEfs3#Vcw(ewhPgm+Ys`pj<^pZOg zlI#y%h;ahN2*38sv&WszA-c?>NYC&AFfg@Nqr4LZiXD$qMK*5+`=4t%Z!MB*w;c(J zpH0J+5!Mboc2Z6JZE4F#Wfb*h*H8th$r2y`MXB4EpaeQkdTk4-s?d;^~c^s@ar_wt`%1wtCpo(_ebg>rIp@1*sidFZ9~>VF!zpu!2jETYwiUF$ort zbPq~!RTh!(3@lCc2ms?>C8?xgbG->l=})`LC7JU_WiHuO$wvO_SPV(s4plGerG!Ba zV~J}xe@h0}ng4OkuMTJxFq`k*xLx*MBU1g4sobka-1p8*hsVWQQ5@2k&3S`k4%Otbyi1`0UF~1OzBjIdA9VhOzYe zPLgdaiEeYUe;d@ES4$LrF6?pY&R(ZVOemK%&G}r|BSDCkrB6p`^OeGn<74r3W@6s6 zc9)NL_S(L-P8L0OIq@=VxSIUr&ySwJdX?bZ>2l;x-uan4j&W~!5_MEntfv`yUf>Ru z#@h~$=<^2}DO2RUF^~1$wI~E}p8rN!f5`B4=8c7SPZjI_Zp)0i-0j38$Ay z^)WwM^9Mt~nLsDHJ^m!*_OUEo8Sl_6^RV2)Pj`(uEt2@^P-3Hqad)DzpeK~-&nqG- ziFLS6IhlHvdQuLik3)ST`7)67>Tt5yfwz2Po3MXqsIVfT)xVh+=iG+LB!d2WLiE`s zbEwWIz*p~@3f}tri@VWKCq>su?jRobSSg39Bsb`X!%Xz;bvHx;Q4dVhhA+&(S0Nby)s&&!&Wwca!% zu$dXCDn*IH(353zZ_LQM3H_df zd6WCUPvY_lg?>YF*FX0*Ph!|j9uhe_1O^50-%w#Hm@Iva)-mY4jd2r^)*1A5R~#J=+VeG!}(p9jsN#Tnjm)`i`1$`u?7E%>fu2PI2v3sUq|G?^|f z5%U&6jSMu5jck|P?J62z^5L|rJ93UdbsIkl<5erNz~biZwlrhqr3MZ+#o0)O=A1{r zJ5D01RRg~?RGg^%%3TF%ghr4~2s3n-{bQ+Q$YQo5xVS-G;@0fYe9AlRi=m4j!a!1S z*q3hJ|C<Ccy}r03XdPO=B(jV*?YI1kUZTz5LXRcF%5b*Wx3DT9X2E;a{#3# zndBlKUL2KI9ufkD?nbRk$dUu7ad&h9e!a0=C$HZ{5@$%IlsO&c2-*AM(Ry)w)se*v$MsuDb!ZuIH{>FPWMY3N zGZCo}drk+&o-^N>9Z;r)UFOEW3Bz0M1QWUQ?J9S^S87^X+B%OsC|2^4!#y;KM)x$C ztx0eey}P%VgcF!}UUqhNfPEMJnGA^)!}ey(2}9x3FKo^sOdRgtxmZ$o3ymJeL8kBG z2swAOVLb@X>4B^O%TeWY%~ly-!TIKCLh>I5p*kH>&)aawY~(SOp*CY)NGOPmFf4~P zMtlg@p_5m~SBIMRHF{MUucvJcN2(`+?yt=Y+PdW2{c-jFw$z;ig6(z~lnY!p3pzaz zBbm7`F)@-rGHdl2+uoq8J^+meyR?2ZQd@V%f4JW~BNIwkVHR(jqoa$G7wAe`-!5Fr zz1Wfj_f3*_$t~r@q2Ge;{QSwWHQoVSu|n-5JKOUFwo3=nY(%TSGWNbroX~~%{JCEO zRCv&UQmaCw2ltjZCLaqoLHgx30$`Fd>M6DS2|9lHhe5?g!<2NqB_a^L`O~9oUp(f{ z4Pn;)>)->XqT7KX_{LMo=I2*Hh;?oo#Hpfs|LX1RCA@ZrOgZ73bp%|Pba-!;_+Cuh z-s$}0`hHB-ad(EU{}OPDXpD~h76IRoVu@Rpq}Z+&EL)Q zhf>bO{;L`{%CBIJZ9K6!XS5aw;|3#=HHuwY@7q27XH^jpAr3M>FQcE;xjDNy4>>Ob z1EA3Kl98hayPYzMDEbMS*a#wdXOz)5FO)-uQ-ZpqyG(S6>YKOAQ)FkX-&^movlHIn z=q1!;tMxcHYBIDRUmqlp<3F3cv$7FxetTI};P~Y|m0JFgFfOk9LlxZXs^$ZL15R*I9-RL(94WY%mg8UL zjz8XLm6kUSA^j@M^&3!vjY_0;oD3CcuP;RaswjxrIMY9!CgKq=U7(WZINgwgGTSd& zBbtzHgL||OP_JKHnSj_+IUk|N(f>=bwc^q{o~}VJLYdmn>~R+bLAMcD>1c%@=#1nQxJ~(R34)#!`$7~ET zwfOewfuh2wYeLa^|K#&79kTaxe=QQjVxfLcs?~&BCyB7xvHAD{al${A3of9?Pa43S zH|=*iFR>Z^)ZjDXcD!7omkF)4K89$i-H_Do-?1ffD4?9`og}$tOm(>KbZz zfgPm}h2#6f$=|PVNVVz(3u@f4poi%Fc;liOyzK8(Sy}t($!HH;*oKyrh`SeqLKGhY zoEZ2Kt;Vj4hwc4`u;R{`gJ5PEIY-8s9|e1D?h+LFD#aNWyYqIU9$2vI>`VktNtr%5 z(3$_-(9vUV&5x}x>?Y`0E9HVmLHGdOm2{%Szwhb_42f3#9*zfL1~;4$I?!rRr2JF$ z{&P>1&^0jY{$xT}7bviB$2)u8&tG;3B9lGhnF1K1{_ocj z{n09-I$25A2gfzNV!A*W`8ny@$n9GMF+>175Lcyb=_`mqIlpWU^hAq4MD`cC>E=Yrn4AwYMB{PY8aPS_N+K*1qmZvZuTIZHu&3qys3&1Dv203;(F z>f3_fUY!cQwVq(5zdmM(OKs@)0hyi(Tup`IPXYO_-`XWQyV`LJvMx%FGLS2;$NHD# z1bM5at9^Z5*2IvXUL4sUHb{XmnVb8x9mKl|GyvJZc_O4#A{QM+HkFp=_hPnDVk)cq zGj@>}3|s~z@S+k!m-&`gNaqIa&i~9${)%eQ@C`j?#fYs9!suNX#v~*ZZ3^2UOE?AE z;<5YerlO6fGTOFoHp^UrNe96|u4usDdAQ#ScN-(;m%T4PN*@6%3zT~xdZhk4r1?W8 z@K?a5#rE99hWw_1%gWv50BDu~=ND+JpUh0pCn23)jbp9)!dDquR+Dq=_r==ZS3>p{ zbag7|>U{nzm&gm8FZ%h_AN=oEe+rdDpjJ3x&=%FsFwGZcp}4pE~iZyAs_fvKaA`@Iw8!QpuvnzV29{bNnS zKlDx||5WEDjku?nbOqKCsQ1M6y8@Tk2RQLFad4lTizR^VLjoMBprAPeng1#OyLSOg zKC_=f9d|sJ^f6^h9-2X8wwUqdMM`7r#CySIJ3NMynT@J9n|a#7pr5l71l0~OA0(dY#5WPG zr8e!komr!rk7E^C6*pgom?*cUIF~B$kJ4&SpO;0L7zHTaIa2_ph%?CiT|l`wTQzVH zZ8JFjN#dvpI2&PIk?X$S1a=t8`j;LaR44K7o%SqQjhVp0^I z@^$fUN*XE_n`0LBsO%&ox@Epw>CYNZp{+r?7V1IJ>GjN9p8_#YOI}I_|HH4x)i=P& zGNcp!BOQNkcR}_R;wgVzLw56a14cFtgWBt}_DBmxY9Zh-F5;elfE(y7N2kT27!hn8YIX0`HHyXlctu9eTAjTf{0Q*PuXiQPr#XO zdYt*N3(-AiLd*l;2_uM51$^y{e4#pM&nGU+P6o5r?bjd?b>W4gPWg}f7d-o0`j1ax zjj8vi)8^A0O)YZ`j|DwHAf?e?y}2q%MmSehLyREx#3B6C`DMjL?&fCje@ zQ{>LhSb$J1*4j!f_plosY*$b+Y&CnYEQo_AK6a(QK z-}{!A`8-$@V7NIxzDf0hD2Qz90*2N?hIusf2iE9713hRUtT8Kv_YP24zBeLng8wOX zm}?7}4MMzk;V)4gUV1nnN?!B){vnRT#PL-s$;?WiDT$~e-$1`N5fxYukUgOiVE;FH zAz%QERuQGu(5V+esH|>tNbEB15r?u(gM*AAI7FD+FOhqkd+L7xV-aspuM-Yh#&H(Y zWtNC-4i6R0%&css50YQc01G`)&iJ`2TvvIlu z=C!g%uVJ7-h_Y7LJ^B*dfuKwS345^QU$5FFr`0%9L(gL*-SBF6%|=K?UWz9Zeiy@D>0# zH|L?4hp?5v7(YARj`z>|aeWGTe`!DeR^YtBvy11Qfl519kyiTH?1?`Rn?w zPkCPul}e>QT_2QSkG&mqb^<=?)vuezX7yz0<=Y$+J$es~wDjEL<2d2?$YM6^x`LDU z-Dr7jFTi^m=ZF*sZX|aKXCx*jNT7v%VC*A4_4{P%i&CH;L zU1fH5PW7zCVd4G4EZk!kvNgMKa_|wy~+QFDybRNO*j*{?huhTt)ZNmnJ&HH zziCK74+%0f#yzG|SitEE)pNSCaIazTe9l!jIeC-#j991LkVI+Ac(O1;mhEV8@1ax8 zqne^IoB*14oP7+Czx9T*xIHy$Y{q7q(A<6*EayU;T@=a1{QmS*m2&vB>>j6b9c)|+ zD>uk~{vx4aBc#yZwfG*@=cHS3)F>V7Ib<@msJ8bn5|MwMIu7XZcw?sr;n_5v`7O-v z=MBHNnvFrTMO6F?Fa^0X5(fCV=fKa}m=YxeS@N7=p0V69)n>%`W$t9U!WU_mC35w#Al8$+RPo@DrnR+-zFHRh9aMYsg z!!6lre^?H;brbU2Gq@$s?=k(Gzo5);dQh1BZpZ|HycJjsKn9+{JxA_#I@Ncb$+G;; zm7fnzMNarl3$Dy>mHwmVKdEE#S0)Ge{hKvaslgl+V?=wjn|08(kKD~>+^0X%Sc99S z#H_IU8ubGxPfAFH(#SrG@i7>Eg!M&(grDd&+e-1&H|I-OEe2F5K#dai42)V4Z z8to`v6a}z2kH|K`=qQwhHI%Xi6KiG@zswU7%DZxWpl-zlCx2>f1Ob=$)5H0I!@d3A z4!8N{c@pT+Tyr;*IJs#v89`l*oTGk9IF#XJpDTapb@9q22NReQGqt#e^C`eh_OSV2 zKYWuFLXe||k=Tc(;6ANwh?96tg1CRhWPuRI3QM|N-j@rqDm&njGk)cJq!;t;zxe?= zc5u%43Z6AbbPW)NAP?RQFO(MaJagUV8zqf1!T9;O+T_gGE=Do$|8t58IJ|F~5L0D5 z2tjvgpqPfb5nEt4$Y}vEd9yi;Vs-SJy4jMeuAXP93B3$!*eZdRBJYg!*eHwJhF%cG z?{3e(gc;;jPgNn~0mLN$u%G%xPy%k?g`ao3+s3F%LI|Kp!{1&o&Rl4V!pdmKu!-ucfNu5*rop8U4kaCxcy}$Gj7mR;OMHuZuf95zV)s_7>T<1TUc% zoEYy5DbI=&t=4NxP_Wj9vT7AC42q8#PI=L_wQTio{Xb-%8MdFC(fpUhz^-19VFMw~ zdGnSQn^d;Opk&*ke1A@aq>D!Ui^dP!*Xtc2d)C;v(cVAJ z+Wy5r$o|5SpE^r<=UMqvnfX|>BOVCSPeLM0DPgnw$Y65f=b8HyW^2J=_{X=Qw?Aos zEkob(AOsy(UFMtAgBsHF=G)iwlADM7i|!2F#3o4x^JfjvbS=~mclYWop=jt(sxCEq zv#NKzFTC_$!h!JqOtNA_vGyS)h*PZ|78(Q<#bq16g3h^9mg(njMPs^sk`a+~SNHNl;aoJ29sHqgEuI4yq&5jBSMvbW%DEZwn zIAOwbIJN%pw`bx_x#G{0m-h<+1rzrnhtqmqzA@R%`8v6dCOuR!NP#Fr566H z4yL~#$!~;@ybWu$$obwft^wfs05JUN_qiBa;lX*0HL!RfG$&dfmcKgW^dQ`~XH(^}fo>*~r zE)nAWGhgCaX;}?*y17W-09#U+@E$c9hCn-qcCr?bn?BAW!T7;i;S4Zh{No&_Z#(I< z>MP}x$vjgi=+aXH);%djB)xr(F{%-8$sj2eoOm_%y7@iKB|&j8u&Mxy>LPOcK<2|2 z<6@j_VK<*qb`lJ1arC*lxcND^t%#gw0^YY1T$c6bC0GQZE@BzQ0|L0WR5AI3i@xqP zdq3K4WnOaG&a&p4$Jq>Ym&$W9R-8s`~Z zm<2gS-BC1$c#F-OQ~_`nj#7j&!~uGt%9r3dJLnE`4=~NG8|=D^8rvLnA5M=@&pC-N zAq(+ZO9Jm{WEBqJ;AU^l?j>L6hKb~+*pA!GNQLg1VD$?$JydSi114;m>$g83!3@Kn zUjDy>EpYjt=oM$B=hAoFP_7jdY{oKaHE^ZBQEFBSkzv@o#wIx%ukI3?GJN{V{$@rV(^cUv z&h+4vkR==B9N@ioh*1UpD|P}U739`Vu$|}O%R?@|3iS@qvudXwjx4~(`hQ@8zD=S9 z7IXKnOhW6MCZu2PG`aq7oajV9s^pTQJ=oFCuE+$ifw=a&EBXD>*LkO0@T##P%f_ku z-RSklSRjTA#A$8Yr3(I7&7Ul!+;$gB9V`a{J1EFfWoT|wLfCtc+k^kV<6Dp$ z;+JEBRxSKo($%nQc8JR+uB-p>e>p@Ae9!e!ykpzU#Q`PKe%z1;tdRq*=InPx2F{Wh z!UN*?E`RRf_p4{19xCjfG##iitUKUF#l0Pjj!(4^HH#f5JX<#h%Mspc!gzZoveNb* z&%cC;AcpMBM@BK9CB-2qeXfCd)1fafQ6Wx91O4}1DO9JT+Li1UGi^qnhuKQ){9jlP z#M_?)WVTQ}2>qGs`A<`*|0(hVP*bCOkLC&3d9n(2o>W8*<@%O05OebTcldOJmXUBu zN|)@5UVu?OoHkB}Eqcd)TX|loQ@BaaZD?@b<-z+L@ia*!{JfQ+^OACb6;!q-wA5tv zJYQ$h{r~zF`w*b{LCu!Y2M1zQYPVR@1F4f|dcgj=z|1*kMQRMKO6sWu2Dk!1@lNfH zs;=wZUm9_vGuD;YzBdmyu&XZ|jFU4XcOXMX&${Mk`876$%ZxNXgtN)--ah9(+v79c zj>vgVo28~?1eB&Cn(+-Y&4Gn1PPb&BCK7}vE?^5*GiYfh z0`ShxdwKX-WTKd!554zD*aHU5wg_uM+$d0jbA74ds9d0$((&$!E*1J}1m9&E0m3=QFyTfGLA(si&4?HvZ}65)@R zFedAW$D2GS3xYPA!)i6Yz=IOs5P_MdB5tvG>#PZAxcfa(^Ub+8uD|x-th;HBRZ;@( z@RZlA?LW)$JJ2s{w*Xzor^!B~i!cHt`%d*sQW?t|I#A8}a+X=e_w+BV z)PPIMXbVBoc>27_4Z1iwUv+_Vpy_&Q8tR@=+wDDzJ}Z^tTxrx_I+L|U`VZGP?{?)V z#_2y{gddM65Li9{Yl7mk9)h&9#CQ8JjYsJG1Zhx}t8>$l+bJ2s{(coC-|L&e0tuj{Z5Rsi8YuETrXrFl>q+ zMl@KrQxncV5*a|QO;I)FWBgwgZOWlj>j$i4RpX11I}1~wANP%G`Pt4Ll^+d+>vM~^ z4>#XoBn=xx1QYYzS)nXUuMf}q<@&ZI1WhlDflv!Hb-L&JlaOfa8wYwH>qYh#1^dm> zFbD4OP>SW~*CsamFA*<6sX=NOA~<;Po;_sb#i9y`P7)gwZk4=Q;W)&cbl$5{fZ8CZQJ^9EKhD?IY=t5Jx8C?j1(23qu7gTCH7wKIeQpmQ@}<6XC|(zCGs zkDN@BL3F0A{_`HMhiaNe9eA(#hEsvePSTLDh-aYGad!L^>*vt<2);+~=ZgQsy!92D zcZoo)#G~nm4Dzj-xKXn$)jC8RFnit*YQ7a9a41qIqf-i2Gh%_g2--Rs_nui4ezh0q zw|{sa4i+@15+if-%=_+I1eLQIhxgx~$J_u@)d!t~0w^6IKW}8UQ~R?#iv7ZA{~as> z00;#XQ=1AG5ct)HGepUzV@xl43OgH+r9H4Sg(R78vVirPmAHK>0TV}6D$hY4JYsWF z4P>37yv<%-0SR^gWV%O!U`YUWoq$Wn4N&8v3#lv}SXI1-D&RfyKHT2hCxe>d9RDPJ zwm*EUXeO{CnAB8?WZXvO@95VS9I!50V@tC@(hRm>2#k_``=#Q%r>zTsDAZJrocSJm zT!mJ%eSEI)%L`g|LE6Pm0{+eppFqgBk7@vaZZk#y@8LOKUXZ?n+?s>wMX8^6@*F2k zw$S;X`g8s&$Xn=JuQ177g_1!1&8^2=x)=%ov#P1&8dp7Iw)6tI{vwnh@^5I4u*W08 zK!sNJevw9ZWUmaCz?=1%BZ*scy?*&(t}o+5>N?2in0-StC$Uz_eYOV`h+oj^*U%7N zlWht4DH{i3H;8Ziat;UeR8#R-!xh^s`w`S7rA~tGak1EBr?|WV;PUmR-GGxILodht3vnLc{E6d3KC{2C&@Oe-8f!GOHwGg_ox@*Q7J>b+-Qr(9?L}YN_%7=X=K_z7GzG0OaGG;E(a={UQ;}>> zC2CzD72?}PK_P;o!Xzm@9@-jd1*Xs9!Zx>;}FkY>OdhzZTR)yjAW%WVy4}u1ZOQS;7FLgr*hio{qS&EAEU) z4?|iF7kULv3-&+WY{Rv{@#aap`Vsy9xEd27v_wifR(Km1OD&b~V4%4L$9(B_)FI`K zO{MHyY5osM3r0maUiFOQi-Dh=-xJD)=CzWuGvj8rdDWG^Q+jW>3LlH`n4a=3)?f+c zwPD?%69*}rYi&Fk*jyq({*JSla87htU4&dII84P+pFFEOhZ6*CG9HVxETa+Y8ZyLr zV&Z-ydbRtF<{3}gC;FrJ90ailtTwP}jY3gDRMbB5%@hGVcZ!@k_)#7UL`(5UvtL)B zl2*w!A40NR6<5Ymq8=ryf9|xGFwAY%-Q6Gk{RNd2Vt*YU$^%0Et9Lzxb@sRj8nJ#D+AzCedg&0X7VjdRH`!`KjrD64! z1C_}GxZ5U0F$QbYtrUq=hE>P|!1E92Lf52jT1A$^q=llXr6LPjrh0ZXz1LMI(8aWw zCM|WqBFP}u#_|SKRL=kIp7;Op^%iVdZBe^0-Q7rcOG|f3cS}i!(%m7Q64EW*-67JA zba!|20DcR%d%xFt&$+%oK&&<97D(4lY$97nS;>W;5Lo>{QBO(9y(f!{K^_1F>8Z17T{PY%My#K%2G=T+Y zzkAP!W#3uYq!P@tySH^_xVTt5@5DiD&-GOqIBrN*y6`KavV5FfDFLNR!BXV!^gH82 zO>r5qs&DpgG3O?qOWR8nK4EGR*6PEHg8yFLMS&>Q4>87^B;!}%rA-TXpNwibz{&|T zpei>t;%i*x@f({kMpm@ikKZlFQoHR%8XD>zgpc=rje`PmUjPI)E2D-{9l0$lioVGH zuo=djXLs(E8_TEuwa6d`ZC?+o9zCPPP%C7Joc%>FzKbs+#<;r^m$KZS3Nlbc6jGVO z9}ZqZyj;E9_}d)wK=EFog~I2jNeygF%}Zq=k7)h20G~TZR-7aihe=K-8ae?B_!)VZDEiqY&Dod#%JqM>03Lt86fkL=@NKB^%Qq?i zbqFc=KV2X+?^o2#o(y)Mn|9gO=K^x4W0#q15e%y*eo{Pj*3aD2 z`JJ7I1aR*Y)k*nLoj@qJH~a9rtDV89Gkth*kOx(IfZ@Pzl&!rF$@%?<7NZj%ucq8tlz^v-Jq6CT;C@vx5$!` zE#DVf2b56TJEDL~(Lao6HABQCXa(%n{ipi%H#}CM{%gymq=vhKVZr2JnkDB?sz*^I zs2g_z_Nj;khD$R)M3vi}z^Bu4MrL9ZBYIxGK^?FP`hKU@|1?I9j>v)#U%_cr8f&2R z5TM0VM+SVOt_~kvcXB@CO+xRlJ*zwma1P>xU8#0$F5PGy2J%Xbd=1olEwFi zqU(jNZ z9?l3tP!bq6g1dIBZ~uE-|7)c2`~6eOZhl*C@c%U)zv0(XzI4uk(=4%jFgi$pPxY?B zG_P2lC5q@Jw+;!(wW8ZHj;p&yy1q9fX~=4NTd$w`QNNS)mk+7%(5hx+1x*3D-SxZT z6T#VRO0zro_?mjWf_&3#F}EN!320v8@106g=;3zk3WnRv{pVY=T1HY*z2tv8X0T%{ zcv!gEb;+x)Z9{rf|2IboDtJcY#0q{p(BG zaxIR&MIW{cr83{o$mbobNFI+^bx=k2nO?PDo?_|c{WYDVaU4_s=EVpXD4!>epFss)2h zM4`(U-|YJpkaoX0vzxD=sl3oY4JvsS)X>9ZLxeE~`WO!ps{Uyu>VhW5AV*UL+G4XGTfD5fupz)EL$&#I$?ddc4jYsbV24UE`s0LnPy zJ{Z^5e7l!p+P<7T1%v+n@_V+qn-#qzRnwoCbc%r`)^#` zP+lou>XBLPbTF8|`O8hd*L+Sux7j$l@y3snI>~t6Q9fP&dCv{b4jvnH{FE>&s1V&a z{$EFO<9i*o9dEHVWUzU*%G{oYX^#b+#jL8!t+0P=auG-`#81`?aJN0IiHUlaSX4PF z!2#19WAU-3`=24_00FsKXJW{bs=-VzE(w8-5gz*HA*-Yv>Ye}-F|6;Ve!Q@ZCvY%+ zMo>Do)>Q3VUF_Dc9ZG%x!G&)mTO|bKn$RH^ShB&g%zcaE0d*5GUUl786&!@?O#s(Y^)GYsB~5WJwxje zdq;NUlIXOsg627)kmpSdm;+(2M*y?E&-&lmjdg^3!uCc!`|TlDpU)`3vLaNGqVg%7 z;I$2r(qNDp4anA_?04K3EAf-K_?#OmEN9M@r#$aCzuiIJv4b#!AhjH3#2Va)-rHgS z4~C1rVn#24qqmwRXv7-CMpfj8Y>%>LMFr5qFq6a`c8Di5QgD#7#VYXdZvb?*j?T~T zj(&}_b5MCFV?J4E{><0MF#Z>}d2f&3U16Rg)P>(_eP78D$a_z4Eqw zm3k)J@SqFL$QcqL{j-1(m+{sO15}(49i|g7Ui0VHt-q|Xax68Da2?`ei}|IVorR0Y^k3^1U?MG zFPA<$=^4pV6aLJ(;;`i8qRAD$N+}2xntJ^s@D4UVqU@cp{e56|6#j~u>AiEl8PgdT z=l3|Y=~=Y_&67<0i7{x|HYUGUa3ZEPKW(|lv^ZpPmZ;!;5R-AdMW!KwR3p9{tjrbm zy?Fz`cBtiUqj>`qi0xCkF6dY4GsOn{9tiXE*#DUpQs^v>S=&e8A$r9CJKO?mr z2p!*LB}t8+c*O&!FTZ#hHBY23WfA;x`jjFz_nr8po=@Q z=aKVk#z)Yq2(9ifgHJhvR@g*op$%Wj^Ia5s(mrD9oIA^fCtXGDgQ$m}a%woS8zuzMie0k14jV@|g zY3mRPzYk420$x(@O{6;v^}6!wYkKW5|AL!y1c+;}jT!ezE!XoBJ0V6abaG=zYQs>l z_M)`EP3jSBZ-e(|_4&=?mp+rNGALRCgS7YPO-V&0LBaw6X?m7I1J15@7lR>$)t^_y z&4{(J74{!|J$LzYaz4-+bD-&H6_c}KG+~GRupGB9K)_A6+e*snZS*~&!K;zerSv7X zAt3s$QykDsMpP(%sKJ@d*ysp?$pN7F+xf6NR7*8R(e809DBBgEHOvtIE7YTK@%@w? z)aL+huvLkWUyG2M7emL8Q+cXpcJ-v~uLvQHAc_;VVn6yux$)5Lc>`hU>Y;`VT3+Qm z-v*TLiF|GQL4P*=BwfzX`&zw?U-m_>ChMg>gnnH}QN#7J#Em--s*e64{fvu#SZT)R zp1(GJd@{sVXv+#$WnOiPZQ^Rld){ZWWkNryb1F=NN7QAEjb)Shi044i^CAs7y&j3U z9}+IT#epEBhr)i#rR0}Y5d@q%gtGP8=g965*_+ZrfypWkVBi&~(QfP#LjXJq)jTs8 z-VF&HC)_QP!Hw+SLT?KIGllZJUGmPxU1W<2CTB~S{{laNo_)E;Jj$C5>Y7QDK>RzCLoavF$NA( zrhUGQky=o7xOn_5ALsC+q+~BS<;0__93~dxfMd+mOfOd3J7%4`6;$;$t%*)SojEh3 zq>&9y%Na|#4~~+3*O|!BN|MiobP!KGu~TT=G;cber_mV~?I+Jev7q?!G(Y@!ZVJ4D zFH*mpETLwzRcsxR5#0L%MoHPgQQ<`9g!B`sjm1$25SMTlOEfU6uZ?p;W3$+iBJX8w zJKhUe`=1^(xub&T8OQyP?jt9!<&LlhhXK_F(!xfvHCXWM+KJ5oC`1T%c=8}ry2L9? zARpZN_fekfK>#C?%T$<@$rSv7GR%n=8yi(0szin zd}uHZq6Gh46PfSZnD8IbU%BjPsEFL39dbT32e`4-afD+v{SFv#bY0hFh-h|!RrJAz zek`49Q?EnwWx&q)*jSq6T`2JTAKvGBpH0fD06LdWZ%L;Vm<=N4oGg{#vGg=x0hE`3 zWcL_3VYyeSBx*<$omIEte<-KJyMO5Y;B~WDBK@fbdUdEcqm=T)LRK-c63}rku8#8x zXjrU2M*b2$b1e?{w`HJHr|SXOSDmxDGYC47xhPaN{L)d5u7jMyqyJx&U{YcNQDKl1 zt9EN=<*V`CTu)=qKAzL%|rYwbUKSu~8-wr-bXUYXRjYFj;1%}@ceD98`tbZU=@ z`K{xHQBc=i#-_-4lBx;I>;{EnCYg^%!LQe`o8-AvXYzyf_4k>G&17)o^<=)}EyQ(n z;1*13jpzC+Mh<54w2M^-)FQRZ;?~2=*!xw8Lt7U&rd3{H515I5;SoPGPIQRgT*71$ zYRTtK5_QM0#-wN0`p4V06o?Mb9hcg2Z*IpN;@B*;uy1z-xd91+GZzh`tfl|yg9 zn5=Pr1iaj=kB>74>qX%yL125#z3PD!E5aR1Z*&D@Njg5^_v~|QVJjz3{tmGyzDQML z2>5FMz9~KSrn%Nii5d1v#-7Tb5PpZyGOtzRhpsI>FB{aIvtO4hfia6qN;>MnhsB)9 zhqy|MkFaLj6=USt>Vy3+sW`+7C>!XuiI$0XxfEAqBr7WnCru64uG?$Ef=lY^pF$(?_aJ9R3ac*zJT1vh_8?(M|IX`l2f zoiN=p>Jg2z2^v>r_dh1>5{)~~9&`K;*^q$K`^pKO-Ynj=Lr(HEC5S~l8V=fztWx^q zn5X1RY2kZacoErM5{6W@=3f;uC~O{ejvYjgCK~qckN2;2*VUU>A6W2jexlBE2z$c& z)qv3woa9o%9o|cJ(nEnxP-*bKx5oH0b~p#fq>zOHULaeKj~`2279TevzrZ}ZAK-#) zWEj@}Fx-plCiq@wmih1G0F=CQtJfLKy}o!G%c4@ed34%1 z(ca5F`f*7A&&&0FYzJK_kF2?fI2wW&*JiuZZZl}DK>($oor!Da$?8fB!V2e739x&lnZ@!|%c2 z_<-L^|1_U%tYO|t8%h(=pfV=p2+MqGL70^H9l9-z1e`fFNwh^B@#xShc4EQ-B2>^N z4>{%D8_04QL900qkSB6MyyDk68{ED*K6Rz)df#Nr=N#lfR08zZ1-w$fNeCKkq=mmG3(d9^tIk`cgBm^o{@{3eWOdDUCR3 zhy!5^f3X82Id0DX=Sw_KJTU&gg*J%8lMUXzb=(pAm_)eh76aXMdv$#;}Wv9=-ZYDJ^F3TWO zYv&O?%aG~ZjwN&S0r5kdA+pJBfxnH&Kg>6?57&AeZ%Xr zBc`8V`OwB;@fi!hQ$|#|_d2iX@hUg{2;JEmBoa8mRgne{e9hI1bV!NH_?Y%A>7w&% z5|>;wFg}A9X8L=QwoW>na1;CU-MowahZDgM{hcp%&|bfot_0T?@Ih5YH)-!~onp*= zKNI`7L46Ur$=@SX(yzf_e_3>*)NcY+kQJP-F!+n|y&KSmY9MY!!NAo@J~iIEDbT{%^!q^y}l>{_s$?t}`Txinr<6X6Dr;yO(0~|Qv`>GyW{bKjt zis|3c!>}|^5ur`hS(i@pzi(YwD$kRAB^Bu18~@ciJld{EDGEmXn+>5=aV6GxvP6TG zsy5%|>u8mfXj4I;D4I%f?D2%4b^pty_S{upiXZap3+H$pFv7|7b_UlBFl!*CLS7;in^7Qq!ixeXAHy@XkL&H;_Viq8bS+MS|gj3gKk9)dqcZ=j08 zdDPh(Kj->YQ*a$+yVNdJ7?6&WQVJL9fCkaChu|brXL&5yVPF-_MLZ77*mR=Tpk-e2 zXZCp;94vmwRWJE#l$hO;H#_{iG}`ivLZ*>q+jMNGrxFgkqV>-#I?zWgu8}_I$R)dX zq(Pr3-Oh6%DN=7{j6jQjw%E#11t{R;xa*Myk!P-vB~?y-0agDPFjt!2ZN*CKmZ$3Z zUsf6mr-du<(!1fYzqGjXA&&~Kgjt8tiTZtP(fH^KPq)JlDZ8UEDbRT{R_5C{dP2|( zZz|Ax!iaT&CUwsC&94r)6Rz8iUah8iG?K= zU03%h&rD@U-6@^*m|=}ceat5GmGN*9izOMXI;)#S&nYAG*{^$rQ*A_M4u&JyeWKMP zz)Tr=qJg38;~`GYzuyV|ZhG-ZfsMBQx$I5As_{PWoaZfgSz;037lj0qKc3p&4l1NK zd+5%E{=ZbrSj^iA!mCH%;K#RHNDvzv+18;!P;~e$=2dB3y6y4+yF+8;U z_%5XvtwH00Oo%ucU+>=abx*$&{BCu6LM^p9U!a@X(bydQa}@mTsB*n}sgrP695775 znjPFna(3zwQ~rXsCZXaZqxR7r`}r<=h}_ra&Us|zFv~DtJdB-ix5iZNrfv;Ty(oqufvJ%y@xlyUI+s^w};ucTU@`uJ#>0tG`~iIGglH1^CWuT+2_9l=Zge$quppPsyvsU#%pnoD4(H{nL^b5 zu8VzDIXGJI4w&fBhd^S3TJg&rL7U&SL@6GP>Ezjvi6Jv$3O zq@*tLNPp#aqanf7Ybo@3XX#}ezO^tr`Tr>nzY(;1?;H{Fs`lk{*8~$b8-7) z{(6;PXAckGq}hDiI-@5Fqg>#^6zev9@KIrD(f>$_LliJR8wBf1GfmLJm$JlgI+d2Y zlkR>f6^r|XL?auV9>;dVEf z{uGT%Kj6~m#AJ~NFvIsrdnszeR4`LS{O2Wm8{YVb?4=In_X~h|EFUvYlp(f0jZJfL zW3cB0yA}C3W^{6>4A>N#S3(L#t4Ufj#e!bBZ&uxUTZa?gj4r?YQJH9_1rsTK-DiEC zy!TTpK(ya3|Hw=U22=O~W4I?xp^d)0xl1RYRsVfLy`lYycef;rJkbmB0g#9D6lF*q zZXGDF0|-SzFQ!;7SuJEc1Vgv=_oFZ>4JIa8+RCLA@Zr*M=)i!vo?kVTX68KD`7X!s z>eNS}K&q?Oe5OP>;HFaH0-=XV7K^`qh)v^xIA(TSZc4U>pYR?#mS_ETzh{{?Qk0}k z_YvQ;XLI+DY|X)dC_!jGPVqm@CsV#_#2W)q;@Z39j$eWj>wQtP|7*o0ghk7XFTF>P zg|8{d?iO|K6bTIPxOp0mI-STE+GJphzepZ6On<+ox(qjjM8sj1cF2x)Bw_XV4Wi%# zLNd|k_#-r=dR8_)mdD9-p;b0un25%Onj)3^>6y2&Uw(s}{()1&L5>2sOA@c2m{1fh z0W-xviZ`Tjx$y9dC2hl(ZwVW6u1+ljaI_CI-K<}Xs6;CC2>>__c>Y$_qH9Eyyj507 zR&Q_1%Axty{4KXUjcC`imPT?muDl5^al`P{JFae^G>(~>`L>|~G*(t>gs1XQU;x=B zfzORquIuJiYxC$^V1?8y89+I7aQrt7!n84rYQL@3G89hyrj3{R%@K+|wn8_McgVHj z;`X6~2Mms>QJCL=Gz>TyznW&|6=Bi>N zO{1G@yZsff_gAH2nM{@R*jVPxOB1}GK<`KHr4$&a+9Z{q9yO{)$vjD^(V0gVQHQuo zWOt&8j_9Ovbw!240E0c~~? z@=N3BzgRltpU_I^Jy>;xnvpFNMz~#7?YHt;rjqtnXc_}|MXUKd@T@!y&Kp04V(|F^ z4)7y$ULA4>1VS5q1u94(*zlCy)9~o=@#PrgkQ>av;>RXA15#2B+3d<1$y-*Ov$oJ2 z#2d_jnMx%%>n|Z@E$VDyu7GhIBvm)+K&!NS+qr5R1PHu(_TTQ0zhQY^&8saPKisl} zx@T$ZB$(uUsR&^#a#*mZQXSmuTDQPbe3~>v4&&s?o-uFbSVJ{M2Y+Zn^U*m8wPZKJ(nl9qpt7IRAeX< z7_1nwx5>Cf%Di{*8YPKz({48^GUynE<#GluilyYV z*B#@GvZe{L=>siZAM!N@rywCz&y5ZJ9UrG;37^&W{p-0LRUH9a! zvQf6x{4ybp-9Iq6z|$vo-h5}`iwqjSI-hUth@&?3EK;VaNaB`)3cg57ykdrWR{_tR zRFmflFIHOUyRYPGb7sH2@1z!0oKW8G#Cy9vW?w5XZReddC4I|n{RLR}MkeO1R^-0| zOTx4?3u&iu_pUmM(jB8@NroMmEsEb6ryT#FDcOjUqW9pXj?vB%dx=M5JM`v4hq_Jf zCq&rAC2!KKPS0ymulLf^Gbir2@~lzneUNh(r(U0Y?M9QQtaaSLaf3@}f~S`5m7Bq0hE;n=BEVB5H_L4Eeh9zReNM(y zc}+VRZ`B7TA3UW2frN6aYU$ygm>Sp76)$}lp+zX1{-nG)=Sq7HrQy_{RoT7*S-6|h zBAgbzFKlp;qZu-O^l+2ILX)ygsR@7{EB5*Sx1_ec-Y3!@XML_w5|7T{D=W$cSARD# z)K}2}gCN5|c#ll0l$_=XH&4?yeTfSlVVP-=t~@U=hA|>Ko(6=P5VArn0Pz@3RYuHd5{KHB*-pfQwspEG2DF)m*nxP)03YZC~37zHS+d?d^?x$ zf*E@Vqeyi|kH|8_C>7RsIUv5!Kg`2+>cTJA^;M(#`P`1b+}CNMW|eJUJ@?^xHK`R? zeaqnDYMaBWCsrzUG8zB<3GCrD4+B1OTXqsm&VfwjoaYAf$ru7?ICLgh-dxq4y@Ho+ zh$5Wmvh}qFeB&y(;nOhMqz9~@%yL5en*wlqpq9k|EOWYXe5eiZNBbU1gnQFFs;9Oq z^4m;#Ki^C}x4KXEk%ubCrV)<>Bua&_rkI6gn>BzP(U9MwuoM_N%~iWT{6E@etVF3b z*S2rS#Z`su)JJFdw*@0v!xf_6ufYT@nn1shNA7800LNGOm2NO#_Y}QVN7retQneJ% zY!3TmAo(7;d%CxS8Y@SjGPEV~MMzxM{}_9sJ@3@ZLI^WpLH&fTD#jQOTFl&f`Me;Br4)% z#&iZ+T8|q~PrCK4$Vu*CJg^qoQaK?qF+Fv#LxlE{VPWOa^wko_1d~hdA12k8y$;CPXQGKj2M=&u)a$hWNH*xIb0rNUf4Z8M7Rg!m&U_NqHl8I-Q>a?*t)O)1GisH3 zzr<)jGkAsqaUK7T@?nA!YGZscCc{M!uMTDwcVjK*ujn9~H~3%Pm?A8n6<{dI1EOhn z?d{&w!IFQ`lv~nP#11;vYawnw4mPC1AsZ$i_{`^0+*^Vq2wHBzCj?&Hkoz%@9!;pS zsJC3cRgTFgito4nXwkm-{YebzG1+5w_oHz;XG668P=fuDm#z8CcImy>?LB0+@@S2E1VEFBVsV2D$%~s5= zo+{y=Ih7cHGHlhSunORW^Om(ANTf*@X+3;L(o@>Qw;fv`Y=91E$_ zOIj>Q?XunG<1qQw`K~W2&qz8HJp}x;wo7e!*>kF z8K7Wq8()a!ZW^u=n0KTu-Tws_*{2?{-q;wRO=%333a@*c3~!;oNcEyoLjFyy)I_o~ z#fKOzvV`i2uMO18XK6t~3}4bfwM2bg6sFHKG?-eOf$~QuA_R1zs=qpsBAIY!t)}=- zqaKpNvlYc_-zJDYms(`~i-em?UI!fi%OCIXby#474!3I5N|b5=6D&t;l&UYmBz%=* z;)*3sh<4q@ZJ#&IS!vzyR5YpzNS!LkGp@&v?yid@OZFOdc`dmj=*);;L*$*2RUraZhQSbp{AJqR5~s7uxt?>}v-Q z7ul=AMLzA|`7FO-$lnOn>J6O_$2w*An33pzqzbk+4g^8%7r6h3d6^lN5jWjPMnRDD z>&&urDy&$R1SGf~Sd1iigxxDuwjX@0xnT#$CSv{nsIM~;jy12@>X^&(Uo*Kiod!Okc!LDeDi z%W15&Yw>WdD+^7^cfN~|vSQ4AD%gx7$&HibI;7UASAk}EIY=GwG>-{e4S1Un)t@oC za@FkkIg=N+-O!;f1)oCLSf721w*`0b<@1vpT<2NXblrDQ)1MGzoqC_(U^SB&rKr1? z4%TfyrS#p?Q%b5^Gf#wgzCSz?&=wiv`B7fuIbBz96YQzpdl+^Ubl8+&?Jf+^bDs+~ zt&9;qQ5I}?sL+2Z%xCZ?OyJHJ))#`BFz^+E+8%!LeRTCu5dWRI_ITBLoAT^HL7GK! zxi=am`T$&o>`0{@j-~Y;Ub%s=HG0Q$Q!UFiBV}2p_}5?*Jhv#&Y@#P>PZ(Oxu;4K< ztUm#rX3SA4DK6STrY8^6bxrmqpNc;_8q22j4??#|^IpLS;>(toh&<344KBS|$;lg| zUav0J&iZKiFwC1*=dDy8yjfgxlpYV&OK1}FB^EM0>7g~F@hdu zOU2od^E!SKWT<1lJ5r64XLY+O@kH=r7~?_vZR&>0xB(|biNxHGAxE0^N>>8^fWqb!8pEde7;h>{&K!vBr&a5h zgE7ww*xNPBSy560%ukIOSm2u#GewJL+wL*6DoSbu1^=1`LFtr08-`941Bq!iQYx4dt#38ra( z4aRwKuz-_L0I|3|zH|n4?yTZkseD-WYd^_fw2)Ay0W8!1ZXulVzLHN{gA@j}d31t~ zeOjDPouohQS3)JT%MK+|(OJhoBFm6JEiCGt{DfO?ZAf?@z=j1x)nbTgzmoP_w7OB? z+fqxc>llZtX+B=?+%zu=X8C7{^HWw#dWPn~CIoQ2r|dyZDL~Gq@bnE`z@te-L{u-?ZuVhk1vmw=k#xPrFquM}iz1PwHbv%?I7xJ2J33zz9?tKT zaKqIyP`J;%q>cHAEiW(5Xjr$h99yiHcw63oSCV14kDs43e=|Pc$@4%g_#pcvSg2R+ zr6rV$-no{5@;; zNWOfmVbY|9UL-eVJ6+1#D<60%R$eHj?s++AEwcC z*3D<2bu0Wo1jxQ-`bSn1QaDu4xK^^uA<&;kV8&ydh~BU@+4#B*J$2aAbIg{dATo6(E#E#t#v~yo}+J=ht(< z{0kgjqRA8^n{-5D%M~AGJsXz4^h@v?91B)gN2o}+rPMjv61AdS$u;*tT*BFBh1{zE z`Zzr7rIe-jD2Jw!+oTw3CSZsS23^5Ng9xEGri$MnrrOe*eZEs~Df|Jl<``8dC{?If zRT!xpDe<{bCgG_I$wh!Y+n?TwS|;JBMBS8g!cc53i0QmL?))b(i1xa?%g8)H0Xy96vnyG!;C%^ z+Dc0?biwo=q$)FSoA?S!})ho)#MJxiSV|~9kAJun#-Rg-KVH1_;yO{31`C)z;e5@4( zWm`PPLO2>QE>j#Sc&X8fzCY2CpD#~YK8@J3&NKfC%jRwgy{W>91BwJ`1Wn7{NraFP zY_7YX*u3C4mZ*s&x)+m^aCt-gk+lP?Sk&H!tj3sG(x==_mw1Imv)hd;dSH7HSEv#K zmiYKvw)b61KqBx;K?(eINwO>Sr&KMRMA^@hIwynD?W^hA2)u@Y1_;y-j^70h2%<`{ zH14R|eG87#oK?3zE76Mnc+gSD%`1!`^R-mRoWx|qLHaossW@rd(&9JZ(|*jHN9@%3 z4EU-RiOcGa#*gJsk3X*|N&CdVREOaD1}>-Q&s47KQd2$MOX)KGe2_z?SLQONk1cLH)+igNho7LFm1IkWPI zy0{YxK?w#ruS@H-|4hCPIwKJtIU}7r0}paJ&9O!e*dga5nPP76?Tw6dr9W~l1}T|T z)Lnx#EWDDXo5uz9(*6eIa}hSD2W_>D5{tB2?`Tq2qi@!arX3n&qX&0k$htDRF%1(7 zrFXMb{{(Uohz>m5*xn9O6J#x}tKB9OTO>0ljdNZ{g)@nZA|9!~AzCVGw+=lo`iF!W z#)1iOta^}Lk<}L1MwEXkuTJ?r3%IZaJ@GcWeGqY-FwT*vO6p`xIY=&Y`^9GqF0}fkseePbZdJvd!iRZvNSr8 zJ`vB81@wSjOwcNYFV7c}#=ga;S{<+xfj~Cz3vVYWucqsusvhum$a(%6)=osbfZ^+a z@0`!-pKDdTgW?Y-Jq4NgBG%Xcnjti0Ng)#y=l!e0ZqAuLcifoKE=bqiC8W~C z$q(w=J9(n$5H7Uu&;eRwyBeh;abLYsveo?ERf6242q#o_VNv#O1}9FbGa#o)$0Pi* zBQ1z7R}y>Zpq;oKwEJD*-+ab}*Hk;qW5e6e)`b-J&FQfLyIH4d6%ihy=x<^JzX#uQ zA`_Nuf8^PNnpTzTNnu|)2T})jjDQSr?O&;WnM1Fx78MfDs|<~9GzA5^3dWF`ybJbb zC&S7iv*~r)MWw4eq1$0Dw|=8crW`Z7=jz>T(J#^V{I%Rq8j%(>?1)+qSj8U~4#!z3 zP0A0+5AaaNL%V4d% zY5nYg#S~yYE$qwn;Q0FyOu)ymI&kA8{hO70pe#LJy-q6X494#)o11S-ZvZP2UR~aw z$t6VCxtnG*sAw_3(KER->bm+TitdLE?okvenv)%_lU+i2nP8~YnA)kkh6GADl;y>{ryCfmUJ*>NvAy67Z>$`7Z&xD0MG@E zSI2b{>C2?@?*w0!DC~&6ACq5om5ydTQ!#ad$n}bQ+7ypHW2m!Jzz?M#_V>BObG_q&5yMFBs zMSO2v1*I&h^$`74dFlVY7>;v%(i)u9*T}qvdaWv`iOxEoWu0W75SW*=qJP_?f5rfrJ+Hdv>4mMdgFVN^BTm_Czgui+P8HyMmY?1#YDP;^_4CO`C7^_V_q%z` zc$cfaf~IUaXpg8GywM{9sG;jI`OL0=qtCDDePvTQ=FoqsUw>|mn;LOU+7+BqQbJ+p zYDD`UMm|gduJh|Y!sxvO)d2~v8f-2}(pFYQSaND|V(MISRAyE)2?5FxViBX)s&^T} zmxehUlO3(-8VI?xMeyZ-2I)0PAYR8BMxcwnzVj@oyMW8@&mNtX;-M z(dX=Tl>Y>o7Tya}t2(<>jL$f#3eP0~8*y?*z$O-_wEUsXI6Tbcuw4R& za7UiZ+lBSOW<@4n%hiBU-~gXPAVR~sqz5u$<3kRddL%0Mqg~Rel{yOT`!OK@N@P%n z8eQeKCxP^#CB{U8YiBk5W-^bqSCvI{*6RblLTu8Vq^2mTs!^fo8lzm;zFUkjAHO2I=Z{vGq_t1i8I3!@U$A+`-`JWSO#Vq~43o|OnCg|;T>-nLW9Ek? z=*4n6h3C@CO&Ro@(CT10GNe`~peR}+Di5UTS-m$RUp#rR0dMM{@n?H~Un*Q9 z4;SGIG`9t7iBUJM*Iu5WN-Yv2m*;4+)?_wtRH%}>C}&qzb{>FZZsx`AZ6aKSjCoE8 zF@}Z8?Z`NEdRAg)nBgG;=P|6y&X0A&faNfQ;--fTxY>bgu49kHH?wEMY~J<^JVfQ@ zu&>dRU3V$boo4Sq;l%x><`4k4FaJm}H+qd*HKlb;g>uVjJHi~`;}0a$G+rK)e$HGz z?*t){@i0Lqj8O4#et7Vt;e@2KJjj;fr@Q`)uOukjZY^~*=lQaA`dmCJ)tyL*X4x;| znz@crsSSxJG19D3suFx-0cev10%`nqzq63wB#JOKOIY>@4ylH`D(SGQ5#K5DUg99+ zi!U&W%hQU&OdFI17Mfzpep^e`yy5q}d6()_qw1=dI%A*~VB4u(O$b@yooUJsVmwZG z@C3H?d}0oJ^TJjil-})svtAq*vuMeJ&5r3M)P3lccPOL=E&Xf%S-*E47JdDUhV7<#oQ{l^3TDPySBgtyCY~Mwr9Rtg z@F+(|BK$Q5_8cntt{O#f5A7?#JH5gFeP@}SwqF?G6F7h{5l(S3j?0u2EwdBXOI|5W zLn9zmP(PSA(BFFCeWwMIk(Dzx{YaU@2#oyb*2Z0uzW7ktiPGC9o)@}0tKtu~K`vMQ zkMH;OTv87E%FgH*u*O58&VqCNI&Doc#>qA_7>sg4T^|!&e?oD*ZoNFf!UAnnP*pS9 zLXGhfw2l^}-fn;#q_3jzn%{pa|2@%VU3=C##Y6wG5(wsd?-C}Y)&o0f_c!Hoaozu< z&{?F5vS(;nBI)Cnhsmiq5ee|A4AmIfY8=&(8Za%MJ7`cK_ef@uF|rxag{~2Ynp3i) zV61r2NM*WlmCZ2|z>w;bs2~%R3u^^1?O?$n1N<3>?em$Dcm2>ri?|t>@Zh0+s}aDHl89~B|0u8 zJ)*)-idwNUB*!BD2Dfv$S*dFcHTd!&$IeHHYRPC==jTL?nQ{Vi-yK?}OPSjb4**&$ zN3gwzrH$Y<92m?5~uLF z``Bgr4`V8OENvMVu?x#<$g_f4nsT+t+%M}+wd$Ts2VeHfqm+Yv%N@DqVlY*ItW-(4 zd!t$$o9&RPp2;V2wUU0wW5WTaYpkemO0FCXq2u2K)$_K=u+KT|sfqMwgguPC21oM?Jn0Ce$0(MeI4`Fm>Dvm3yb=DMJ zu#M*psGnoySA70S4QQCnmEL|+asBF{qFw4-xEd;)S=+GYo)mM^2_gpKrL{BkE~c2U z2H%o`$vf6fS1B;7Uhf8&BSew>2@4E4PM=t!#HSgb8a-<7p_=OFKx$eYQqrsSsSqQBWk-SisssM0|X))}vl1;xeUI3hT>1gos$Rh<#}9 z87%fd!3XWQBxJR(?^mqI=dwggaUN&H6NrwU#xy{upz|;* zx2?Ev9>^rzG9dhJAWUaQ1v-Ib ztN-l^Mhd-1aJltT;vx2}F(kUbX`H>=RcDB}nM&3)Vu14xvBb!YWlA_a_}OXn4@$d! zz;)USJE8M1@hKJ0Yt5fqwDvFDg++>%|9hTRqo|2 zhY8dZb@qc+@|k8ifU}Y(+m;7C`5KqSQk#YHVJ88-@CQhsb#Iy8iRTH=Fh?52m6SQ_ zzePnmIskYNY+(b*#z+pp?bu(b(VO*`N06`}txzM|qbac>dfIRS5YI!TLAj+_nK~v( z5!B8hyo80|P^Mz{B%yWb89T;RY>`x@B~0`I^wJx&@QLLFmJp0RJ9fo&U@tCsVfJ8U zY2$*aCKH#}|4iqCDz!gYhrq0}Fxt=fM!ucQH{hs$R*7=uo=e!ghX@oMm*Kvs%%LI&zh zNvd|kNTSzid=)P4e%Sv<)?Y_;y>(xpFd#|_UmEG|lJ4&A?iK;*?vO4erKG#0MH)fr z?nXiy>3%nQp5MLixMQf}oZ%lh_TFo)IpN?bknVfBW9S66QC?T^I)Hx%=5Fa~m%n4?3njv4(a*!n)4g_Yjo zo%W+5xjIdj0vQ^4F4BCeZ9P)n23|cEOqyL<00*?r>(J;-w4BgE51vUiFx7>MPWYqF zowzJ0=YR(z9hqKhKJ{f}0Y^fyBbYMBIw1g}8rKKavE5pOuNvROH{jy5y05T`T%KVu zunqZ8LgK;_P0yG}Iv$*$)tL!ZF{{UO7ZH$A4VW1jhT-8QDs3iO6O*^!4eYzGU(;3y z_&a|S{lC-_fWR#cg5eL8L=qhXd1l6}Y6;zk0v?j5wR8(kzKABL<0Bqo% zObI;DY-dlO@@k1yMY)>|*$eu|($lPP2wcp+KXy~vY?q?32n0;RH}<;7&*QLCoKZa<^LLAV;AcVczmOM({9BU)WW-erc z)+LkVxX^6~q7&F>R?BjfD+1m9rvWVsrlA$fvmCU*OwCX{D4P}EGjHF89V)Qx zu_*&|uYu+5)?13F#{KdEs8Z+AnLOQ3Qz=I8V>yRiHfTf~{pplu{hVNnksAue#H8nv zg{p|#{C1P|OB~5X5y>0AL|O1r8HWUf&;-d+SMgUe<9=4%ppVt)NT3cZP1GeuZ!3^m*mV`7x<2!w}U*{X>Q}0dqO1cl#xJJQdH;P7+wMJR0Y7GNU zi^>FjqRiRPs*50xw;bpZclY*Y>I_+^30C&G132CxhJ`qjWnp1t)%qeijQBq=jQxMs zn#z#?Pa#%2)QgPfH^JS-!aaqx31Rd5utiS_i<;vf<9|Bhlb)Rdl+y4FF$+44QEBzG zfaQf^PPh;{RgmHy!0J+f4cK)UulX_Py}RtOMBDhnQ~j++SYXBUH=yp; zB0&_seS9A#xDqpaHE1kqqbr(_Fm}oUynWVmeTIb@9aInaS!ppzv(!61fz=Vj^@8fp z)h0}<9&V4ucBXgnZ}pY2j31zKE+0um`$C`}GQtVaH===gqvu ztyqC}c_)jI%~L2rRQpN}J2Ew=*Edxd<;g(fk~b91k+E!zTrlI69WR$NzpgfEZY)mfnp<} z$*6C_<7Gob8rrOdk6@lU5Z8;>e>)Hz6FOtzL3Rr864vO5n0f*Xn9uJtN};cFgDEb3 zPTlZ!zqM=3+yMKO*Lhk?<(xJ%;#iL#a5r;C+?KqTD=0B@NIIB?7M|Q@K9pTTvw=({ z3FC->0k>iK*qQ5Q$L1)2sezdj!$eGw8?M&OBitd)fzXss`!8jh=Wz8$_~a`VR4Ls{i*fA$CeT3E^VGzQxm%1*640{_3^dkkQV}}#L*2Z-90gQzW*F4o_c__~Qvw|2zLVe1n&qKHdQ7TZj zBd`FI1>Mx?{uK38Y1v=0m0}e|e5LY!keg|?i0^%(r=yKOVH9tAKwqJxEMpw8s;r6A z?9TX*d0jeY-~fUtblknUtVD=O4tyPAyt{`8e?WXkOul;EjcrQhNO9M51kc_o?oj+j zwNSI+r&99vIXAqwcU2&b80CS!XU@{EKn94{h+sJI6@Xu3FW1-hq}@nKANUGyf`B}0 z6pMbA^Ki!Z-j90B$m*dGt+nZ#v}dkH11y`txn_O*KeX|K764eXZQ8Vzq{Mxbk=ere zS2E8<6F@6pn(_ev*m=Rc-T0B=*{~t04UHI1em>NCM8e^lwCQ)89NAdN-~Dr!dk0^x zi#uHZ`PBWE zl{@~=&^vRSV4z^-&J`uQoV#Q`$EE~E8z8LEKPBqa%1C#U5DI1GGvIwhOP%#Yr5eOM#PxlcHlL^4?2^h-B=Zp+rukIoRZ(yuOUyCfj%`-+-YPt zuZSW$oG!~u;rTq918Mju?b>~n5tOGW>fQb|!(M3Yqt%1yEfdkWPfx~lVNkbGsHh4s z$tzm>J8J)%1+YGYO8Ydo@}8{cu1i$>$7VK|@!Jgdg!w=4LIU?>YL;+?x$Pgsi=Yp%ZM2qcermYwVEcT0O?eFUF+4z07Wb)EY7ltbLrXDt6~2!-S4U ztJBenHx)oYAm)s=9US?>u_$?j;g<0&))d-PI5!AnqMRM}XxJvbxQel(IiXv#2~Wq( zUxzXMt9vQC@FQZJ)s(_Bl`EG$b9Wovu8|vMIC}x;H6@%JDshS|=URIsrg!~n{mM3h zq3SB2r!WboE1`gekr~$;iy6xt>0w^Z$;!96hzNCJ9FJO=vESha%yzoSKOsx6S{qOb zSatphn8K^D%xN?y%n66vpL9+!PQ(W(WKS*+OBAFc!^fsK4fhEXDl7g8H7k~0ZzrxR z6cB>5A2v9aGG88PKkw$#belNzeT#=3d6nGm*(eVMUeuAuYMm^WO(3h}bM!BI?(8&u zV-H{U%=xI|F%ZIgu#*KM_K_o5`LU+{uB6W)lx(W>Rz1sR7Yk^YZ6Gf}^BLIrPb1a} zS1VOEw8dY7#jLKJzIlYifW7z7)nd%M8+W`nRy!E69WQjB|R`uBN9vEgw< zu#OWpQq0RRNz+ppQ#v<_{xv9B_WF`OkZK8Rpm7S)9HLd<8M;W+Ll_5L-Why766j?0 zCV!e@qqvJLqEyWphz_(G4S|CJ-H4q%Cy_f?}|h#$mI_jfTq;X zHrG-EiZEDiRhPe1ovP`2-(U+k`95(|x-iBGFgFuvw1PPN4WY0wNkht#rNIQ|kc@=> z17r>X_7&K_WbrvsDd%}pKDqcrPSfKwU2tAzNqy+(YtQb9_8*qf74!5IV|Hx%je*;= zTK(a*Y?W)V7CP7fMo>|uj0&ULPwuaKyk_G#adLJPZg5cLREC$=b;ScVg<}OKKa1T2 zXt5Z3kGkr6FiBgVY-(W1pX})l&v&wvs#IR^|4G+vNy>N+X5gP%8V-)Fx{2MO?G@^_ zY{T+b89P;3yHHfLMJS~sn<(zW()fzbQL6am{GJ7qF<#2NKYN7|_yZ=0k>X2`Vs}xX zAjH|0-5pKO?pgz*)>EUhBXX15PmWwq5j;W&G5?P9XcM#a_XOR&eQp$}!axeP&DK!Ya zh-iq_@$Y_@xnOop9T;-Rd+f%NXJQR=pWwxbezA`JqE;lGhajhq!H6+cbIZ$G(|G+g z{zxE_W5a64a+nWOBr2je@edZ~hLxVOGXo^HF1y?UH^Rh|ilYw~``^}t>aMgu4LI+@&(J6=cR z{Z%NKbyLQEWz`=CC9RV0+6H~-lq)zv<7=%L67p2Z4iCQlu`#YZ!F<`;U5Qug`>KiOQXm+(|er+Y4xah(T3VvL- zQt^@hx6c(&1o^!3l0J8xWi4*v^Dn$fnw^9oV&H=<_0+mi33_n@n5MNjcr5{C>$^9{X&6?{1C#}FDFzaO9sXTR)Im_1Ydr~;t)Ch-iw`2 zL>YW|WGue-IwfD$ZGznpm_l`0pS>K~mg@y8nsM#9=k(eMt6RW$?>$ULw^p{u)$W97 zBKrkc!Qf6Wj8AOn3``Z5IL@KQ{D*e+xdup18+z97jp7ijsD2L^+$Vt{Z?3zX<5VU9c5A@sRz~5eSp$2W3a=_DEphzhW&}+9WE|^oUzH4>_L5_r ztYJ(c#~elV?pF~Ux<)~T5MiqgP>FyhWO+23URR&A_I^GRw4YPz*k_~l!%n(y z+rav}IV=vVXwV$<-yHZ!qpIV9CSv7^?DN*{uL+4SzRnc7>pVGO1^C-z{@4qHy4gDl z3-C#MRC**5emu&?zpfB!f15qW%mwr3n$&-JktQ+D*5Hv)X@s&dyD65juhQ%~i+hTK z(~7DQhO%_67nXn&WPGWeSNm^;X++6EWefI^*wPIY%cXl^bFY4*{CMXt#SoLW`^j(=?$_h+Em5D7>mt24Id9^Do0%`tGC9(DbfHf+ zXpQzmW>jhbN(N2WSANX(BdUGFtRJq+Qn7!%E^Wjt{yUj54UKk(ad55+qfz+B7FfOo zx%1HZ)2lVkidgVprwM$PPE+^qHJ{7Ot4;lu{<#7H?Mg^5$B8%THW64)aAZoc+g08$jY_QU*mYI>^>{lkr>v{x z0_L9&-j`WknY4g=ygT)@#AOLUn)#R%$pRLL*;`V;S{K@e_498RkED3?xlBk!RsU8r z@?n2FI7fHf+2I@?j!1lc6izD16KtU$hr#!ha&+kj{PK|8(WcwBq zjFoV8o1^R}ggKlI_xv;=l_HKzoD`U6$|2sY;N-_khlWs3URtWst)r_#+{s_5Po>hq z_ZTWk+TIb4*2<_X2o@sHCH-J#-55y=V0xmrCs3~eB25BriPYD93FY-4MA39k3FU$%9ha@v3u=E-=6bOJZkk z5ZF4U>RY26S#I$=X{=hJNTpQ_xaZS_vklkVF8QEjEUKVo+$ZbMnBI|8Uj|A;)q z!bhzERrAat z>7SY|mm;hJ;)Q}|kCm}p3bKlQhZ9`Y=d-R)?s))9>%;~DbDU$6lqHwtxwQiFG+)|x zot@0vo2bbBun%2>_x-Lvt?`$w1q=~mlLpQh=}Sj-L(pnMEs&+Q*%5mrF2H^mY?NE^ ziVJSq2ew-L9E-(*~;| znLfa2qXvC;o%@&P)+aK%1i(N#E(JQvy!5x@(e0Kb5(Je!Fuw=3v1q|HR=`76z#N!n zkjmNo$TPgp7DJ|=sLoo}az@+{`f{KF)p}vn`h$6%fCp0Iq<f1*n_70x_;qGCkwD&Tk04^7vcF!g~jc5!LS z%w4BKtu;czIU}MiDJ9Gh2-(1q+>Knj($T}?iJZ)n8EO%1Cd%K=*(aQIk5p>seW!ob znP+ihCgKUjYC{)TFHe@0IJOT)6E=kF z^jd2U4g-soyMS$#`76i%+WHfHwbP>;3AwZHBdwA&qMK!WN=tcnt0R??>lk#N3Klgb zzg%~2k-G=9-;@)KmvUy3RbLD5vS1Ng*>@~^a_hgXvJ(kbCy8+wC2^3^fI3Vguu52&on8YQ*p0|w|x|K!XL z`G``cGlGCp?`fKD5fd9~m|HmyPlvWZ31laFR<>t290Nw%BbGkmQ8f83D}!x#GVnC7 zYOx6%_V?fdF)qLmA+>NKVOPF#$a^rY_9dQUzwXOGzj*}*NTgUjE3!{c2gjE= zZ$DdMCdqB}^(kp?zio)dUjkoZ&_Q3|P75FpJW4Jo&X{ogeSR_=+X*IPuq2-Sp^4!0 z(NunW!M`3!rYJZ|NT7Jo)+uew4h&CR0y+;XA+AlP^1%!JDqEJ%i7jt8X}F21wWUH9 zY%c#uR)!H(`xc7U z&>Ssr5upl%mFo8D^Ze`&)uuMj={#?SIJZvDTf)!4Px%(ZA9Gj-YiW&z)8Zjpt zJh$!#_Yd7KuLqI4x?;iz@ zij2Ar+tvN2={q$(r+&Ql<33D|)6O^-xfr(*5h~*M2vMz5DE#um9uI33zYizA(##or zS6Gej&lNe4|KxT`(zXQE4KpSs1YuUF&Le|=?S^22%k5ssKwtHFBI{<^QHzoa{rXR@ z$YiA>=a5l*ilUcoApMSbmGN)cKH_I{R;nJ@n@T>Ti0}bW@IN59xASpH2u(7Avt87- zmzo45t4UBHWQGWciI*gpCKbj-h|trPYAwI46hzTbvvPq?Ev`}qvQrRWBvjJ~Z23Al zx>2_QMKmv*BJl95@2#b4AL*vO81lsO@?a$P?ug{G-ePpNXKR7Y3D=G{_Y|Qa>(!Z_ zAGqhPy!|rStu;Kr?XVX!acPIOZQgis(o7ks4KJbnXK)O4NX{qRsHusI&|iC^#~BLB z3g!#*W{mEM)3R@fNfDyEq=;_y)N!1bF%wo&^1@dWL&xljaiP2AIek8jY7Lc2{;vk{ z2GJ4=wi-+gx8k%5S$$jF992B`_i-HVl2A$BNGd{ZMBr!DG-Y^CyOqO`|V5THb zR1s$>(KlkvH%vA-x>@8Jl?JS#jxZH6Cm*<-$Jz-!%y0VAD)2wf*B=(+=|7A+dHHu< z*c=j%<-HbgHAJ=1txEo2FK~YS;?vcbZsk|Q_S|=yoG0A-2*=UxZo|5-9rKl8?(%8( z-i#-@R_^F5vAx3=9m0%3oq1tq&SLl7p4a`4X&;fmP0OLnu<~_<%AAko>HL8)!YHvm zTfj|Y)wU&G)JPiq$wvu3T6*$UCypP`&F6IPf7Cs-5eW8ot!BPFF0rTXnirJ0kaD_` z8h*$@Zo%!@!Nv%Xh8Nv0W`QQcSf3-@_qqIe?m5|_Fw9Ifa{rDz#(&0_!0ZEQxDk`Z{*v@^N>SeD%fISQ3|O&OhLVfw#Ia zKcYlx(QJt*nW5sMCwexe8)m{Wx&$(e<~IrS77|Q^OKsJprq~dbZSH|#@)nIYowxkk z0}gR*JD%IFZQ!K{n09*EX1SBiF!7C4n1(l76I(Hx5<1^?=p6VseI-7+%D*)=YC0^7 zky5VbIk@U%Ls`7jJoa6vK=5HwB*7L%Tkd(~$UjEciQcz6qnVD2!3cAl92T%I=b2~! zEyKMqI+M;r(!_yN{@C$Xu=5*dzk~`5+pwUtMwG(x!Ce_YIj(~uBcVZRS)cPS-QkoL z8}a1T7D|qr{Wpp&O4a6;$&-U0(ko(x+MA*U^_#k#2TsM!??ax`i$r3XB@gnrE(-6CmD;O{l1z>Wz{!@(*s&Q5WN>}M5^!MBDD%1xtj;t||+b=awRoL6gQ>1ZSW9#=lkwg<5hDgh}BQyF)Fi*I$wmg1jEDREv z6(QB&ei|_$GbMzV=+TbS^=>8Zb2Uoz;67-Leb#8WcH4lTBP;2~0GHW`C z>%yOTAyCkea0oON6)ht@^#yX!0pDOU=Z`_@yxb2?yd4i%DJmw8o|{2z`-Y!ajb9z` zY0KJnehzx3A#{nIq-vtVb3jj5a4(_J04LW^|D8s@x(k+0esH|Wp6Y;!p(4MVok?C@ zmzqA$2~NL38sB@`+oMs^#bf6EWTA>%2u;^iF$CiKM(E-Sh5h1cvDk%K-;d0ZGSy?2 zlH!C4RhP2xB>81w-wSpIEv`43z6cF^ROxV$f-bVE|c*+2Z>*`NH}o0iq)wgqeGfX#5s$m!y@7{rOeG zwP)#) zAqbTi3*7lvl@~7}bpGb9R8qT{p=>oEG-%m?y5D_h)~AOEI%sO$Y=lo&x^@L*ckI+r*5%L|FEMXKV?Lp@aHpMj)bD>~Ez>D| zx)9X0V*BLzB_hx7iA|_uY^#FWO~Fjwj%jZd)CR&?VhqthT4D|?0r*xe5~h>W}>{~kK5;;+2HC&`SDbI>7f6@ zp-@NIry}?CD;^dmm-#@-%})BA;T$u9_2ea*DB8Kar%j!l&z6F8ka9@a7d(88a#Bnc z4AiN5?aXJM+c|SM6w&iieNn53qG(fjhHZjaErG%`WFtm(`Dyk0%|nY!A!K(6$Nk{1 z{ia!y4WGj_9S$g?YP~925?9Y;(}_Fi#k-YP_aR~mN;sE1OT*Z+rU@nECGRk2l(+6` zXyepmkX4e)`w|)1KNSfK$#m<(?}vKmZX;()C2Y@gRq42uF9u{KY$?evbf$JCr?ePM zVUqTBf47$#nwM+$7>3N04QVp;Q#539sQr%KnUD8s=+9wq@Igm`KuAbH2gw?SdHZcw zuOb%AGZvDG#x482G9#U}Szb9#099E^zbgW>#ujXX>s5YJGT6oqF5mR=Q=Dcbyv1Fa z{_U~`!p~0#_@HwBnp^3Avj9J0ept!5k6n*2I#zsq<>PVy)eo?+1roUx489v-z6r+U zLA=BLTcJ^ZOFP%^%U?vW71}LWHqFZvhTZJA*Iob!HlEstt+{;3OBl?{`5E!bliP73p7B>$K} z7M?9y1nQ|Cf?s5^!e<2nA!I}_FTogq2T|laB~p#XT+C6G;#3(rv&W98X{7FVSt>_l zP_Iy4VMQECj_kv}l!l`CLxvKdOu_Fg_1))$6}wA}3@6Kfy&}WK6j;aGBmk=9$t!~wUhNP#qO|CYt~5n6(*7Ex?!#z zzD5=yS%Wn<>c^iwc52&#TAwq4;nRTSI!TQ$dNi_Xu5^1$F={MIS8`ca_?6-rZzLY3 z^h<7>z+aa7>)j8xPUvvu0e=s-*S#W$Pb(OAl9pPVb}UXU$HwpFi52MH9fAC`%#SvVnV6 z56iiYGnPt?jeh&a9eH0lTkxA@!2EkNb$mH%i-?L6IQ$W%%$eX%biB;}1J+D7RYqee zlCE!iz>D%n7&KH9E)r~xFB>Z{gHz3J;L&ejStsvsVw5 z*Bn2I%*72^GiNr~eem)B{?R;72v(o*8^1Hs(p7I!>O%wF|J`>#pZDayr13cg3ZpH#HfbwdABSvw55Xpm6?!b?0ut#F z&Iz8tstmTP>2P{HpgsV*`oNzheBtF@L73i=Q>jR7DU*5h~T6$UjiaCfL zUIGS5FXny4`Va3VR=Qf=sz|DlurTQ+fiX(f0Ob&&Lqm7hF)XnMqMjMmP+=(1gq=9l zs+Rs^^o~|!X4;i~@DbD3PvD1<-x?lv5+fcI2alkX($}B4thPi^~S`k+@0&$@H6;4 zUn}UXF^HO2puE+K6Ku1GvIwV932BXR541?XoOq2opPm4fFy@Cqk2>kE`(aZagla7hLs3FEiZN-ge z@|$0>lfGsUW|<`G#Dw*3Q{ZGC)?>X&b8Ycxat9oe`=|volQU(HTZ1v5{;IN^@qJ}5 z`&P~Ko=SxhDf^G}XV)*E)zl{GE@r*0jhcD0|)YC|{3eu?79qSHiip-${HLbvDI|xggO8A6%qfNl$PTIVw|(w z*Z1iNV^(C-2^iZ=hfF`|ELw!WERi_wP?FrrBaY;r^**l;hWe|BU~u0aMOoj(T~E;& zj|81pWlocAX<+~j%8*6Tkm(@dEYMF} zB;~~ELHW2wO@f_Dv{BEp=}@>s99o`$#QC^#aQK7W9O6Vh>$v%+2;a0;xPjun3O0gU z&;Ed|v%zbD0v(pu{Z)tQ8>z}~)%}iM8SLb$+_e};^I>%d;j~vL3hLBN)L2oU*~vbB zD{fRjdb~ft#=E?UtF%aCF}bM-j;J_{?KXn3*aU3$+rimb7h&C{F)c@Vx=dr-Z}H6Z znUfi`I!ig@nFn+{-$(t*o;g0H19^YJTt@5ni5(weZr==UAcaTNmB;ca9*S!bOflsz zcQZnig-KlD;=8WbR$lc$f~qTPSN66vM5OQipKIr;f0tw9oEUmn>EhD-Pex-YIZ_?q zIx5BPK&wt%W>#2n zqj@nJGO_DzR*=!d|2y;DeJ6B_SX^YuLQ*D@pk_{t@~Pzf3(wvczrEmBe! zZ=<*P1y2ZBU^L^HmExcb$npqSc+a6Z{?_D*jm^6pZlJbtn5num_VKgFCEG2(Tad5y zs<(c~?4}_%Q0!3A*Hx4fBTiCZ@1oki@gY{0GETJ3CeTDp7Z50|c++9`n~C)ct3Uc%{vV^o02?lGIPr5jIn$ zIr=v0*;H9pZTS&!P2<%`;rHH@N>j)SgyD+Q;7w@m7dlfjyD^;Z9DSYO5z|AQ0#x}S z=%5Go!5$6+&);DPYImhAE1gy_h2mdWQUvDVJ_Th!fE$cr6ud}CDWU&99-wp}v4)WX zjo>M}YQVoB!AuBY)&`LwVvNStC<{6JP`<4JL3{19c<5!|NGOVCKdJk&qLPpG^tmWn zg~?5U)9E>qD4Ks$QG+SRG%4nZ5Q-*a>DS8Duf(w){a!gB@D>_MU-vB#?du~vkvA++ zn)OJm7bFnFzbYir8n~@l)1t`Z*i-e9txCqvAG5Wse>2Fznyv?DRXFpzS~~lo#bj{z zxp5p#_K)B%M-uV2q!(SR>(B0P?ml53H9$iO&uwUb->7B3`y4@Bv01uzPJc6AY#-BP zYoab;d7+-zc8GUPj>|ce+L!m zUIg6#U=iP6ScK`i&iH}fI;pzM>|8H`skbnE_m@yhrnxzKh1oNn)mUvx_{c%A+S}4T z`3{dnjw6lP_rGKF3BKfu+(D?DaFzHa z2sPs?aB`BTS#)I1dpMNYn;}b@O}uY9{vOoKX*+lcQ*<8}^0dFvS9k36Fxu3qh&7fb zX1fjD@JwX4^Ftr8KqKGFU1cK~Da$I_&uZCq*!ijWxZXZ+c-}shAKW%HWn^@ow+gu9 zW2GdiElAdvylWw_F%**XtG38_FAu^7!)DU0{ ztpPa&3R($PHI$^BY~!dpQly$byvQL6d)Y``66A&y&O<>8;K*XPO}zZ+P_P22q$$)H zBR~IU@KCP^s6x7&%8vr#i3*GR7Ecn5`l2Mv&9f&cb3%IDb*A95uu#Up*K)E_mByFQjMN!R4%n`(txOOWUOs{flJAvTMX0tSN{^klHj`c@Ic^a6_rfY~c<%lFn3`Tj zl)%Z2XR>m}d3i2+(x)2cXnWtR(c6{J(^ZRwu3ezdDBI`*9{+1l4^!>jm!g z2lol%ec2**72Vk88cZ|&}B4Rx{I)I{kqBz$njH|;GlcZ+j!O*J~p z7cX%$b60yztn?pOajeS-vr8#avCxJ}VBL^%Gd=SOed30YwS~gH3=HskN{ug-0J-#% zuT~4ad5w3DcjRG(CGdDoHDh1>Yhi99%rr??OZ5g>+%4aj&*bhpyx}H8WM~|=kZDc= zhrd0f>@LcGSFG>Cz6;Pgh%{c{mp_0MEW6!Uuc6F)S100459KfPxr~#Q%j4;t;lP@T zb;!bZ@rD_Qm}0C`xP%<&vhKF=^oW13@B5y&Fd8z^(v-I5ECOR!`KVhBi-fH%s1^KPr@pkgX+g2$m(i95EWw?YHsUYYckK*V7Ds zcXjKL6OnXC{G;{y+7&T?VL>V77MJEz{ELT^uSh}7Jn#~*&pyxTTdcRN(S?w41Q!NH zOt{L5JLGywfB*XL#P7wv``nKwTp>dTdlP7|sH%ju6#e|7oK}sRBkj@`OIHJ7hW6Rs z`u{uNzE;u(y!GLG%E_-LoKyYM&dg9zJs1JX;`fjaMXcR4$>!cj#N67)w(vHcw4?bL zaxeBmEoQ#KDU=sC@a2n|Pla0gIh-6J{;SCvXy4+2U^kMim(U7r-- z(?fz%A6J{1_a^^{EfkCjNj%*ZO@_ZT6uKrUyji~{9jo8;{q^PKV;qe8)ec|a|Fq(I zzQ0;AXV?I?qz^239~-dre@`0v9qg=q7mi?iNSl7!LOodn#^JA1Z`BpIbA7uCWG4SN zJgx1-0JTEW2)5rcfc^K!6h2}H}aR$)tNM+ zE`HvpWcmdf6l0<^=sxllTT%N>@0G><`AIRyMpBEOI}?~!zJ^2|$79zz&KJnCOVU+3 znl5+iK8H>+3z#APf1PydjKt!THjBrI!VJr~i8HnV)-=m%8`LY+6F+8(7%g#2&$-PR z`iRXrfTO2aJaPPk0yq}6n|zNu2;+%Mp#x{v`8|oLdT87x^w|s}akDIK>HVUm*VSUa zH^-l#D_uKDxM3RGYa<_i)}*6i+_9{oqWn80h%`lD@gZUtEGZ<2!s*BE?%DqcZiU|> z0^!chhLgd8afcF+ef%`5VgJ73cp!10+#al8k1f_e7sa|epB#2c>xu0$VpvcBQcaD8 zo~_nz4pbt=SHHD)6lib}II#agr@)|mEYxGzDBc=u1^HlQaY)x;uX=ndGpk0`EkLwZr|-X`x8(X9oOJCc#=Z0M|8k06nH_Y0_Y z#jC=!n>fkihyDJPN$uM>$)j1%Eb5MoX_JyLrYe&RX)BF|_ACq3GRu+sfA)Xo)!9EP z%?7_mQ19DM+-;h@>e<2t_NP1(le9+16>i`3mI|Cr{aIqSlNi#7ujFG&vFFYSR^(E4 zM+SXL^d%C2*{r|ER_MBq38g^1Rd!rFT8ky4WIbA!oIfWjdOML;%&J=d{smCj4nSk? z8=oDQf9Uv&(3tq5zPY>lgC}1Z(2oLKD;bF&lZ|5-ye1Ky)t%ng$eB zW|!5zd4rz<3WBKoM;nHlaU#%nCkJ`@>AvPEDSuMHk-k=Vx-gVBNF@9Vk-X+9PbCQ_ zlBZ;2nS@E(k0**21G2l#R2RzBxuXlI3KeK;kS}g=|5)6s$pRNCm__&T+;x$;^ysw> zJ4QABv)oJSTyu?Bfw2Y?qu_ui-7s(_Lz7Vy?LzUg|FL#uc`k0&%Bm3KHbt#P)};%U z;`|F+v7a3kdkK@c$vS2&R48TO5fFP8F0r*97MohaXNO7kE-*D74%{*(t6m9sn=k|2 zC(8i_5-IKtW&CsnaR%wVtq;%ct)?~Q`qF+l+;`{YPL<$#4;WHBK9$(*pOeAg-}KM& z?@sYj_qN6<%@}64CRO^L!!4lra@v!e+Xrj+Qm^kIJmF1>XyH;wi0jhT!qqEIf|8)P zNdplQSOmR%^u3Y)5lvs5YTiTcW()&I^_HGLV`Gl+5-!RTng5vK3+OBf@eJ#l?EoM~ zg!6J$k;9(vw=?Jd7TVHFHn~LK&w_!2Z$kS=Jn&V*Ml#VIT&V{<9QXkJs5Fe~VQ&=^ z{>}t%i3)NKK3l?k#a3M8DIvI$JmVzHpPED1oXEPxLY0Doev{T*FI&}pU$vg&GS|!5WrdyZLgy-av^G(MAOv%XRrl4=T5$!hPI$d+cW>}w zg`5=KZ1+^QRNXJm5{2v) zmTD&{Q-WO(aJjLRHZXrvRk31#0N0W4t-NUzqbH9p=a1Z1jVL+1I=>gE?p6keTqf}8 z84u>x+5SH#4agakQgv~rvM`k=B;rlb@$(^#CuWW=iq=VD&E6OpTI|u;F90u5s-{8- z4+`Rj`D!r;0TgLc))TpoH7!0;QTktzbe$+6WTLYMF$a|s<(~(Ap$_{D62gW%-XQN_ zgQ;rt(lwHs!3Z8=-W+(nnoPSr+r}HC;_$aFTcGYw^su$q*g|Xc1%k;K$8a(@)#ghi-TUA zAlY)a!oS=xiyi=>XvmT1%T*u&a*JVReJCi9>9euz|Bu8arVZW`6qZKN-^`(oeziT5 zzH{NONt@&NPq49T+MCx^miKfY{!(Yvl34C*jag9Ss0G&@g8m;%4qQ)UASX9IUX|Sk z=>y~rM*W;dIoXs5HJcH2#LzAY2Crr9AqlLXfsjabVk9?yaUC%l^nC&~0V8!V4_<&i zJY6yC)#^5PjtLCbUVoNd%_+@th}C{fnua8gcvwjrRKQ(*Q0CG$W6RlddiQj9^U&qpdb>UMa0)L?aY~lC1PZ8@Riq`A@ zI+FmAK~!NNK_Toc4oKiI=$;NkHh-1+DFcc}K`o$_AIw(SWm>j)4H5|@f)FdMYu;;} zV)vYv>{DcFcZcOD{`m9d`oD<|H{E$5cN1pb8Y8yDug0nJ|B(q6#lNWbGmRz-b^um& zN$D>)XD%?l!(A=J^$yEOnoF?pFI?cglc=hGQWcC-E_HiZr?BdHG(Iomg8 z6D>xQ`))C6vRwCRjsyOF)h_dp+I*|nIxZuG?5mqU`uS^-3jhUhKY=Xfc1WLtzjBq3 zY{!(pKgeF1l%S|1^k|M6AU8l?oL`<1DMw#i{w*j{cA%uL91M`q7k>l)H$Yb$eP32w zeCKc3-5+ufMqiLj^^CbNJ+uig1UrRV!QqSz;QXiQ{!=BMjwvI6^aX8(kuMHpA0GXr zqstdC0Tu{2Z)BB#zbYX_=)d-)<$~M(f3g4YhsPU^8%&nfIl3pp_ht-s%- z5d8nq^%hW7Ze7%{AaUqJg9sc-LJ$xU1f--vx+JBgMY>a3Lb{}-yGxW-5Ckb{L8U>u z@!t>Nz4!gT?;nG~z2@*)&)##*HP>A0Bss|Q@HuPV{(r?VpAvlFviWH79ZrAY1Vc-U zI58%QJXI->jE#Dhq=3f&EmLZbtSDI)sQWz54v;Lo+xb;tD=^)9neJ_#shS_<8dKJN zLB3K3cxe8s=#7cBjjs-&D=zP_I4D5&l5CYY13}b8%_It4x~0K<&ww^26jO#SYq$Cy z@Zn_mGyHVO$VtTv>s=DId$7*VQgo{EygU{Yhzf%HC%!|U{us4tobzk$?FieE6UV7z zXeQpu&8bfP#1edC>~qfa%iP##iP-2j5=rFdKuq*s8R^dnbiDiQX%HG@v4~C3POrTC z)zO)9uR9z7BPSM9D11MopLcxI@v(Q<-i=Y{r{S1@2n5h#KSzdc zzM0cu7mG>-HKfkyWNXR(TQ z;^nYa+(B-BSAW7h^Sg@8vtxJI>K#c>=ws00rvRTuB2c-=4H#{gl(ThVYh=iAkr8(2 z@wF3u7^DlL5;7BgNJR3jhtANQXfYuO#QyVT0hgNIqg&(m6NdRAVFP=CFnDx&j-{o$ z7dn~n+|MNKr7oUgacUe3v!U$}Itv#iWp$Aj+w><5Us6WwEcEP;PpI8rxC@zoa!bBv zl9qdh!|0H)8qxc+6f5u}J>rrdE`TK`fFFixG00WTSB996D~Vlu=Tp#+VhYMpG0@aj_8d8dA?+9jTz-Y$>Jq0Jg*c?zi5%e9HfMXklSGU#3VO zMEqYb0Hz4GNN@$6RH3jK9g8Xsq&FD=WRN1uMozy#{bQ6F{qi(W3pSWY-dG&YVmQFA zJ6(GX>VobtCoQkhY&G3u;AowCe#@gg@O=NLLi~PR>Tqbb|mL9_Y)*FtcNF+>h|)THNB)&;{fO^7DJ~!6BBlB1khR-khm*)r<|H84bS0`R8MKZ-5t~ESRxb=WA zfg?I(WUv-EeBT){@%);c(GzEvn>9A~?*5pz6q-F@H-_47{FejR0kIU_B~{#!{#Jo} z+pjg5wa*SOUMkWbe!rd>?9Hk88Ste)hGx|{e1Q@z@X)1@+@l){3`c3*kHEt-Cw=R= zl?fdSf+K4O9Z68yFH%?MPCM%fq2>YTdWczA!w$F;aiAi-Dh)l$9kx6bQ1#CKsoo?V zaI5>62{dXoOo`_{!YqK6a!~#EdVv2&rn^QX^>k^F1G*3a5W_MEM)tUnXb$nmuei&%?!v_VsPC$CM(Hk37xIiN{eTnS9@YExIs#>OPSUvkA-L32 zTb%BTbJdf8S@!X&BRNO$bvH!yt`=XMo&{Oc& z7V16QX}#XNK*W+CNS%#o3X)%Mb&;m#t5qQUe?}yN*oubedjR`0)41?b87X)m3b)A3 z3a+})GAraV;+rsWTqPy2agk`tn{S0c-9Qq5ucF#;7ZTP?uf(tOpkB5 z0=2gGQdF&MSn*SOff4+!8Zf(dO<%>1>jM2*d??^w_PmXhqbdcfkf6sQ{JQT3!_07` zu{@|Z0+VoQ!0S9$Fwkvdpo~UJhmV$$EmDm(z|l%$WHd{-qZT?o2k_8X{;4;%-iBTX_ZmC zN5F7Gq4pR6fx!C((D9yIV+bJ>#g{)sHU;X#`_PO6HIEkh-&!<2iaDX75e-0pZjF&Q z+vWm(WA^PH4`eT*6*afyUJ{_H> zTZIGWw1%tWKysnb`onUExhMmdN3PEL`QUVWfq9G8`GbIX(Z|5#@|3=*W$&0WiIh0aIt17Fajld>Np8Q;Il&y^-fnIbu@HJaNr ztN&=gV=pFm{mPQmC2qN}L3`sxWv zrRM__#XP5qolbwho-^Nv^M~d~CP}>lzR)k*rbYhT0kkuQv|3t{*f`YSoW2TdYB@Td z=RTTl5vK*`gE^_e1t0YiSgjm%A3>Tab+wx2#)q^3TCdbP|A;4E*zv zA4LX2`3HfIG6g0_-~c+7b|}z>9&VOpM!uUh`1tdRgX#2*m(myOHW>l`^U{y=&-LJf zRANA8|5H$uVogSrjTs{#sqZsO2po7MQ~e7ES>EXpxRf0GQi>PmFTu%|WVUfE1OkDl zl=I$ku~BzY`OfgVK0FJ@K1FZS(t&Hc$cu}VJ?km>Tf`nO-oXWVFC=YSYn6D>)hCQ-%_vb#5+Uqb=aC+#(?w@^r~AuFR?$tW>K-?|>uun3uLuJv`l z$K6|Qw?unhb5>@1|7(eDNp;OiC5}dd#~SHj#vVy$X^;yLkIn4um%8I4WkGsJQT7;9Y$pbWCB#8Ov*8@ z)mWQ#%)kL4581dcKkL~1W(+}O2h!}zG+sI;yOilOImL4n%JgfK0G-ee-SOfFXWxwr z+xAaOSYfRU)742^-udc`l8z<0lhZvAt1dTCye;1jbaZ%QI|7d+{5S0z5UKy{_w zmY6Ms4}ZT5#oojHIPhWo;jJpDv?5NVO};47)Yedb5@v}7w~z0nwdcBM%?n9vny#Cgoy5@xB(aScfV2-VcxNCC8N2A~>DC@Y*e zlhOe%Rjs;XbxRf$EmCS$4#B11UQi$C$t)46AKAUz8#>63tC?RGPkle>y~B+o-Ouf; zvi%8=+nK>7tHoX2?p4U@_&D5QG;6gfv3LL;+VfgP4_6fbr&N)~;U3ojwZdX>jb{-qK}H`#roN zIe!sKL?E&>j0xriclkA`Wf%+vE=Q;cu&-r2U;P$ekdJ6wm{#j> zBDCX&D!6IEE?tLtbv!`@TnIr-2UBc! zNM!vIOhA7S9Q!+R9G3@5QS9l;kgkL8oS#iA<&@vKn~WKl4^!s`OH99s*S%|beShQ5 zi@nk(En4?h_cK))Z%;#>o2Q_b?C7olCq;1F>GoI z_u6-?dW#$XJQs*1p}nDC1QVYp*Vx7UPf*^|AwBy2!Rfj|(3bNT=-SM4mNQ+XUc zWCl+kAd%pdEruUTs+t20Un#Vzuo75ZhJfW|UuUP?%sG1&&z3u6e+#uam3cf%QQe>wl-*Q6{OmJsDd_ld;%~1L!AbIXU3PnOx^yx_L zb%$dWEc0$buZp1J)dcEq5nbs+(zU#w7!m=o6hzZ^MvjZr#n4sZi;0PPR&-~|3j+h> z4pOe@WCsk=Vh>>(ZEHAXu#-NRQ+4Kl$y|><6HthN*Hn(*T(y3Y3BR578Atf7BQ)CJdT5^>x5&w&p z9mzg4C_rBvdAwi?a>r_Uw*6%kwxB4S8irCqF%uq!0mHiDk(UiHEX(M&&gVNhbblO| z7GoX`P(uUHj}!zwf71_o;-YcdL3Q1!Q<-fL65T#Ai=mqKmvMN~vr1x%8^B}(Uc9Te zP=8Rp*T?Q|ogo2s)nG!G7Jm=|=AC*QJ5HsYFjsS}$tRzQi4US?nnTsir^j%G`jZA} zsi6ej9<#RuK!QiE33TjZde&)XuB;QPwT(3yiGd19n1XgnX)zdHQ6|eNXoj`2SBd#Nvg?oUcnyZ-K&X4W$iX#1qwn%guX*3j*La_(dWK zTxMMa3m9Ma9+(h4@G2j56yc%L!%H2BJLk>fj%2nHejo2M$!}`^AzGJWC`Ducx)M2v zeUIY8?QDBuZTgArPM_{iEb@G>dq4&V33Q{-Z`4p9D!kFS$S7r z=^&zO+12k|%HDJyA8mAKg8_TwPQt)!T3uSHhigNLXW8fj$ZX!w!g^J-RNJPigvC#B zr8eCm>xu!Fc`N+3+ofmxB4cJU!0*!pKS61GPc8}%78d0XpSv_V3JXA_E0Q;h6goI? zQslL?iC!+Auh;+RGBS{n4;3Iy|Cuc9nE45uU}XS#wqz7D1OjN0%TeUk)hpC$CU^*} zQT@IfTEzL38g<$LX5ATHcPkCVCre<*;t};rk8HHX zEt&CpUpX9n2WPpYsIY8JSx;ox`Lqt;c!){TRY6I^{-XPFp0awya#VqAz5$t}ABaaZ zj4<`m!~ng2-z7Ll0OI9Nk;naWdE(&1ZxoN#3{C@J9SXfN%RDAw!%U0hd5t zi<=0KJK9YLZZAnJXGJCRhU^r|l#xvIffbqTMs7?a=Q$Oz3yMB>DwVZe!Jla^bHGw7 zRjo#7A@mXjDm9X%*t{5@i!y=QyXO!)yM&tm_uO&rt!Js?N5Y5a zEVk%qW;aj5=blNFnWXXayJddwuHT*4_xl7iQr_{0wrS=~D^b&I}++yS3 zW;+fi8ADl<%o8*e-UG^wr!i79(J3wNm-kHrWC6*67Qt3R-k3W?gAEj@PDFOpn=Gt- zscP$faVr!7v@EpZ#@w?GI)9-Q9HDT_xSDZY9!-kEU!k)HI-%Io6Fms zZP|BY<6kO@G0SI(ID1Q`?1Zsedag}vIP6Eoa|>#>t|D;KIVMMAG|oN9C`YO|6Y_wK z-@4n&+B}R>ydK^)0~3uN4{3q zqNUUCA1#B(!HX@YnTsu|D|l_Ef2xR+a=oNn=hH( zxw~iPRkH@r%GePA7pEZGO#MLRC>L5yMqdA_(n8haBk_BO8=F=ST{qhF91L4cb>2`h z-O0Pd<85S)l?fwl8>axHq$luf|9}4J2t{#(92gWt@l(RMIMv`_3yZ-+pORfEJRnto z&mI6w`6jv3$GSe$G>j2JC(fw1Yp=gLls+wH;pBurMdG#~X4Eahp&Ok3gIFcHqq&C$ z^1iBS+75^J?f`Ww!5h#(r;;ri*>=>uc%whywi- z&7B7_R^D`kZ31Tf&$FJjYN&siw66m@8;bSVv|Pr1-ODe~|si_3T5?AEtj-AwKXAdnOP=$kDOkZ|prq=(8EYf~dIqQsw5rqipYUP0mpoG8{vjmApk;EUyr^ zRNzAGq2cqw6mN@K+Sq)SVEMVs7!af%^Vv5941CpLJVmaOw%ue zsrt!LzVldlhok6qAh%v=X_I&VDu@DS-PNxg@*bZ+I0WnszY5VMwm^qO%EKt^%|B$R zBS2VH41vuPfZ!x|SVFrZ4~LJyqkHpa6JK8b7L;-1=dG>|NIb=lTpn6g>Q3}WvSNlL?cHp?FP0T4rPiVMD;)e@=l!d;o z2Pt_Me!1iff!4(r#FncCybJHcP0OCh7|UR@#%vWGt38Iab2FHS=i;op3=_4r>1T<*jk)XMtb{y zecxc z9%p$md;x_xj!1e_t&hByVW!dNR*KQrR1fm_i4)>gx`EZU=Ik`kceuzAHz8r{)E%{j z(=4TAJ%I-|i>hSh#RzJ)3t^Nivn~8X?~zL+&MxlX0`$GFk1J19d9ZL|?>G+1ql@wi zf*b@git!uB<&J`kMS7v(7@$A}E+%fg*8cI1-Sq+uigyQY80~=@jTbdH%{)DkQryxn zBWKaUSW1<+k3v$_dsG&!&Z)v#O$UivqfZk8@=j8Jbx$pt%+;lBOfvzHl>p7v77w*i z8%Hmd26w!&GQgvvD?@_{ErFihoL$kFrb7E}1v)S~;STYYjnqnP@a=gauw!S5nGGv5 zSUEL_#YhzY7MUI2xrdl`qp<_Ujr$+mNTcv|ZwzT`>@93R@1H>C>l8^Hx@z}n=yHS# zAtfbjqY*c!M*cYz#=B}LBBD{+ZXp|pfS&Vp(I_!oS%(Y36U1U(LiiW7(52IwC_@O! zKyYkdFcJ1_=MC)ym>hUQz8q&5v3t)>A97>7TWGCeI~;lJpYJZqI&n*$gaPnOJ8U30 zQPobfbQKbyCv}4y5t*Wvs>%p|>lPyByveyx@?mq@5W5vY^ni$FL^>%^s2NIpkbpzyPo6_-hrVkb>p6QE=4Jr{5l``K#I8?i*vD9GV&0kcs zz(vgE#Z#+&(3-D&O<7Ct`#3V8f1gfG_t3Ai*4?}hloa4Y&z@(*Mz}n^1Lm}AYg*3h zb#6F|qK`E`jlxF03(V+uT^^)|bNcCB`loz^i(vsuC~em$MBxi&)6Fbv1<28wsK{wZ z|H$WjSNO84Qi>A(0IV!@)~43 zin4cI&4B*jxPF@cM0@#o%qz;mK-k9fc(dlZPVWY=WP%(bXg#HCY?+euMq+34ro`jb zkryR+Xde*}${_p`c0Q8PEdk>kcvv872Ks=S8*(Uf3i2N@Z}*A zN0dLm*!vt{tQ?@n`n?t-B+(Gp{`w_oe(@a%LugF_xALer+H5xo)5WW68O zR7K%&MDSc(F??{v4#tHyucCl(K*6q%{C>(Z)`mbf@Dhd*G-e_P{S%@#h074V6e|4< zhw!OQ!slT6=A_z>UJ7)K2Jd5)*{{VT?pD30F0)dfKK8>43A+&QVZoT=ZD?i(cAbGo{Tfc`bIP`JF;E`jL_{+hsruYt^NT_y&Lg z5YpLgWi8mc93B?~+VZM{oc4;YB>;)RHNy~h=p$Y`rX{7*%`G-8qubB}L&*Qs{8gqV zRwG$pGVd+0yV}=X>~xtDZz)ihNIvubRr1nddjYFp?rDKhixp3__yKkvFab481{s`U z^fbrvJ zfJC>ygbE$KL-DzOu(n9(t@RI!QZR*>%qh^H#$z(98#)(24bUe^9G ztU$-3=S?vLmE6yQqFRI#1k$C?)XT>1ASvvz%&WIZ7bbCh~F`P@n>w?@?9m@MBPm;%@;qY`{Q-v2xRVOrfBWr>Q%sen+_wJ85wX@|bdyB!Gs-{5<&lL6BRrdNA4}sVAxHT!IVFeOOc7yOs zz$jnr%?l{v15lJ|=lA9cihLN6mEgYRxOcw?(I9i4?+@D%q5VZ6Ky=Ch34rF3Io@1G z%mt&F!;^4&f1gFqer5VrkRSz5)T9@fF~n&+;nzN~53J3R^d3uV5+w7xuvvKA!~=18 zxvr7LOJLp7$rdbjm%hs}#y;vSWiU>x=@D;#Q|y+zUh z6q08^?iOXMwTcoq!?qB?Suq}h2DHF%!qiVm2XiS~-I2&is=$vl5cXt1*h9{^#C(N4 z;{>oOSil2N7I3&+&VxpEU+DX~4 zOh~JPhr*Kx->IdafSU18$BrO$DG`k72X{+pU^I;vo#Xx*@dme;Kvf-Jj|tNeOXER1 z_wFvK&=$FS`6sZN>xnaVCdpovz_4GI9Rn1Tr@le`c9g_RmnHu-Sk9F!a{l#@UkyP( z#PJBlP{5p6|KS?!7zez0L^KBK@*s#DXp41s7FHl`;6!(K6SI@L&v5U5W*Z{ zGs>c1?W0(+%wRBXfNtAI9sLAhx(daF3iW|xXHUQYpIjR#R)hNv2nz>14MLp{P5o;m zy}ZkO!8Q}R_7$zS3;cOY*ex}oqt#Am>K*Jsw_B*#^t0$ zGnq09e~I4p7t=nXF(76XBKR1!bcdQN`nAX!iP2#K2!lR<{nPW{K4dTL+nJFYJDDRl zN9@vmIYPMFe#k1~2Po1rZSSv7FwCv;l|hT7IN`aLU@|Qb?`L8o+%y zrn({!T?$5$QNwAXh9o2o=>MXta7O55EZUYXxy^GrMJ+v%hK0BM$-3@Vccu?haBb8c zcnLz)FW7~b`*>C2+dbz~MTNcW=l?YMmCym`IkGyA623L;YpK;umkN5OZidA^ z0OnO<8A|Cti9(+O&1*tW3668_-^exAdpP{lV$bq`>H=IFno?HmsljFg%2)jd*BVP@ zi}gCBSS1>Z&rWgQWK`U3(U&&xM!ztIIk^=DLsJ-!Hua{{lSc?A zStjk<^ARsljP*WIq;>Qj^}M9Oy|hquycCWVLxevd^a2Xn!e)I3@);y3{{4!gy~NQR z#q5hpiazxHIVhQ&da=6&+25J*a(|5W_ErauOG=w^g+_r|XF|6v1y6!b$}}}uP>|OM zP+9_}3T_2`ZiE7r7KfCYrAP>+>Jhc^LlFjMx1Q4%)oD0GYR* z!bHa95o;XhxV`d-zPw>wAus~7_uTKQ193t6C6Mnzn~GS%qXQH!e&P-?7~WTG-I|@? z>zv6hasB;VLEgK`0xxZA_n_8M$6M#lEB+6Y5e#o`ihb|7=wQQv-icdFDN89W10;B0 z{&*nk_#!XP8TvtZo$(i-IuL~(#?glzwew?jtQFU<2vValj1tlj3R*(Asrf(&ScTq# zGV$z<{^Hp0S56NuT;abk4e3d5kK`85zklJZIo0k0SWDZQJ=p`dT~M1wUQ;8Tt*uL( zNvcab=d#2CQ(8+}XAEG*^HuAz)SjhT{Y2X}lnc`Ts?y~_bi8a}3)&w>!(>c>8g*TOGdPy7zxGmP#Jw8{T1#OR?mQpHlkg}x@7DQ> zIX5JR#A5OxJq!VxnO=8tdW)GNx0Z~g4tfe=yS>@|ijiP4PB3(qeDXcCjmEZkgHd%L zt-Mp9xPs#pbDsZI!(;kmbS{}JCtZM~iFSq-#J~a-@L?jP1hsRwJzi9+Y3EwWH?OGi z*~c&L7TE9b#r-+1E|L7a^3gVia=x^k`}4)Ux%2&J^RKxKw@VOR!VgY{1>kqtAOd`9ps`q>I|iW&lM$tTn}H3KD-w?H1y*k*#kS= zl4-m5wSLr_6w(jAX%0Vr!yyCO%nM{N+JaBr8%LwDY1c$F+Q69bi_98rYpsjZBIS?} zmLye>0g;hv+0(BzuNz<#y0yOq>*^Epk2+Vh+fS7ShF2PMSCivbJckL~Mq(0*IAKbW zF?rIYp^2M}4t*d*IyOI!H9l?J=o9$jm=30$y|-#CKW^|RmY(1fKLo-zs`!8-$ZX^^ zETF@H4`~Ox1YlcICk53dc(~AmOM|(np=NxV#sc3O4|qLa&;&Sr778=VKQ$r15=B{v ztU{h&pNWh78iDc&%psiBFGuLQALBh-+Vy42X>0`yaF6m&iS;*VUofZ$Wk+6#{->LN zW84WDMfc*`K*YY$&=@RMWbP~5{~0WvryAANNf!J17Ey&^sigpL%y`{Fa})&aTY-2H zAXo~#R1A1FDTIeVWVa#9XB3@e=#2M%fAHyn`=BxNbbApxIaQ|m3g(7v~EenzdIAyn}Z|2m&?8zNmBi3 zbl_nLwx`T0x?3!XILze(@9ud%Q@-W{ z(6qz%e*gv+gCi~D7}hr0pHT1_8Xe+-jnqt^Mob*N=MK)&^B4aEm*?uovzyLecJytt zfaMz6nb|V=<8bwwR@lu8m}I0O3elj&T5UQ&wqYF{ zAx~?=YrW)(1}#*upDxcTh4{T^sFt? z7n)GH7^>I5-33r}$z7goL3E7I7p-YA46AX+sKCdG>r0+MF!a3K$wKAKZdNk$C{Jjw zCHrg$gDzibT5f~S0EVm%wIJb!6DK6wIpHm9FVxESL&9boNYvxx0Bz zLT3CxF_9*+6YefCmxA&dKD_0X+fGDE_!TRTny)l;w`8p!VB4!beB8o){9w$*{WUlS z4S)E=)TPb(_(_t|rn&t*-_qR=v;~t5^fD{f>l^EiusT(@$jw<6bD1%)RN`La99AWz z7~c#>n#?zLW=Sy5&sJ|q#$D_mH1t7|Ih6|HQ*I!ZyhfO17nj%xC99eO=5GrV35TwN z=xY{uh;Gyunt#~?xccymLQE8&v#T7c&wS|#d3TmKLl`FcDdkwV2Wzh9+wwbCNQt8k zE}blXSy@L1pF_1iN3z?o=}u41e_R?2@nlksg`D7K>~#+um4y{0xP3kw zvty5T=%U@KXgaUtbLi;$4JI9;nF{J~j%W00B@fTpWaZ64(>iv3x32OhSjq-=X;)e8 zt{6>xWn*;Q9iyoxA%{xRSN(vKCULYYK@SY$@nu>^Ym*X%5o~Os{8c+Zy#-boUX3nEM?v6?SKUWs2 z<1gc&m|o=GsgZVer`37=lwxG_5wKkuI)F@UPX_h|J#!b~w4T&f{`R6M@X2tcNPH~m z*t_HFErRYvCf~B%x!)$RKa?xfBl;^W1CN)G;Fm|x(RfY|=f;PvhF4GoZ}A5p$bS3} zdxFED+7r|>1T6P18hOzSih3dr=k{UCVJ1xt#~fhwL9zpwBi9A=ly0qlAx&-fbgZ90 zgrkY4Y)z7`>HZH3bP2>9p$9v2O5O~7;3t*Yk>pabG0iZ3*wF8if*!Y?#V8!v{{ z5Xh|74u>ZVF^Xab4FH+FjnXEPb*E0`@cJ2N;=%9-L+%Lh&z87^fW09Ki-J{&x{bIT0^j+?GiwZbSA3yRL8E=59>)4R88rHM) zWC-lbT_gk)Z8360bA()|9W3hWSRhj^o)UOmso3EmIK&|F+`t2D+&;JcWvW%TgP>$( z&N~L%Bi7-3Z@ul0UF{X?n7S71n=IK^(>8XJ8?1vU<>2g{?EqXmg<~{;S?GpQKb2tm z?;a10Xqf%?kzU+vuw~`fzUldKOB`PPI&SSFjL8BNbeQG^)f~Q_3F{()9zP&an8^9M z0j~<^+QGnztw-TEOaEyHUk2v)CI5g4>1$rfI;xE2t@+^*-o}E!VI$0(EJQsRzadal zHN>YxexcTofj7&)hIri$)a|eowY%i*r0uWy&-lQFdBi6d;wO@dwX5h@Hj>Iz;x34R zDNbTDF1gt^DekGd4b#B0EEese&zfcNY2maA{|69d=;pN8+@|w*7 z4V!!&Fx;KklF#R-V-(9DBFFGgB5VaU-?8;JJ?_JQkqMj6HK*5_Cgp`W-gBkVU;|74 z7v9zbq8#dbW5gQphzPG|Z+@bFD*V{`r-Cspc`S7@W%0Z&x~sKmV)VQL%Y889EZJ@h zz1{dCVsJl2`X@%HEx;}bBljJi7EZM6npGOd2{`JdY7~JHREnTFLz(-ZulGWiM0JZ@ z@EHd5^Yc>BD1AiQy-47!RUT>qeZ!$)yX(wcO0w}RB0(}b7omfLhBk#&Q;x>8G?4Iu zJ3q_F%{ku%hslt0p$0chv13E${c$E;NMb_d9sbssjt4Jkp(S|Mwq;pm-aU0|N0MW> z8O>A2jrn5PGl+p8@OQk5Mi~A4RWQ4FjmP`+R6sbh=X^ABkot|TXqEbA#k$>O*SC8o z5t&{D7^Q4^mSr36cB7UFl;Vj|=vzbg1hP(b0ds%sFIubxPOXdGld71RV0W{x5uOY> z`+d+N60jETR9jX5^5Z6)CXd1X-_+#MqRweS>= z6opfxV<-RD+kROF-Fj)JG#JJua6xFw@t&k z6!J4uccX$ymZy^p1*;@9nw_uKBMjCwH%c_NZc_%T1p#+5S7{qud$cMCOWYxShc|#0 za#+j$sR@_)w!MBIorL^W7NQd!L@5p3KO8_diD>k}PWM-5OU(TIoKy>w9HoKL2M$_# z_7;kaUrTD2JZz}iL2e_l&t+;7dgZodgl9%4bKn?K^BsILNtk`={;+6= zZ1-AiQ@ogXqNTG!yQX+99^d?meHxe=x#qR#UbIa8_5O>osTy(J_0#wfJ;A%4u68=e=S~Mj5753>>H4|t zh+@D@6sp@Rvm9Or(5icmq(!aL61i^g;KUu&*r0<-!MJmXJu`Ef`)k?$G@8`7Al_Yd z8&2eavBT+GpL!RWKH%8N(sxYc40?o6c zz{o_w)=ssT7jq=YBS)smjT7oC5eUtfy>;-6|0UH@m{)Dq*g;<}2=9D^P@u7NE`4kI zTQ`_l*qp7aHCAsn9(6=GSf$@8t>w6e#12&&q6+1}g5~#K?w1HdOQ6W%+^baY-1f>9 z4lrh7NEP1@MSmerJ7={5+s2@6^3bF7;)NSPUDKq$&-KEq)I6rLldPhZOd*^w zz*fpEAFg648~gh`^Y%o_HKISjPA{@AHwkjz2D`Z6X%1UhvXqwVQ_Z*!-9^|}Ms7(K?u|_1eF2{wG9GV2 z`dwRog-MMElfnPOIfJd&&uA2$kpLJN3A6*SU>*-9!lCnj98zJQuqz*kDA=L~(GVmN zWGf}eJUF;Zy|bJWcT{bnz!?{(p}2GWWVql7<;r;ReVpZCA2eqxgeF&?h;RhRy)UM6 zWPk(q<$8jsQ;1+A;e+ z+FV~yPZnsx^WfY0_;hP>SBcdgQ@5n>ujwgZOWIMhF^W@H`d|~Y;96dCHcsO0BnkOu znw$gjN(yGLhSaSopy^t9@-G5!dt_wAjGvV)e7@m?&gQg5?WfX^y{7W7m1 zMvXgt3RRQZ_za2$Ci^FQ#@{+%;&KO`PKz0-YuFHsR&;*N#g)$Nxo+l|D-!TaV?2Mo04nP0 zzovl)ANSDK%bua-o>5gvXG6QzV2*vGE9P7ab!lc^>*2@Rh~+x$_}1TU+t@F&MTJf0 zDLlb?>wGbC2s%{3_&5v=<$qWvh&77*B~GJK-RA`o@8#Frb;wNVV|5QHGni*w{qz)0 z^HdW5988lWo}^g;m!@~K7;A~)YWHc?Hm5XyXktSihgng6Bq($_LrEiT9udlK(h`_k z+F{~vVT!v|7s{@=%EY8<8LoGI=6kl~`}oD$1L{9X?`DQS#yinE`{9QZPrfwS)}R2R zEos#jC%!Tw!<#`hS-lZfvW``EV_zB95*@!BV9z9#)KM?#&%qR*5b#cC)4nB`$ai1C zLNayRbBD=uTECvWO4s?^QQ8(-UsI_W^o};q*!`>L%x{b;b1*>=q5C?NH1B!O)N|T} zTnpis()6s8jF-sHxMKszeGpt{XOnohE7iJ|WbIeFC&up=ser{EpXYhjs-505zn7cm zYK~;GAP>b>?)MA9jqK}Z*L{Nt$_f2)&b>98x~Oj5KtE~O81T*mKlBXJ|DOsF1Q&7T z48gb-__v+wBmB>RWOR^q0@+3o42*|^U{`F@Nfdyo$2Mwq{9cZZ(ce6v*5x{gJ^BlW zy4fy+`S%?*$&n55@%Tf-`7^IWJ*(|5O;wam^ip3U?sDk}N5WHKrfsfgL6&2Q-K1Zd z9o;R(=)qL)xt$0*H10n5?8yX$YeveQhTMroC(-DbW9}j=AvRps6p}?(#!T3CPy>@x zp!O%hM1Ht3@O)~=RwE`+Hy9YGx)J6FBgbNcu z9M&inbQwTce=J;u^`$Ryo?<`=K)bf}-RWdx78d3_EpqeWDo5P3__5R*!ykWa+AFit z5MkUJ!WE6iBiYF4)zj(vId*xApM5rw1=4u`=hZJeq)*AkVVq*tV>gDKJJGQ zg=%&d4mk8?o=!MxFE{=?oVHSH7b7V zj!+=TWfgPZCd4w|m_RPw!0MD0W_o?-VmG+Rf%&zCI;bfg$iralQ%n95J80_X8W`?5 zF5l4(1IbbfEZNfI3%2U54m&q5lSr7TRGgq73THt|3bEZKW?#&{;dqFHOv4D4PV`(y zIru*_=X?H3X7V$Q-{K;1LI@2Lf^Z`oANW5(lgC5uqxm)tZ!9+7Hx1dmd#V~+xTL##d(K9`HHTLFQSJ$BaZCwn%3B}P_)Pn?f{`xId_y0|U+$o2C~_bswbzQ@70(kXlabbr+2%d$_EP=^qgk9g3uX8-($>i_P zpXid;y(if3Z{Mf-wVq;Wfp}{Ay(wkB*Y*Bw2_Jlpm%{V}A}|=9`a5QwdIGxlz@`^| z?Z5xQ6@A~AM3gT6phwuH@b54A_~Sp#lDZZ*Acnv5)KdH|Za{WpR?KiT7b&OFH=0zr z-uFNIZZi?a;Zd%{i}kK_ejX6IX%Tv}!tZ;%tuT@6&H#bSpUCTt!8Vj5XyRDEFwlpu zb9Hghgs#aHh2c5lg`{#bwoR`ljT!!6xhdc(@(DK>rRIegRxHbF1${kVrn}|#CfLj5 zbR(_V0b&C*k&EB!kKDKVkt@7s?RAUhUT&zBw43-bDOVUHvEG~_V!-5QDthf(+Dy#p zGV0{fvRxkQMe=Z#sDe0D>SNhdO zv`k%2<@~Tgw%)w&Mowx|z=y}AmIUHVgkO7RMxtkze<-15!F&Y`_vPhUlkp`b%q5Qb zVz${sDP8YKs${K66|<99Gnj~4Z!$ixqUNN`>Y0+QdQg?|M2C3w>gDxFkPGFn1mTT# zTHBcrtHm+QEX8^2uuGLx9(^8V>SyMCY_OUmV@mRlg8E&m^XuH-I^;;GC{tqa!q@SM z#LU5=i>=A`R;#C64ATyCIo?`f$H4zf4$4-FrOqYX2Gyf9rw8;&VOFCusn+D zfs8u)I%Sv;p?F`B9u~hhp?I|vx}V&wysuop_siTlj2d`{4sX|~w=Yn#By}T(Ey3IT zw0Hf}KGs@~V=%3)_04eUKe3!Q@d(A65d`=(ej>;(@MPPU*##T;HV$k4)O-j!OO8Q= zt_dVZY!DJeyM3?B)Q!1@KkDUC813a_fRQEjk@koqTc1^HiPumThe`RR! z`f)q+b8g5@V+x*dJ^%2&+hfVLge_RJ2dT-~{GUqkb$9Hh#tNJJINDUYPFhq-sU^7n1$5)=GGK5y zPj*5D>{Uvf<0I%%`dB>EH+b^!pUN>N}B~&Lg2Z zJEYMVqTx(07R$~mXd4TE_g!Z060v;-IGoZGY+j zN3}IU1~wuVLrhihi5vXSZO_QxJO{@OHPTPQTk}YIl3s_QA@kwZ?u=1Owkq(Di;mUl zqBb8<+Z#v!&8cw_d3&n-bgh_-utu}Pz|O1F^dgy4oe?M)P-v`lk$l7k7E(XF_hDxwrkAu7kHkx*@)ItRNjp!;0E6%ddgaST->IirM>Pw7Aw7 z(*~`OYy2y8m96)~IEaUhVZlrHx6A{D;_ZDH=#|MgaV@XOJC`Gquti246Ny52G@%C>78Rs=x= zVPGVrhm?@+h8emBr9nCbL{b_7X%M8lhLmoQPU$XbkZvhy_)hS;p8NjZ_j&*Mhi!A4 zV;yVlwT@GPf%AtcT*kVT}5BRc*43M->)$R(c9TN`e1g;KI>2kfQ~JfX>*BRZhTeG3iJFo)cL)^jGF zv~6gk2_hgIhtmU#717nmM3&aGTI}|CI~h*3GnvG_$2TaTQr0zV`Xa+wuGE4+`SIuB zrkxhlPo&N1l78rzUK5ISG~D0OP_!|yv3Y;KbtfH=m1!|deIMifd$-Q~SIfk(a+y{7 zXIkd8Ij#HymB}!SK*)G6UTZ~TX^|x)lqqQcRqN(@CRl*}n|Q>hbXOIDNfp;QC8TiF z9INJ3Erp9*Q7D+zb|4H*$7Q>N^7CxI@NVl^7HU>DZ-E*{%HEr)N=YK&Pt5Z=6$?2j z0d&96Lb9-t^}OiBbbh8Y5A%^Dq%3_Quh^6h#GH*y+ic6w;+(j8xJZX9 zcgG${Y#i=~h@ep}(zs<(uTg#S!3Fs&QXit?ii-Ulax(jOr2f~?4FHBKn%3k094ZUa zG+OTbf<{AyW5FvqY#1P!hkn*%352MT;Jq}z)FCT~W!o%q)SOJr!sBggD;=%7=-)4b zwZ2`@%R)I^`_YLtDIR-~m1%tfDw6_lj^~Et-)evFb0D*+r0P=kN@6ek{^=$@pv5NG zt=54J?$b;T;1QcAQVrHqO4J?bYaKnYS~W=)++;Gah0Vm0{dQ+HK_*0(F1`dpd^8bb z#^m7_Ms>AR|NPUWmco5%={AlJdbk|szo1U9q*V7c>YM+fy}Sku00nx+%#eQ>7<=&D2W4u?vpSlW8B)% z3h8{5GS{&$P{Zl1PW|BW86SaDt zsY^Y4hjypY(0iechP*k*izdS zD|z|UakA9j-jx=9Dvq$Y9*H}UD$xJL*o=mabT@^8qA0;+MWP7tR8{?IP410dkfI?9 zjN`#iSo#~z|J#YOApJmNfwvP-#{5@!xD zM)s8h_DV>i2P^IudP#k=VuE&+aJ!0r{ZzhC<-p5*HGr?wvfS6E@r1z$U zeBSGgQ`whvXPJtE$Chs-u5I+a=vqTZk!q;zEw&RzhihE>F)hpJ4g(<&{ut}7=z{}W zapKq6*oSKaz9f>PwrNE$uov`p>nRtKg%r=vDuVCmQRm{>;Fn)f86+!2zCq40I>`QZ zxV4B;${>s#Z9;LE9|TqS2S^Z|@CCG~S}2?%7@K!H6~v@HRX)24-J(8DEhRkQuVg2$ zE-oP|cX3Oy!A!0{oBP-oB5dhQAO~jkRNB7u&6EC`H(RJR91Aq( zZ+i>1LW41wZQYmwe>~6!B?vdJVl;;8jxt8e%YLDu8)t!{3p1wE%tW6~VH!WMjtkjx z#%(b?>FWkTK{A3fE921#a#D)?`O10ZjcyeW&7&Ar3_Z#$j}}sb#g}{F&)9WSiZqb> zw0r6<4G;kuW8XC381>{Ro$P_$03<2^IE&cx|CI;>-HYr*hZ{)A?+}}e?VG;kC zy9JbKE;b*4L%j%St3Uyep=sXlp;A>ULod7O9-W+F*F(N^K3yd-1GE zOXfPXXc;$48nO~14_~63C>9$|-^to{Ts(T-?e}yFtO50>L#$#;Xvk#?yuqJ)5yeFu zxr~fqZ*iG0`oKxncB_(;)_x0RG5)?Cqd7|Z&+bZ&Xsf@24RV+1%Oe7eZY6dlul8{U z#e483+`T=9OcaweRhdJ{Qq40HH_duXuJT#RVeQ*64$<%;urvWM z683t!C|?89^BJE-CL8lhltH)eDvrrG(q_ZB-<3{wp7JKD5oh%M6qEU(#r)!^!4_rX zWDl5l>xs#7#zM>dui6^MJ#6rPls(wo`KI0CL@li{mi*lP3H$bMereU9}6C5r+eC{d z`dEREs?kH4u9KUf1K>Sv4K6v- z60=i#c?K145^(H|dg#OE70^Q~i}foy_2cT9bTAzv;$brcCtX)ZSxo}>ocAS4m`Bx= zRNskfuz5$xrF!k&Ti=x)=xW=Qs7>zI54a-x>VvJ&a0@eHUON!eTV3i^G6rSr-KU>i z$sal^3pwEZP@V`Xki&=y7kwl5GhM^|92*9frY{>c{+^Ep+*&S^=SLPMqFB_80k{&Vy>z^ZZ{@Bd^UmzI3 zNcg9B7zvJ51?`U|4|0;|k8OFmld;w-r|gs1EE*C(ROwg5Vi_)l+9WW890hAFG5h+g zg6*|a^<;zoJGrwD@4?JblO_2jctaGp0c$4%-tGk4pNhw#WbwNFSH%-$ad{PXuL-CJ z++SJFXnk1TEAqY6jE;TbZ|G}pza7iDj>5qk{gF7)GxFOMGK(OhYwVgC_9Nlw;{fLp z_;O3Jj>Tb9)Ogew5x{1|%Rq;{{-zsZm-;SbjkTlbM6xAA^0o|>?8>>>bVv>yK9nJ) zMw6VrPDR>=t7{p)F{bV_?;8BLlcE;tRj^p3KpEt;-QoFF5AQOF`sv~zej)|^`P2@1 zt<_0f#UsOAzj&X1C@7(U(47@d)%=li`f@6+rK30=rC5wpzeOk{xd~s>RRYKE6TY>H z=`DPSmq?HNe|J=DdNitCVgIQolHng&84QfaM|_9!;qTg`UV`bxS8SZf zn=cIP^pM5=y^orFE5rZ!d(T^JrG}bnfYCI?pXC_OAl8VmVf7hnMEx`n;NSAf4E#RL z?|$36Jl?_#<2D~Y;WrMCo8X>MpP+*K*t2go0^l!zGRR;>-}WZV^?eox2iWTxpcX_I z*+cPDecv;Fn>8%>zON%`4bX#K>?=1#9TO10%&ojB;v2d!?stCRzIpiEo+Qe%OdJaw`s9_ae#d%xPhFFGOTGHJ?Nj`7 z>!b8H$H|S-ubadK6Ud1*pp+H)Gdmc9Yt;>LHP)$Fypf^!O+#R)f%S7*99aDcM0C35 zvh75@>N<+!kIzv=Y{F8l!FhQAPl|`?SP(RIB-yt-4UOp3P0k2xqoy)GS+afGK8lHN zMA<#acB>H`Ky1W5@6peQws?vOe7j7NUgG{L3co(I)HtdCCg1<-?50~H=J7fY0FF-P zwlWCL##LpToz-Y)#)T<0jT%_Kvml|P#eI)*1$quRc{vu^ZU0Qbx8J=nK zy)}+K4MYd2p$v+7PXr~sx$lKEWXH#Vgmj=iNuTgFVwx%`v@Qf1$xwyUXQ7+i4nRR7kjRJn^Y1LZIrf^cg2dZo;2MW=;Nc(g!7B=!RL6+% zlDx(Vp-Rx|z+j)%tBCp25mh2`(c$^Q6;h~0`%NEX4J|bZAd1-Sx(?A2ed$bVQqlE0Ex*3J(xZ-68LhF$~w5bG>(<%U)#qQ<4L?|#!{fK=% zX$jjS8}|B*uwY71Bka>A-(OD7a(BpR0&OULrlJYU8&MJPOzA_xYaCtS3G#yVuFs}` z4q-|u60UeL__JVp<+pUI%f?i0NVyk!_*8kL7!)LcAtB=Vk81JyzZy26TDaKkb07kk z;75Sk6mNWyK5F{K9RYmPXwW8}9vlGinS8on)fszB9P_reJk`o4W<1!U7l75%=b=~4q$$r4~tzuMu{>)npkk!@PZ-<&i- zW{I__0g$ouphldO*?>d*I68y1_)Kld3Q|j&pfmPzy~|;XuWnndmG8Deu!672Q8o@v zD1fF!6Qs@&2TN2)TR4{QNd)a%(a!0jI&v_W+Hh#keY?M}h8?j-3Y7_HDULM}q5{%n zDenN1`xat9XV1#v1fB%G7C~Rh{cYk!xWyq zv=;Oqnu~3Mmiu;={|lXg?3f!MhoaW`FMbi0A<+D?%%*}3ftXTXoW@CeF1`&K2-$b~ zL^46bV;VsF1pB~bJOFQcN|-7Tva-m#6`J6b=HUFzgHomo=-toDqyX9OU@!oW4$iaB ziKdwO))qx0a3S77k)>wSpg#n%gPm>W3_*o6n~Kg%5_@vG4lKr;MsT3!OM^?N!r>sM z%XZix$rsZUxg0Vk-hkW0a#@KnSbM_g!OPNuOyFZzlHv452hKGHo$u6s>*O1o+&a>Bghn&PGHM^g==G z)1^(Jz~BW`|DhJINc3iC$LsC_?#nU#ylL z6x$RqUqWofaG)Ubt^rO-Dv)LqVU)t#j4PK*4vv=%ZKsn`6hQ&RKjkAhUYQvE`slk( z;YHsE6%~#n{+v__3y8v8`KI?zQIFD%iL{Ed>+XT^sha8Edn3v?pb$gJ z_Rj(KY{u%v1Ojv4%GAd;p`gHZK4UpVpLCuvz%@8>2|A!4rp1b~i|~4%w-pJT8O*5- z$-Xx#yY28pJm4@}q@!7=A~l9wiGe$kL3p{Hd*5v(NU)zek1^MUmrR~n`yg}$X1QJr z+K=(H6#bh8C<-Zq9nJ8dTlECqU*Y9;;0jm7vBZ}rVu-cL%OcO-E*ve0+t6E$phk*{ z!TAMffN?q`>RCo$e-AHXF>C;0O92Jc3p$FDBQri1MFp`Q-rW<)aNN#W71?id*4T}< zC}$r1aJSQaP{GWnXXi(2t?~}C24=Y)iABItf?$UU!MgN1J4gcjw6ZZn^R*qnR9Y;#%no9Ln_u9w=O`o!^!oIf#6PfBg}L zJTA-Fl2Yk{u*IV^Tmp5OCvint>@wdD1qp9>jac85A92LI7haWVl0nr5R5=N`c(Gi~ zTJpSDCZCiqJ!xhw0v;R5iS`gAlUJ*AJO``wxJ)$dYm7b5a6*l+2hed0{5sn7ks*(~ z#B*LP&iP&*oc?*u^YIUY!DZT0A=*YuTVtiic!4;Oc*NNa5}~+hieq!y1qEG}oJz|6 z|1p@(0*QP+UPu3zUP$$qkIP}D{H5|2R`vTCn6>y0KC+oOupj*k!H#%4xOq34R+b;DEc^e^Wsx&L z<+9m_BGBHcjf40kv(a&(pia}gp;oyO!lVJxmy1u%MZ2P$2RS6l;|z)(z#cWt`vFr- zB-maA8h)u|j3QflbfsmIL_wXAMHv~7y7rgllybm;f#=O!R~GX*H`HoZ-OeSWscP-d zv)>L|SwRtlj}x&Eb=l{CCdT0#8>N@K@r|l6$${apKJm9uA(jJyS?k}#wW0W5)9wAZ zYP3`^_SQtX^bi4Dnp{nF_58rkms9#=gb>zs?tI;)3P^(1tN?gRvlLE$Wz42TSZLDo z^YndKSUepBNe1d>*BehhqCSj|3kzYK%3k;^0!|N~iPsn+3Jg{GV_*{d(!f)$KJ~rW zv`zotdv8P2iW>^O@76Ypz2{OD0oX~n!1YynDL@5WUo;0~g)C$8y~=3bk-EB0xo3R~ zf>UU$Sx+@n9)~B?g($WIdg^AC#DWGcR%KvJjA=_VxroMc&b2!>-~%%V&}HT7-C-`4 z&r5GJvDF)w!Fv~a4N;aQJw*B*U>N_dW&{*a?Gpz$%D5WS1m z&<)Rqr(jX)VIi3eWS%khknVhcevi}d4GA>&I}s#V223HJ7Xd3W&Qqcavd%_-pc%S_ zt}yxzpLu!WG@@ivDQ`)PQJEa;#hZ{Gkh?CFFF>&<^sJAYB{yH<_(NL?M5-A%zS-=L zy@`NLI*)oVT<0P=5W+?$-4!=hIL?{FSq2zD{lIq-2lCMeA3s6%rbQ6I7hKcqZ$})b zB^YPzTgQk2c!XY6Aj1WM<-Uv#0Ry1SaSnHd4#32!nH%Rc;e~QP*yx5RPz0z(zR5VB z$Cx|)%9d$VNs<5`RB2&~wGL!#-Wj`X%^%tL0-A2^-Z0hh=Hxzs_{iz%Q&YP5)zGGG z=zUPpl>?pnry0XP%wGxg|2-6U5QAPPCyx zAKYK#(Y4-lG!{>#1hE>B*sO-g#)qXEMk-!*DLM^sm@mkh11aoNBZ}Q5>1ey>tDhcZ z;PoC=)2bnq9j1L6FT_SyO!a` zocqa$t%_tj{@{b`(65 zKB27s9qlI;P1e~#`AR>5l8@OCI9zj_G}vC^SUq8aknA*J2lvPet6DKY7|vRzVv=U- zLS&oQj0|nV&_>glZkjzMo_1K5i^tUy1|^|T2NI}GB$;_C$K&K8R?dL0DMw^-{|P#B z*-LyMSGg6i*}@KQr?Te~#uC=F#lGb>jUCg|e8b?!`{>1b6rhuEee?)Q_;kDjvnNgT zjIuMQ0=+PspZ%`wxOqYk?&h`1!G$63@3Lm+q^dv$SVc+7uhUuUHBb0KJWhv5R;fwI z7x`*Ht%qFvt0L=lx=^#8EUANgQfF!?9tARWe=7p7N9DcE9@76)?XGqnre%7@7O)u4%WUl{Efj1Mc<)F%GZ4C>UKi$ zM_0`gvB*QJpp}4xeek2Pgd9A0yU)@5fnDs-)rpF-DRW92$Rwxa<(3mPcT#bn%6?(U zYRr6-U79<_p6mNs_ZPav8()IAD4jn7hQgwy)yadDs!b-7PQRvZY7<7qzmyyfvMrLz zEkO#?-IkFU!~ddxvEa`FwB`mc`=k3JLjK3g)8sJ!Tq ztA)dqkB_y&Qo}6K%z?xtQN(6{mGh&RA%?8ij|-J#C>P8(mfiL~UMxH-WmlQdnWw!G zL;GQLx~WcQ=@aG6n0Puw@rz%U9L^_Xb7s7NI=+uikVw8bIsNK12D-u~m@CBfPsyqt(l ze_8r~5@-?k;!G5!yf1D4zqu10oI7=fCdep0P-g6kYq;*W``|aROIuu+8)A)rNKgfPuVqyqM$a&C*(ZWd3_XmWeC%NCpdYo z_236PG~N898N6J1U|-#o_Yv-cOl1sD4LP@jbDLZTRYatmHZMue++byEEP*V1pOlCV z1lh{n%8tq3o=fxdhXIY{_}ZHk4qf6kh;6JtU0%lJH@ukGYi!R;dtn{tXf?{;M~|+` z{xqT(DIA&?MW1^sRPkH>okOTX!C&x5tNCP*_t$;lJoaKDQqd{|^cxkax;{3O=zClFATeq*&gXD>Rt@qGR#zNpHtoi&Up>1by3$|c|K4l8 zK6AZ%NzCs$d@?MM_S$$BPYv(8t%fqVn&S|OEuKYOF1Pxl_To+*}4{s<1VZCbIdzu zu|8v+aKg6e!FKQW+VyyLB+Y;LMiuRUfQU8v9o>)vcUNId<(>nU$lA$=<1hd8r5N@a zl=TL{6!p`{50#<>X!N68%a2tjJ71Er08S^1T1q&<@L16?4?`bnM_rS-S+0~O9Ch~_rj(5&`%}&;ugjyPaMq~?` z`7Myyw+Ae|JcM4bzWy<<^YpZu9>~X0{)W~@sGDl`3mtb$FizWxRZoiQq=A-pQ{wX{KyC^7zP&{u;-okU%5Y@k-M&j?Tuhm~6BJ%(px+Sb%e53t zTI(?pLCtXSWsC0fs0A7HfZ5C=KAnmzzR`UA>#f$ydLDAb4@X5VF}cnBVLV21$xOdj zfc05MT~kNsZYnP@Ml&r|9Ig``;CJHJDJx$FIADNUuPQUa1dT1x>7@0XNQ0-lcVu2} z)dWDo-)_v!xr-r=S2>IyUmkpusLjj=AW`$S-3cY7a>4=OwZSk#vsB9e=frtwqor>o z(Gf0ElE#o?vpI*wnq1Hz0x6$z@5_JOTW^2Qtqwt4nC+Gfjd>#}d}Z`e+fcRE;3A<& z?jJ_m6)-ebmC)m&Lyp7cglzMOgeGVsE&qAxymxt_Zx*#{y8kWCPu zH+aju=Zgv&SjMqcFT#TxNzXgilqz&fmD*me>xa9EsHMB-yl)qK)ao_dye>3K(BHea z5(3c_nY!A0!T*UF&}?$@V+L>3$aD;3z4rc0hiLAK58$XYN?h3Bz}qjccSWVr8XoLE zIY!r)QxBGzzDrMOUL^q(`vGg=DPy3_SJa(}SUk9opZE>(idu6A1PO0JAJ6sln-yQV zUOx`+%!tccWYm`h#*c%-Bh#<%N=zzK38m5Wf1SYscbo z*bwj4{#3{1YvE@bkSd2aL|~>k+PGZ4+7CfUV#6XKNel6GI6@|^D~x*WJIQf3rxfq9 z;D5rl9g?h_mb45IcDwv?i(4^GEyjp>#Up%1oYN!W`(1jsz)dV1D> z_B!r^u_%&ivkd=YuZ?lU%4W2)4i6EK{Q%OegK+ zw!4&xC)|2@eZgM?hg;7+V{`G^oR?tpGHV876p#Kyey6MmXqu%SMhq)6w-~sv0!_~V zOT(5ul?h_w&iOM52Jk?!9qASoBA|smY%k%1JM$BZ)8z5-i(l)Bh_}8FvH0Q}-jMaC zIged#CWKV9qNL$@$y%;f%Wc#Bloo$vNP=0d zi%$I;j{zUCC-ZWfM@v_gsm;$ZZk)ntk~&H@2mWm4(g-%$rQjVTbm`6=SY_Y+qoVMSchrX`a{(PeR2w?YM zIZ=EATh%IbiGdkRgVSDcZABp2ytb2Gr0pI~Z9`-((RIKxKEQDf{#aR$KF(Iauh`?;B(a4Vv}7~sXb8d4FvE2l}SOCLV-}nbe}+Wsv7^eN7lXH&+(#~KJ|EKSUQ#!`-}Uu)D6Dce=+XLs z3j)Ixc*YHMDHf+Hz55>Jox{8r|DPU(6#TbGxt$n-Ia326L0p9}v67Z<2RqxmUbs?r z{g5SVibWY+POh}Sa{NuJj6^0S(XluIRcfv00MKd?MXm;a4w=S(Lo2J}=?un!2XPU2 z_W5ON_c5T1nGICbV4xW&K;6SNQxm>FFUJktGItZn=^>bAkWl+kP7*$9?`G>TdtmWN ziR&UuJRw8rW>&S3v207rjZ~Rj2)!#u&4rWx^I^x4VFGlhk$;P%J(DZaJ+Ztblu|+; z&tdh4{&LG3{3UT_mJJ~Ae6aM{!96XpRDiI}=f|=3kGk*tLaZ`sUN$&Za2r?!HC8mKynE^ zdF-lpi22*UF$5^DYQqy_|H7$b?FvJ>l=pExj2nVzG(kTL@m~K^SKvPFKoADi+t^!_!R`< zMEF{9khqY|AHI!v6=x5ZL@hcFwY8-<6pgj91z`pfR3EP5(Y>W(=K7o7!;se8pI1Y5 z#=L%olj4AgciqmuVFSWxA^1(b15TMJKlfPQi)z4((2b)r1ncU| zK(LMT?*{62lXEE$qzp6K*DlhvIIs`r~1o|<~Fe^n{0N@XgDJJ(TqQP#!-}&b3mB(=6BCmCu}3YFsqitki^Z!gFN~F@5>?`F)L&l% zi(*p1v_9J?{(R=%X0sy@6N=+|^s6yu^+4S}2v7sFF__)6-?z8Q(`JH95K>%kz?+Sy zMlD{fo}ni)23u{VRZ!SeL{d=E@=1n_6aCj`Q>tX&5F7rb&~X)wbTb5NPlW|k7`~x13}tvRKg>Z1?{lfAn4Vm-l=c&OiC)qOerRYj16u6!t`g6i zO}pP@Q1**w_)iu@B<@aZ20(Wy;smkA4u{&<1W2KcL9dO2Fjv83f6)=`++$preOg(| z)I{CM^7^avqZ;J4t|b@rB)eW(Gq?hFJkV5aKYn1BmUZYKCV#mBAGxg@YgBZ^fkT($ zCD+fs=<2+Sb6Afbh(1tOytaf`wei?*Ax(#Bewo9XfDxTHE40Ft-GJUP^ zl}$P+dZY|&>4G(3wtnpB;G=iu=klS zM8l9EV84!*2=QKi*Oe6gtxMZXKIRN)OIg2Ozx)b$U>1Ly5tgB*dLMav_f>JQ?*=n7 zC&3w|=DqR+$@htyB(@->cX+Qc>>hJSSh*J^+b`%-m_^Wu`G|kov{*iDG*f3wWTAot z@u=#4B#7SlZg!B^+f%}5j91@n@u3w8V#q5gEc$CWeoi4WKn9TUovLL9s9H`gqd^IZ ziEBV0Lg&$u(f(umObQXS;e`GB%<+7=kZ|Pdoqr<7mR{z7WpFTOejQboLIT%R-V%mH zoZe^Bm_g61V?GEMx?;;<4tq_ zA|;-4os??MRB`w9pkrV7&Tra;#K(K7jDi7b9zCH&MOm}d;tA;!4!*1QSb80yqh@yL zv0?Sxa3AEezV=5nxC&L!GxFAlyzS=E8#pM~!Uf+48~U=vzr9jURt0>!$84H|Sib8s zqq+WTSOgjZoP#ljs^`EO&DQHld2l*e09bN`rxOgY(}!u|$`=kgK?}#L4eMh-T`2=~ zm3_+37~pCGNMoPK!2T}JgN!n8x-TX8?a9)(?v-*J@(ye65Km!1c7C}}iuK9t;x+s9 zdsm769Pyor;kw~$foo17rxN#eD`lxE4-I*XOpVG0U0looO$X#b_j38_HCp$rbW_So zEWG{4x+x8~qc<^j?b)@CT+IG~rxP}K%Q})z!3XIRL(lzQWMe!A!hBFZoh;MOk^|aM zKpyi0>C8IRax;E;a$DFxg@aM~i=lai5jXApJ9?IP7C7T0xW`8XnLgwA=f~sKOfHGO z`M^!|eNfMhjeDg*0yez8I`K!rF!dsQ^&e^?$5+z*lkQMyzgt?}aWUyl@KMUUogi;J zwiBGk9u1hm7-bQ23wZ)RW>tcvvtMkCtP;Aft22QQ0s`!`uYv)oqv_1=^)>+hk}8Y` z)%))PPf>zD?HK+oIABU#+?niv7Os{f0Y3u7y_#oGg=)N{7`jJX#;vMMVj~YFjZiJE zt4=05#8WLf1adM(BxI|SNBX0Hl%ER2X#_rRK!Um}pt!D^`7LN;DmvIJ8JT%yRP?@7 zQ_>94HjWESGw4P-J6+u##}xa0^&AAF<1xAVjYp>|(BwiEt|}f*Cnm&G-56^W$K-Kv zByY!8@3Q!3T-g8Ms(K3aN00(gEd0Cz1FX>ptCJ;1hdqwX#kW;>6*Dh+U2fk!@5F)i zoV2aaxGn+-=Z#-FWl}-~?uA(|Z@mBo+v%j2j3vLbQ^?&3@Tw&F-RdwfY2P4MbWL*z zktmO^GelhHFc-6Kf?&is%=sTFU0yxj)anfF^FEeS|D()4q^ky$y2{hitj52+%PSec zG2X!18_>G~RJcZ4lCy4Iv5dy9h8OxJ{FTce$znWNVx%aW&cec*Mk9L8Kn9L_k@$+# z2%Th1NoK?fQ>Hh7U&0T9GDbwo%(*~{DQl>yyWEA9@hmB*u}AMNi=6zy(|5?Gv`G--wLaF;wMyqdwjvV6(C1}{arM3{?l10 zSr`SV=exi|M=UNiiBr|pl^DynS@7H zz{C5g4965QZxuox+n>Ly8me7%PtZ6X+b(S!V-2o&0hH1R2R%30eUzEnpGo$=*8a}! zvg(7EnMr)HKzvRX3=c3OxtXA0eZ=HX8mS;FOikPt$zCmGzrz3w1k9g|d^0Z9=Fker z`9JD{PB4;!sP$beDqBvlE}~2Hjm@rxhTVL|?M3P1G|S0J87lrO=VAtu*_YT56zCX3 z#(qJd|1>n8$Op(?z9<6bKVH+&*wm>bB$0!hOn}pl!Gw0hG>c&98=K#spyBaURsIAh zDB1GCmLHBECnFzJn8o4I%}+OF$6xlhcPc_wbK&x*1^CY${fCUzRDle?Uk9rVz{BdP)(oacyx)ZtiJl){_mla>gl_ zkxv(gF3ir`okUul8^AMGWoH0iKpXP|{Jw96643~4ue76;0U+s72R14t`5U=HPReIT zO8;y5)SM6TYJK(y1BPRh;b`fy)C+To_b0`hKJXaq=j4wEr-APw_rQjA0ivSx13mii z(r!ACgr<0J)YPcP{>H(=)0|Tt{YNnAy36(fRcQIfIAaZn zw9N}2e)%xwM%V$%3+d7iT+7=iP*92ZlPVtePniY| z5FtYR^+5*Hje)2hK?9v(#LB!F*MrUL!XCq&xym$>ZH9VYW*TVfh`iIvJ1@f}X$q)!c$@=Z#ZXUeoYo(C*_bPx* z{IC@9%y@wC7>$JhV%#>G`;@*cV%-4vNZ8M4jUmkLN>dxEeWKl=PoC3C_adApxNVLF z=>~eQ^9s0aB>DR2W~}K_2h==Mzk)-ez!2}PWRy!51-2Pf_YG#UY3+nAQL+nQ_*1Uh z@1k{z2UlIRR?i%)3fpFqZkhiX{W4VMV$=3EkQ|6+W{AO0OaY~%_56q+@Nl0GjfIz? zqTlcy^!vKBb3wp>^~Mf;zIXyc!_0^NB>O9^@3`Q9No~U4e>Gjq4~0NS-WEAg&@f$S z#YV+co^R)ChjSN1newUqkQF@1P|A7# z2N5T$0`<=IT;qtI`pQ=Y_l2b{$YfS zv~(ucE!}AOc_IAxtJHy^+8%<04GQ-kh_CGVSG({?`Kw(l9qr43OZB4hCR0c(6gVyb z6jov{RbIe}-~H^9cfbYRjAHK4Kex*xvnKyQ5d zqzTqPIrBTdR0uU(4Eh^*?7U`DRfcjBp~-r6Y;V{mMFd1gGOo=Qu%P>)8_Q;z3+ndY zbY)yH6rN{>Mf1<=`a7TwNQ;X2f6bSu80x=CcjQ^$^Regn8wT}}#Eumkuf>imFEHHL z$J@J6;{WBJ`{Bm=v|6Hl_w`u4Vk}9-R=r_EgnHw31qDU=`+l6W!{Lj&(Ak?hMk5lh zjzq=9mTyGSL=b-z`i2+L4lwweS3w#iZqwDDqx2++dT`g#0VIZPg{J+I*dNgUJ|nc@ z8a6d9p)cEM)p{>ZR|-C@_jxa6>mNAXW4AX(8!4tO|N5bKTYvy|I}(x>eC-Q@w*x8l z(rJjA7*s56ik^oF9(4kU{-w)i2{Z}_5m*%Gd077xr~#Jw`5-X4^l*nH?!-yF&9xyw zB?84%i<`z@Q5SkSd;ulx3Bnex_oaEc{#VC{B4)7wlnF+1wMuDFK8_Ym_9`=Z>7N}< zJI$lCX4fK4NzL1xr#N_TnQ>n=JQm*a%nDmrwJW?Lpev1qA;+& zU3?YgfR&*ZgYnK!bFw=UQ+0<2lke0PAK3p)Zu--?csm<9!O4hR%=W@9_ofKO7U|SD z95s;A6vEf0x-qZ^L{nK;DRd1o7^$EV-zr?ek1T5o@Fn}@eRjV7h6?y;h=lmrnC6k;WSLvpF=YJ3gon3Scyu@K@|X@TElmD zZirR3cy&O5{qAMRpG#0ab3TJs#UH8@%SsDJ_d1|9!?ufaZ119_zn0kG?XNgrRQRx}|f;e>*lg#Ql0kLJBp~UgV|L+l{x+2OSC# zFBMwsNr8QUPgV!=yqg-|-i;%GZA-=;{4gcFI>t0~Td;n{>57AT-G0FvrXJu$4G38J|UNlU+Cx4k@DQn&>PsI+@2quYBua6?Tdv* zz};$cDH*A~S4Yacc_xkj%6KJ2UI4^o;a{Rt$T9YWSS-xeV3_Vz5E-FX=hUj%UI=x| zja_x*<1b&P09z9hgs|MZc!!>=Pk@hzlN6s_#0F)mRqKcCux-TIscb(yLpz}!iUK6_IU09nT6Tly9n&2qI?Ao3`b0WSk97@`o&as{~eX(CWf>T3fUD<@ZnP^o2q zQbKz0f=AIa^U9<(9+j8!Kar$+i#T(*-%cjfZARGzKyU`@fefJDZ6J_N_!A|X=`xix z$U%J_S>ngu5WZYdmkN-(A9GAaulKkL80ZABv_iC_mz}8-xOq8mFAq<8zPi<~kiSig zgoyzStbkW_6xM8)#2knMNzx)DyRv!k;c6$5H^n3DZ*Q$Qc%W>7XjaI0761_ss9B3el4N)W?w%v38?OdhGVN?V};!r?s=&FDTdY;>E%RE zX8_ETWjfA+#D|vldDNDZbTC5CAYjc_qvimaG=+w2*!BNfQpo+3d%g93`~b){T$j30 zHp&C5_p*Y9BJXqsdjU!O_iLG>?Y1tLGb1rZ<717stI)uR22Eqg@^i;*y!(PhuP+O2*=pg`PR8) zBE!MG?ps3u{weX%r*7r?97f{rcjoH;sqp|7tB3Z%1z~?e^>ykU48@qU*O^P6Y8c#S zZkr51qdNXZFhN8Bvhy&r-H5aBP2DAiRbmv?_eD&RFf_&0PoVxCRaI6HuOS{ALa@uEAFyKK(aKkZma*`RlfKVy!;YyG1?MV%x z&Q@%h`Lob(#bS8ry$zovDntTXor{<3ASXD$_D;xIat9lytVexdZq;1e*6zJK!2ot&8)vTIU@qsI_|lH$bCAMNr^$)=qeUs^xI!t3~XaK z>s8G2x3Yn;95>bEC4;|2!Eio*5+6dJ-fAgqvh9m{As{0`PKRa|{>+S}#L6fo!18grH0xRnDzUM zT@QUBeJ*$aPSmDOxZmAg+zN5M+ewmW?nYDkxGBt)+6+|a*Ag}Ur-#i0(X5%KA(837 zzR&o0ZoTd9zN9LL9}T8ze)dL1Mdw_T22(tg1cJ=Awb9n9v^$O0r)VkHE{S_RW=gC* z&rh_9Yi3v>K||NGWXxO+c<%-g<-0)E6nLqma%og{l~?@K4$D2_d5vNio!G)Aai+{8 zmq?4jJx)_20%i`ako5#coM2yeI8|KeNQ#;HlxR55?8$ZpkmZdE4-!#0(iO(@I(0?F zGYDbvf01uqTX`do(F zYoTI+-4)(n?3h9@GKT~rVSYOR?Z819E>Q+WF_{aq_Q*@!{pW>@mX73sXfOdP*6+8H zG{**KdLua41xw3nFVdEdFEJC{8eLT|wlq7GURffCc*~&%h%A1NeHd;FJlaiqX<1Iu z2g+e?Ba}s>Gir}8lhQ27+=}lQid$ zjFIJHLLo-J`^$Lt;w=sr$%s&<4JGX7sIPZOB*iBuH$oogGm!>+d$6$;FE)m8=}moH zmSqd)+SHIVQhwzn>Ur{G2OufJ>wW!`^IN~a^1*6P+g&_Xze0a2(i?T|O?9TuoE`r& z2@!|ll`;1Sz{TVnPy@ED*!|qo|`qC>?}EdZY78|D)I3i(&n=4(Sla>QM7#7uw#KgQoqqvxUF!P?q($-}e&_Mt`+nbd<~PIKe`MzB=bW?m+H3E%_7|UU`i-z&nXZU?(n*EA zj3=d@=+JYzQ-%XZmNAK-gE~PInxl;Bjkoojsv?s3R1{g^j9$LM0mHvHu?r=8pF=qK zl>kYddFjj{YCQ(KnJ|3|d8lz4Hjozd#pwVK0Gz)!6P^EjTw8vCDQ0}Sx32J-P8QM} zy6u*H=920xuv)}_5RxHPf~^&@J_QOHAXte3NXtbaTVfdK{8X|f;p@68YgrL`4BM|) z>gK(8M~L!J+)E<6Rv-+i{?7}q8t`P|YD#&fI8(lLp!Ao)=p@m2-UCIxSRnL}{b2rm z@Pd?P{BxsYTp9t@WK1-KVsBdd zqeK>&k-4TXj{z&2QWRLKhe;oy0MEIgPpT2=v{Euafbp`*(NlzrwN5Ckks&BA7qY7p zKIqFYga~H5o_@2R^-Mv~^jSG_G=gS&2MyT$zlx`q8WM ze5QCTb!(#w+_ZWWobbw^N^6#~7fx8JJw0b}{VI}@e^_cOzBt(1awRr>iDwU}(aDsY z7}|eNBL&6oyW$83xY8RFz44CD2_!MM@*9e`AD2>cAK8Prc(n)s)h+;3FNr!{lF?YW z&ZWClBiIUl{`LDwb6h-Q`u$LYdiP@j_URvW&dwVSDy`muFCo-@$V1|Hqz?D7ToHsr+eo~cJx?mZH52R z<{+u>8`>iQObiw-FaS#H6zO6VU~l8Q4lN(TR~}ba<+l6x8H;8 zQ&AwDZ306zA0J@`U>VDsc)aFBJ1z*ZewL%7k;zL*;w6B=U=xHPP0kKqvh1x2!awrfJVWP$}>xY^7OP~a*jM?f^O zzh~Y6>IqfGE(8ya8A%<$JNlhk2@CD-Ofn7`@2X}ARsq+erKdAAY3exBk3J0WVdWTvvX&`Kp1c*{){T@w9O7fiZ9#yVi z5j8Sa6GAJyvXGidNV zd~ETg0|LzPoEC)#l0lFUhvd1)m=$NCGxL|U5cfu{+4c=zb%*Yo-tCvcKzsHwH+;b3 zN`{069bcFjSgRuMJ#>T4YGUC6j0P9Jun9RTwg21eFSdU6B!2599A4MX$iuYgnj1zB z)mbjaT`FYRC7t80ej5%qyMC^xPJ9@W4D9gzD#_+SV6ppdc`exLtZaMRV1|^4Blujc z)-Et#V9b#bR7238pa6+o7S%*C^^fxv#dKkWM8aGZL>}4TPpaYJC~@ipuhG#NJuR5z zJUJiizH%OtQ<7L;1#ZR~Wgl+w->B*Pc1Fem;%ODY)b?gt(2Eg(aW?3XjSQ&S!1x)FB zMPV!iZ+p1IR0Oar=XTe?Gq`gm8S`sdkpU{k)0%uH5G!bg+|$1`l*AaWMPR~$JJ@dM z&w4I@?HNZk*^0)Dh`R0nL3fL1-JS)%l$T;nz|*;fHrJ?}Aoua5|y#7wL9;@*^rWx<+O{7;%@e?JLAU zy*~%WDdt{mvKBchW1Dj*eR~hz%6-wHK-Fbjho2WZsIms#l z5NH;csRJsX`RMUSd^?NFZAe zOt&5wn5~l9zs_^c+-Tt5$qp8)sozLTl5r}eg@6TjSOHuIS04j`_ z%9l8kQ&Mr^tF@Occ#GnaRZFvULPJ`Y4>%l>5MTzt&^l1l!uK#ke89amJ;#C{0Eut$j>`qF8FWVa<(sTAK6Gm2*q8~?(fo3go#45tkLo?4 za3{ry#!3E!V_WX)X*ciWfjLg48OFhv8}Bg|J}Y`#xh;&Nn}(C0117f^;XW5qBPV9# z?hoNYh{na%o9ye2W{cBD(Oe1zdF`s+uOr{wKVM>l3VgDE+Cq9~)_K1P*2zJ)`>$45f!ek58zdUBA6o{LyQRD0LP6=I`O-Cj8 z@nh~TLvp;S*t79I@xNJsmR&rlN$cm>W86C*IwH8u(vY=1aF$+%I#`8AKsIt1MgQoJ zd$xy6_aGgw98dc7u*os5nUtl>?IXN_3K>m^mv@p&ClKF;NFE$#?<3!`QIqKR<>8;o0SZEhe1lQ6L@~_g}Qp zcbq3-QR77|ZVGH8v$*D&Xbto5lEkW5{(0gs&ooZu{=rvqY(zGKF|nV>Tt0XMUx20JVbjy34jcFXOu5Ncr}b{`@8Fkq zU^??(T|2G|(o5jzL)@`s)EM^YMpOtzg^r2hrnbZ%s*}{``rXFqGj=}uZ!K{m_?fnZ z#0xpVvCTPllGYkEgSKOQm88p8jMU1AQj|!8rj$s~XVWI<;LI1<5)aeprskdc2_B7_DcXxq61lrONtot_sW7Z2&k}veIz;mHLCIsF9#rzyyYhIK$I4cEVnOpW^vt?(I_cg&hx*r?{8 zdf>lM`!p#7Od~zNIY|>?z^VE%ElnYB^@jTOl%Ja8Ly~RW&%Y&UI1-Yn9Klgjalbq+ zo0Dkaq#JIfLG?M!(VbAau7VMTbLRqmb1ckzb=1XsVkzIS*A3^BTaYdzb}*pv2Jc4u7oCQnrVEssk?v zKg=nE@Am6{IB)vSXCkRCU1J?k;&C-v|0Wje)Y51#KPc#0PdfGh`UX9%|K0kB zsotO>tvVPjzqb`A(Ho`XG{7Dx{YH6Hv*T_FO`cw$~-?wc@>TqYz?24#(wR6~wD2FS=}S+}uQQWo^U-q5mmx=SDU~+5#Z6$SF@olZRDpq` zH-?r3zJSUwbUy2?OuUWb@$h>}@gN1yDj5yIrSH5Md~5-~ViQ^{ zciz37FhH8D50y1*O`O>tT0hs**7<`G@BObOuEc>i7t04L9~11KT9otvrqrV`A}y~~ zx=e^l#gm9@Y*v`7JpD3{UJ^6i88cxUCt`0`bfnF2@{bU*uya4c+Z0A$Q^o=Mx>rwT zO04VCu8qme(TjP9buv~{Qxqo>VV`&j4n967BP4`~|5rd;^D*hW4xh%PRunvh537qk zewi}USz#>|L{jCTtO2t!R-Ypfj0j{1@j(wOJOI?)zh3rJ+#(Kovh&n;yn@)_vsmS|9w{+118R1KzUeBa0TzU3F%MMk6lw`=EI zW*CGh3YLLT-3j=t!$YdU-fV`=2NPzFXqjWq=?iA$H#NS)P37}=ZOn9YJCHmWo zG1++t-fJ%f*`AzdV`zTvpg$xd1ANQ;g+DXk!^JgWN>fhb!dMovjwzgsvfUVcw<_vQ zF&zK@vF(X2Y>sZI`M}fFvy#)#*cY4|a|73eLXAsv@tYh;XV+lqEM&# z4?TY1K~4PY$qI^v!SM~A;IAYfvth!Y?TQLcmzNZ-GKzn!p4#&E0w3-hL`LevX~eY|fu7tSs6r-ypuF3c5uy7yXiJ4H_1qLK89kuEcOghF z>T-Yf{h0{&IIuy_#^U1xzCDyPXa5YgS|s>}x6WS8BxRxHc>VmJk@KUQFeN?FCDoFA zvT^;UI?`vyDsr!sS&)bq;?BP(uLt>K=i$1iN#al$qY=K6Kh$2`)kC~njQY}4nzRlq zQDMr*zzUvc@PE^Dm+1QX0H*As9}%jOK3u%q>rwr0RW-zpoX-)TfVRryPX*on{u}v+ z2gn^8^?t`5^er7+=tS!-*3SpKimsj4(xRuw^;EFRc~f}6Y5F~44jQ8x9c8S}p3xKT zwSHq)NY+!0^QB|{CtSO_F{3-t7%SW~t!_dXJ7c34*6Q>$eveQsbbu3BlkuaiUgf9z z=G^vg>ll2W&C}3qTrOpO3nvk^F27FN06f_cDh_`|)7IR5x!g+#aa$)p zcqw^<<2FkoVl3W8^6C)p@pqvJ2p3YHInf{L`=hTwkoe!dB6)xcM?d&f6dCcIjxP|X zykTB}PK5TyrrJx=OhEEU;H{_2Ref^Tz-W@{gI7lw#*oB$4+8C>gk|zJ2B_#hq%n-wrK^P zG^~9Y@&Nk>p+Jj{)vr2LpparAAuC{}p2Fc4eRoX8%Ug(-3D9r4Z zj>5OuPjF9^AjT*kdQKaw(er9i7K19ff5H%u(7?Q9TLSB}H%>N5?qQDeHp34x&8wb4 zfsW#PFI~uSQu{a20gr4+(x%U1k_ig8joS>pckRl1HcVd?sNSD>AgVKJu!~Y}+oeZ? zP!w?~M^`Ep$RJ+U42)O*;JT@veV`+Zj41>BZt5uTo8q{$KWw47{KzPeY*vD{52@Zz zab}rvc8g94o@#o!c_Amq`Z~F=wnPJAO?ZO6bS6*Gqs&6Bk~-X4!SwRiL-t#$HA9n3 zNxWjxm)5KVWcfl>z{GwTALD$-h@oBiXIz~D6BZ}ko>FQASdGDS z4KxDUtVQ#V4HE+nGP3QhNxRTmBOoy&EW7r%@c%1EbplUu%i!E?R1}LBZDbO3F**sES`cy0u8)+c( z({EY4(_5v4;@GOS!_pO{J%GPzCOy!qpfSI&rz`{OR61=F6I^O^8lUDqst2Yg)b6Z% z+uGxp@Q`p^Rmlp4)`piyc8(dbvR|yyDh;(o29V4?(GQ8 zkvDipCv`ybuU%zj8%%))Yxl&AD=`6g2?QyES!P0cqRtLBg9-Uf%B#*R!Xp@3*; zn4RaxzXf72CW0gxRkg6xmrp>!UQ-0J-bB7{kwXey>7tGXT%PN{2B41qJ9k1+XK3LN2@<(B&O`# z21BDjES_8+`_INGgLou%8mNx^Dl?oU&b4he>-OiEvVkwqq*0~qPzA<>r7mU^F6}pT z3p_BJq2h5L(!JWdW-SxTfR7Pu2-Jbr@bR|_6XLIci&_70;Kkcd=GFk%;*Kw4Q{%pZ z*||R~t`}b4_s!cpayjvs`o|t)si)5C1C!LoCyTVo?lLnPpQj+Of_(urzv>kxrlJ3q zw=w>I%iEaGlVsOpxGW-Im~!Y*mKWFcelx+XeXn`9lCW>%Ga$X_RT))V2i~Sn z>g*IJ>142M=HsHp1`87CV4@F-e|^*3lZ+rmPmoj_^$E<{laAka7XleQDHyi4qUr6B zr#$H|NIO~b@(IK_BxJyhRv74S4LmxgF`mj+mvDrL;G<8<It`q~4d5uNHI_vGE0o7p=Tix(_QV<98c+&@o> zC3{(fDEVp(EmKp9&!|TN$tfk&J_$1YZG3(shsTIJJuIdsx#`CFR8y?L;am@yVjW^f zu!D_%v?_BN319#+WJNWF$@1IVE2Jqg#i1_sby53|b?zXyI-6R*G!pBoFpkrsy+WL( zBAZwjBnygYy*ByAahvclux96lN~?oD%-$9FsS)5$q3u#~rSITwOU74%iS)5{L*cXj zlg|v0hFi&)`fPDw!yMUqobC^3^wsUMZogo0;jDM{}aSRm~ z>$?~oZ{YsWW*1^*|1-|j)LhKZ#IFvFQy`&P9);qe(ISDBCyY$PQ{cCg*N+JzbH-RF9 zj}@VXirtsU&?)%~JPKuEL|`>#8B8J3w+$2$yZpjZoYa=6cqfX0(DOntaVEWRWdnWa-7R>@grZJDAmoA*q^($RZ?=@uvPyD5xil? zZ@CbVs?g$M=D`x*|g{CRheCR+-(~9n1_8L^S_y*Qish#o*Dg3et5iLM0W94HU zAJqg&y)pC&xsE)er|QAR#*^6f3#qt2_m6&o8QOG5f@Lss9iM7~+TUM~o=N@}X9S4) zx2M3woqo1_5kibRr;>fINamZWORO;*ICs-*wJaUX51!F?d_u_P*ty7Veq-#-WyB09 zvR_>s;8s+_ZpEo4phwlTTCb6Al$L%tuP(M&@wGmPl0TMyY^*_3U4rD(ga1S-P`Fp+ zslRMRcDko%_X2DM(ee|iZ|_EczxA|c@Un;awP(L%ybZZDf-22NTSdD%OZgB4c>foX zaBqWECw18`H|&Pckp-&W?PtF>_~Q$3sJsA({Unr2roidXf7^~EKtVH*d@Be?*3D+Z>2_`$;4!9%-h2iNe z7Q*s1j?Q;P^wna{KuZ#-ukDM)eg6yvtG6 z*9gAF@2QTK2z=`-OoK$w%ov4kjoZBW13FH-+F7(wvXv_FN z9eBBKR^v4mUF(ex7RQ2}tqh|g_xb@r0*e!QvZ#y@a=h>J@&uUDAEZrKgSD* zNv)?z_Cbv?sHpvFT$}V<6%A_GSDwWgJYOa|K=hg(7f-jJ zg`^C+wX68@D*gi=e4L{PhR@&axO_;fnRO|weztOmLGyAy6Cc=%yE^VH?(3VK>{75! zHx|>IvU6yw$6k3T{oatG5XYO9hj41JOK4ziea`Q*Nq@6;Xp?a>5 zE*wsQD6%ka!M~3OJ>cvjV_c3QN8F8ra9i%i9f$kv`dxc|APIvFe;}XleS@Evc~|Aa z!?60^lq@_u#?r$&Xr%I&|AP~3L#@!z}br@^Y>$e)l zvAOv@xKZmtze1M{;a?@%lS-{Dy*!(VwM)9^uzjp|*6WTPy3%06dbf9QXK5On9Ps&t zi5W-6ZyJNRghjE*>gIlg&<@jx5G~cw6eYqi^gVSiuz*%d(?kB>O&f;K70R0_U~X;g-7 zRkn`)$X{`SG}2pFUrSm;`uq-8JihHk7ZRQ**iP+u@PckQ-sM8LVCQS&m+t2+lmv2p z-PrrTj|?2|%L&uXMxRGyk|!E^t4HhIfMp+eGKM6SxU{f?&iYioc%a^6<&ZfK|%5!I(vd=!>C+3E+B3zc*oGb+|8~_!5E2Zq)-itRT zRB)b++3UKItlgR&9P{3?p1##j$d59EJ+G~xn&C$*T#xpB<8|5hp4VX_yiv1<3=dW4 zM54Z{R2h4tYETvP(6!{tS?9-j^WK&-ENSh{jx-mNs0S=TlW|L5RZlaSWOgcT@K~&a z=SsdN{|a1Qu)4P+e$3nM{(W3@J50ym>)q|><(tWx2c3P(SstF)ubGFpn7p4U1CbY7 z^fH!yjPK0FWW8(}z`c84?;udN&|YBYXY@pJUanKjz+PcZ^36kTHl@^bpNYk>j77}F zmnz2Hs*>4fw}e$PIRwG%Q^ zu}UzH^49!=$9s)-)aGLqC))JPv`t?e;=7ov&9efs^Q%_{7-t%%j`Na~1n6m%*3iF; zi)-MZ{`YoWL~m=_(%*8A<>aE&W7CRCEADd?yP$*povdBG=Z^1uPZWaQIc|+&@c+3_ z=?N^9dIKd=wEQ)a8$xmJ3mt^qR63OG$IVB~elg$)Of^NujXzvW(?(vNtbbvR>TWCc zkiok=;lOrSiJ}E}M?4oxx&1=7>#;vCv&UEcx26RSHCU*CoG9{cG3P4v#vHUIwW&l< zj)Ras70@`_e+pEjUspMeJgO%0IQ4tfhgUmp-%);`V4}BK($5Uwfv#*o zCvEAehZ2|zKO#gQHbO1XpP*7bD=aR`-=cV)6|yg{8`gJZ@z7+>JDbMLc#Lf4W5L(P zZq;Uue4)UxRgXv&?MzE(7tnLf{7wwbnZjFp1Rb+Hs2SuR^1Bua6)^AzVXox0$6R z;-=eZ?yeV#$wn-&5S`%=Dxc{;=fB;+D&&u|w$@%oj1 z|2s(00o#q~>T`=lBmUJ*Z+vDjxxTVtUy)yVzYn25?-23JGN!6h+O4CsgonJm5(lpinz2o}KBw2Ci zLd|PR+az#+y{auQ0ml_LZ(c_DtGl)F7?(n$KeG=4OVnh_*l;f6P^9itfl@!=Hs7kt zVUcp|9Dgo&>N~RtF%s+xqlxB&2Uf*Fi4eG&V2|Chax_j%FIqEczJ+xeb8ZC6krSa( z*y`ATLUgRALmlI_X%FI|a=zwIX9DgQ(VpMeK8-KcycEE>`|=4VI|y1Ivncq-f&qTP zf>x0J`ZF4CWO#N&Am0{_$_v{5={^@*b*WFHuh67^w3ZpuQSe|&cKJFU6WeWV-wC?5 z7vsX=WQVuVYpgw@A}G4d4woR)W?r^PY78y{XMZ6wGw00j+$XOWZ}@RW<7a#7uUs0l z_otf@6=oin?i%k8W>7kQf+Ln^t@Of;L<;3x4xMMLR(@X}y85L?YJMWlM`ygF!co{t zXjwJKRSzs4#!oMBc<1ZD(rvl-dv00e2YHL1fY)`7SouoD;#+BvCO@L8sUOSbsH3~` z9b`)jWhB1$i12I$s6)$Jrb>n~h8C4mxSOShp2f?H(MjK31`|UvL$CIF@M`_$1eMJa z)ptv11$Ga1wgpm$m1xZh5^vh(k8wJ@U8}5J=~%ATFS{)*8;HuyRI{kyFIaeeek#37 zOj8tE;wi{pdHZ^t{{!t)`Mu9!7T#VcDeO7aLO!fWC1N9LZU)eWDQB=xc%@vi-0zRd z947-VE4=L;p^O{uHikSdzHu@x+q26AJX|mv88=-JZF53hg9RUVb45&z42}|B1Fcda z4beVxkQ@g}?(sb+%maVnjrz>^ytl<-RZ}~S(FvQ9Et$Jz$eeo3wMRXmuPJjvxR`F|Fy4MWf zB#Bwp-Q7L8%}hDDCgpNstPn&vCw4b z>vWMTj;o#C!-G;;+Uu}nMZ`yFi|V>!2SI!Swx2%?9o`%o>Fq03d`AZ5D9e`LU7z!&L8PcE>24^~_jn4r{s@Qn+-(9+hECvI)@Z`Z2ro)AZqn*Izoc*D%ff#w_q z3B+B+5;GU$o>iRAjl=de-AcS}6ME-8PVsl&wtp&8`YKN zp0>dkN(3%cu)N!}WAX~b+)$u-;0%VG3s3E-yMaCT_DantiD@(hazX>-;&{hrJmm@e z%fv2vOJdNEpBTkLxaq75{&}NlQavwTBjdr_k7-DCLRylySSwx;ec|N=j}7%4#fR<{ z!|$8g4=zhB%!0@tZcY`BaYn3kJ6`q#HsiR+X+MA0(O4QOoZ0f>{^AfN_3n;!tlN;D za-{K4bg@7~ZOm1Ly$PJ0+}b5Cw}?-uQG>(L*_j^V&2Oh_%YimjdGx#1a*ElD=`@+S z2rS14blr#9*EdVkAIihGS3Gg+5>2%$y@)fe=E>&jwF5ft%-PBL*w@sw2{vJAH|2%N zU^Rpvx^Nwng7aB6I6}&So37J4({Sv3hkP>SNU$z;`cAHAPBG57qA$=42Elv_R9vcs zudhj)k8?H3b4`5HeV_p!m$-)NSTH=OJMjngpxf?$Vy)5|3Fn*j?oN0!6()`Un+34{ zkgdLpWPJ9C7R87tnFv?gui?S_^DHw98eYnW;XorLG}onMaEQs2aIf|Jj7?!{>a&;v zzHztg75Fi&<0xw~d?WQdn40;sPQQjZA~kEQVG-!NmEfcK%{k!_g#2FUaFO?F`00@ROALcz&b~oQl{@lqpj6=GeYcTO zN-BFzEwYQme=8=0K$I@k2{XGFNGJ*7S2%ipYG^xmY5!=W1s9|SqsY`a7^1`$ z(rq||5HvzYWxHnc#17+*n;~6q& zY#IAS>(lS+=Eg>=nMgg()aS&ig>JH6K%0`~pF3PQpY4^|A@(bRBEUC#7~P0$2N`VQ?5H=%VRK z%PCccdgveg-s2>_nQ@?6_Il0M{3F~a5wrjM?~xX!Ul(0pwNs|s^j?t8wn?qS-|Hin z+Z5`ZIq&|1wo*<~0+H-I0XpKfYTNG@ZggV!aIi2~M~2alXBc3_BFb{^^=Ah

1*z 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%0AjRwVb6ikU`feJcGZ?0 z8w*tYc7yjl=zoSbbd`Vnu%i0!4(PrhN}cv{T1N}0jt2VrByT| zfS4x0?P}r$1ND=yzqxF!nMb9L;=F3{XLc`Ku+p(c@ODAuu*)m{B!C&lck9zBKl zs6cDJ=*2Er07Tf2u7!L51yTlK6gZ4-%vbgbiwKonCZ8u|ea|V8>*Fn+GNW0qV5ytC zn8Hdml0Zq>U)O5Beo_~*c4AUa2j(i`(|z@Y@2st?+8*~QW!X^-W>VHV7>4R_L(=V} z0o?r2l0bq0r+2_O(T7(F76g&0`KE4Ig(D7=enM;oSh(XnEUE2*2s41hQ>=!Y+&Ytp z6IAn=h_T-%Cph)Ho(J!i#=J{Yi~^v3(q-r^_m z^#WAB6h>NpYZjl3jv7DLeX5$-+POCQ0o1CcTzDy@FOgi`#0@@Q_=q(6HOR#txY!9N z^g01TKy4nuA@lP+pFB@G=oNANj6ZYU0-44)@58y+Nhy3+$E0TT0SYoo1l;oqCnD=< z#KV#vm6nmssNBa(ktmnHUn}sc`L6Ty@@}-@K!~84UDwS3`q?9G9cEdfH>Zj<6>91Lbi}%-O1+F$ z?|h!us>*Nae9n4gt@Zido2wdwSfcN=PSC{4@>A|9ZTd*>htv+g|B3d%KZo&g@%C75 z9zx7Gc_TVgQzx@s!%;ZA3d=XhqI|Ox`Mgxi3a8>KPKJPuy|XYJ<#?RTVjq&en zD_tJ_A&ykcW#WF|r5t)o`rgmL2@YSiNcsytpd0Z2>+aVsW5LDu6>K4hUKf<~Y86n> z-hmy1q`V{pbgv;-%ZolNLR*A??fBacf)l;7jm#nSAv?LBJ2NZ&lI{L_q`Hb(>zdcY zkshTVWw7a^-eeafQdJv~t*#@(vv?ty!}2dmaDph&9nJJL+r3f>@UW^n#cKjnhiZVf zUPDcVFLNbT4&h$?2THEZ z<%BJZYoc7{?4*Vu9%2}#J_4z)G2>m&?;mr$z?+mskz`{hM?R8U$m1g{=SGKIf^<

HSIy48_!A4JUm>cwO3bs7-!helHC`qriu;+(fcme0=Ywws8T0_Wnq2>B7m}&uSE^? z?wt~%po5=JvLiYY#|FUZSYVnB+{pxKL+jkzb&^muf*mTZ=wd`Nb-xrGB`%Whgzz(H z1u$ypm9Oh|z2>2pk;0}|^r-20;JXV6zYH?uWwxTj zIiW<$dG8KsY_+}M_|BPjRuG(dQ-!lg#5X>GZ4>WfTJ+1&|p>#O^D@&;`$N zo`*3Wi!rc3xc~vsA-G{?3CVxaP%9dc18q;_r~0iZP|FB^cgG2+$)s?5Spq8h`(J`4 zO2vQy$o31@PbT82KuJ4~JtK1oppErE_Wmu(x+fsG1j+*Zyuu}4L=(v)CtUu6VKwK~ z80IVri(a-6_xkGknncMiJ*f-yjRoRNZhrTT|B{9x%o7TEi265l3cb3@ps%vikC7V! zMvvCzS|6TtWRG64JJKJqxPlsDx@DyE$zgq(kmE~n_GbM$# zQblh*+kHhrKDN{K9Kh@^7p-~8f(L@FC_04$F6Zmh|K_+adY>>ZIuh$UBFL&$(=e=ng$DQO=)19z6`BIVjS)^;b zs}6CmW%9m>%)StPtQ)Z+NG)`7)-iP{AVkr5wbxMW1{Z`G}EwjfU60El+4Z?In%Fm6u%`_PiUiHupuAxH}Zd0N~+( z5XD8s2LHPjs4<|>c!hoa`xOX9fp|Z;45E-%Svu{=J@T6Zb#Hv^W|uK{-Uev1>IUsr zUkpD$|3n&-JqK0bV4+rsS>*1dSJU;NZy0EBaBc`viTy&_;l3@psu&-I83?~a0!ngX z<-93fvp~KbQ0PccNJ}COjRAC@Yf*IoXvC4rE?$rNS#!QQ@Ele`E4F;%D99Su!A!9y zsQMiW3xfSzx3>yiO7iC6H#=W2#KLGI1pM<5iOcE{87zdU1K?phZ@a*&7{L@%RClPFfog@^-X@S=LLk{PU}y=1b3>_f2>_(bw)y}yyw{_)syy=5kS zofd0kuEExVB?n-PBwv1ed=*4Dub;eTuSp3aguC5|fQb$g)g`vKJyDNE&vcrZ4y3jv z(+@o2)dKs%*d_3Gb-~w<%5Vq;+OYT_R_RPfzvD4W%W_K~{zr?M)0!M}o$>-bNRp#s z);xlB?z0j_$x-vq4>ZKDL>TQqZ%)YRbIC8N1B<0GSx90@&;+>g&5n`Qg)3u_Wpycs zk0`sW{&lor?D`KQL?|gIML)JaKlbBslxxTh!cE2YSFM5)ql!Y_$EZG7v3~yK;9Thq zAR& z1$D33uQLkjb-fnpaZ%UYf4KhiP%FLn@hjHpZ$zzaZEbE;u@Jl!K7?2;)dwOwxiyO6 z=tBN8sN`-j#|Ts6=V7}a_g*eE>oQaHWBpwnEenV<;rE<>rzL|Lg&=^~Qi%TFCqmTk zLE-g*=yGv3?-c8)vxGRie=6>#jjI~~)DLL=dT*vA7nDUFiAN1q&egt3%Im%ns=6># zIRdEPk378Xz1?RCm7X)eUt3pnsi&kbf!FHWMx+lF=lgo2RLWrDnd41D7{^ON@#)hV zhV9ZYetvV@{9`FT@i@LshFeczNwvd|LAEq9N@ofcs@Yd?oVP-gooT8Kz{h`!n4K^+ zHvw6YKi0C1l2QzC*F=TOyV@1q=*fkg<$}QN@4O#G*=Y9_p^YZXh2rB&cFU_y@d5k4 zm@x_UH9iu$3!7w1zfeprfCy|O7(BOoxOna5lcz!KDVVZ)B4T>Icjx>+1`d2HeI_fN z5?Zt@jy^K5m&Vfv=DPyGw_+WIrsREJDvbNFeCFZIw){~wh>&{XJfByGFglNJ_T`Hf z=c!xe!ill-!XEr8V#fs?fLlIbXWe>^9@y0)Wb6v`xd{t&{IkbpLf% zYiQn%;0XV-xvkAHIm0#}+R+;ql3 z2|ZxhJKqVIRHC9f!vC+BxeXpt$N7?5ZxQ(o>SNo{NTnoWGTm4)f!b6uJ5y!R=+rc- zpok97-RV9)sOOp1;++nIGRPr5S7=x%*sR5 zo$Hsmpw$%R$=#88<=y;V{#4({2EDzdfZ66O#ZJog6c zP}(3PSb9IFL<9r`6^uOgIbm#QdAv4YHwm`pcUy!sReiqE5a({xgWpv>{?nDWSvTM) z=mWret(+6*R#UpLGYdj9!}QOO6a(MD)x1{&arf8j6GNn)nrFY7)@t&{5@BF}Id#2i zZ9FPEVj5^9-M}q$e7wtTzTcZeMyfti%F(}^d|Mj>ES8V4HXL#YV;U#Ze=SG3=;+1z5K2q4d+??n*bdMxJPnhzcH^E-wBtPvaAuq#qGN_nn!_RF(UpUK? z{L7`00*~wSMUx3{P+XBHXafR+o!w5t+$0Rz7{L=M&IXlYJ|~q}cvOM+HLZH@1^R5R z$2URPT=8HxLZWe>9gUPK(6Vw4vg#VRIVgYYZ1gz%*^8?XiB3c8bdtZjbAlhwL~%Bs zF8O@5Y*wl$nO&EFdBm}yq40uP=QMXm9B8-sR5P6P)Y`<>7ov21rJbWx=Wt zgH_qN6ZHIQRXmLV+@N9irHQK~{dOtmBZCKM|F9-JFxkNewiid7mq-dohz)xMc>Y64 zS5knJzc=V*r3g$G^icxZ=bhR>5sLvnEv%UK_pB4P2mx>0-6F6gY1^j1>@6dJk;Y#B zfiL`oXxH#saU^H)sHJAp(XJ!5xZ_z59U^Z283-@@z3Jw11E;<*G^oP8djg>VuD5$s z9msG3SuSZ?krZfd-`NHHN7jXoRCBh@)CIScBxX5e$AS+wgrMSut}YmEvE^;HIG~L7 z&(hT9#M^lL$7SI?SG1~ZHOR11Akz{jMH}-ai*FCa;)>T*<>DKb<e621)&PkeH|4-l8Lmoj!Om zXj647HQ@R<94oi%@Ta#*6T`MR#TRtjW!i`Q5;>P$>hW~0D99aX=vH6Ee(aJ*h22>Wz2lBj8uQ0Le& zXMQ;22WQnf;)*LeDcgM;O0QJalHCy>kd3Qm@y7{n%Tbd@13}EEv)x4TPq)1#*Xo+x zrp6D;0||v@$Btu`d)X&z|Dozc8$G`J z;k+HY+sD<_3*B<3JpN5Z+Nhi*Ghc|TD^EnUCAW#$c}K_1gAKJ08Fg>|U(!>UCyE#~ z;bTbsoI~zR=xT(-Ng1^U5C=7bJF5lY`bO$UgX%JPPK*d;ImS}Osi}Qq5 zO>@G{N3!B=d`gH3;V1IO5(zJm!7?3bq=NWNvfjA?3XR)pD?rK_g+>~y_Rn3P?D zP*H}#JH_-9CpBP)i%yl_zNt{Ago-0OO4iOCIe{Gwq3>u2iE^>yt89aAMb+tn8i5zT1QQO{i6B! z`mv9SnR#h2J==rQ^B-R@%A6&WOf`Fd>>=IrPaf0tWU5>oMYnA|+JHZgdq}Sqz(o8C z@3M&9g?-F=i1|+lhcThaqEX?o1G3Njole;m%!ApY$tJ}Ba_@nk%w!gg24+3d zgnOc4F{<^pIAd>{Lu-UgZ{AeJ5Mjp@Zc(9GT5kERI@h+bo^`VI5lgr!O*HZs3r;FD z7_S;B&5vpyOZ)Hj%eXP5D}7w>2iK0?+*@3boU6eEQHn481>$tJuXK|*sO9MPY-DPb zTcpcLx^`hYna5cBYDeo2Oofk2Pxbge1Fz0vWdo?Q=g(|-io9n9Ki#CFVg&Y&Z{rzC z|Kj@=Fao6dTj!hi&-eu1yGdL0&f!$H(vPHdt!24tCN#xG;xkDSpXOSU3^xWH2xQ|E zp*&nN|DSxlE*4XPMIQ@#*ybC3AYa?!GC`Xh;=yt3u6KNrigRQ8V94Na<#1Bfu$ zaeR6P{WtK55xl(QgidF&F5M)}dVMBx;P zNjc%+wjDDJbLz4OP3GM9`0j%S7D?*#qGr>CFTjK11q*7|P8Q7;j4@nHHGjwJy~g`7 zZy~+^<3nbui7Vd`%k1vUW1QSc(bvo`WB51%$omTJDw4h3p+kneC&-ZZjD+^Deus+& z^U^W9%83{22T4FiI&`4%q9$|P5J2wJCt{u$e6FBn`)=ou*ow=SjpeP>j>W0xt8`1u zRcZIH)^MW)Y~9#g`Wy#r%@!)!ftQ- z+jub0p9CD;bK(e}S4vdu5A<|!T>*i0~(k)1Z)5{ z{cfB9HW(ZJ`F?5n&JwFSQ{s4<*8Pq;V-!02yS^3^36)W6hXx_J{6r1~Zqp`GIc(*% z?{3w5B7N-T%e{G|Zf*)xqdYn(f~eWIJcXNrLxeo4ZqQtWI!`d|1+Aw!>IU?0=ttrU zSM0~wt{l!dsZU(p-V7jx<_2>#^?j0G?>n$eJ=~w>ECI#(KFhuAlWBQC_N2+U;Fx|} zJLx`eYYiOQ*GJl|ggeaoa!8M3L_igR=;c|_zRy_{PkvOZ_Wn1Si8s35S7L3W)iek9 zNeNu8+r`ZNm8LwWwl0F|yf-{ODK59}P!ETbCXidg#IeCEQEU;L(c^ZM7Xz-#x5DE| zkH7j$fq;tIp?VTv-m={c90}5sw59uNMVcc~ZybuW?c~3}XvYpb7)_T>C>n`Y`n2#wS8=}&jEv9U zAzpy`1hAFuzKF*o5fLwK^I{u$;h~B*Vb;~#;$eBpA_M$L8L#{RtK*E)UZCj{?3BPV^*@ak%Fmz_LB&9)ng?*bn6@@uZwh^Bzi0m0S=M&B=(FyPSxqI(QY5IEICF3ypXj(lEm6$Z zF4O=TGmvl-7(sw3Mzui!;hkte90W~bTGM;9^c?xpl-OvteJx`BrulhS&`c;G?{M(_ z2D9L2PD`VBJMH_oWc*#P4DpALA%1Ws>o|S~=OwHAaHC(lNVM>dNrmqc%VGz-LQ6{oIK`HoszLJ)Ki_frxzwdHcIkH;%| zzg)JCsgoM35%Rd5QRS(h4)*8G98)KqwhIOJ$2Ru++~RAz_UI&=gJnIL9Bi9f+j)?E z$H`N}0p5v(UriqNXG+sZOVO){|K6BZ-BF_8;e{t)A5hspD%aeZ^y>Q( zUE7%8W^@|*YTIm@j+*rVUTf)pi)G7-LqTR|1d){`EOTOr@nE4&+{7U-X@mdaBUD9*9~XX)k(HiQx=D<2y{jV&WBIklJ|j>(?}jc)d;^()QZ*`63PW<= z#zmlgY+5p`CU}uaoInXUygU&HrAvw>xGB3nu}a;w01iyD49nix7)Q zaVWhZcMT!|MK6|aIYf@T(T>w26g^JgZq*l@1Or;AOZ1AjSyKdqj)03+HyUGL3Isz+wjMVCEF@`Wx%ZOhr>t7(BytEH5^xu z?EZ!aHzxm;fCgm1D(2$%R`|VACaZsx(bppRaW=Z9t72*1=i8|* z(Y1aYvzv#CD)L6764er>9HzEhdc$IT)dd4#&1evHdlo;qtn8%29pdMFjG@d{MCfwt zhhhWfTTCVX&WO;bWH=5zN_VNmlem%&-Dw}Zde@>(bae+pj5E@Nu?^sgK$qs=)#hl<)T`80;(nZDxKfp2jm1dfLmNo7{a50KH5)X&!E0f%4vJ! zdNVc66=1ba2@)X)@$R?apA%(!ZZAtv%lVoSX_Wg_Vq>OKN=`AFN%`qiV}Tu41a}Mj zD7>2w7E~}1F1hbo^2YtxJURE-Uq)e;eQ_Jxbn8QQKK_=$A&8t+?46Lqey$dHC_^q4Ri7z|?ysulZWb+&=w z64&LWY|xPAnQ=Qabu%^k?<3iu59tzKsMGSv-NqIt?rNpIM%UgQAuSM&7vH`+JUY^& zs;v#~mKlt7B_;?ep3eiLJfNR+BmmW$C0DdAgY6V6W)b?ZnmjbMh9~A%_bXfzAf#E2 zGv(70HR7#nX@9|2y5_Wh+o+(Yv?8E>3@ZL1hs)@!WUH({iOc{s{a9&A?kUy8=wWY< zPT$g1Jq(%)S_4_!-R_<4%xg`>e*dN0Rloj3s<_AQ(l8F!V)!W^NGT zL;`GmR}la5K8bi-x(guyUc*ta@Ba5j!K;O7T)b2%@KPP>bY{WNj$n=sd-hdsOZN@2 zny%|rZ{%dzF7|Zfkgx|ZEK4f!tX*OQEzrD*QFeoLlkJ0vhdVa^=B~4|`M{%I)G-fd z^{S&G+7&EgQr|ZyW#7iqh=W`!Y`HWy2XNLFr{1Aue&lP#U3epyFCQ$z@LpR{;yk`K z!Z0e$M5QWke=t`ek>GD;}L_F>5FGbRc^**41KdSLS zz3*8jfFjBU`o+~8F!f=)o)tUf`BYw!X8{UeFBcSn0^XBs4-IhGoY;44lHe!mlFSPDbXc%&Jcry^b9ZA!_Y{+*-NSVj1 zg&f)KOcG>?SQiYOMH;t0R@(lM%s2gdbbIt@2>mCk!%ljJm;uozKN4zmP!f1G0~8|k z;ejGDdv*H&b=)_k^+5f={C;S3W~Wii=EKwO^(hYpC)b7ls|_L+S(K*d1MB~=smX;E zjSs#7EY1rd+7-a<8Y{_pq}{Qpx!m)ChFOHZYi61A+$r4;=Py}e4kGS{UAYJ$4H_$K zOrqibawAE11{VFnuX@iE>>KX#nnbv$VobnMR@pxP%T{K!iM;pBKxOuGYQtEGYU8A} zK*rld_4fjpAJ`2BJ|KVxH;O0pYLs1NJtsJGwZ=`Qu~{=*w;s1y?EJK=y-r1W`)6&( z_D*Y!#7_78@|Wcdpw#XRe6UQ7ICiV%APFii@Mk)vStoy3=ERV$6V>L0@C}>>^SM}7 zl7ONd=timh^hpzAhq@@aprqsbpY-qZaZ>~6!3ihIlD(RW;3#|Us0p{b_vKsPS#L*n zPx&O?+`w)Fm>!a+uLiC7!C6KWWOAUbu1H zcD0hN)aK(vt_Z>Aa%fG(@vHAzRmU^^pmA3?s`J6~)nEQt&&_P~-trb2zrNZE1qfdm znrP?6lBaE^bN9E)VwHPW7OvYZPJfljbxXI6s}S%<_RGBC~N;|8NzPCg?e68$eCn$gae{jU)bnZ8mK%=9T^j6?Ax0MGV2f z4n|t=%xBUyu2tHJnOB)QG8&86$uuc9hBaPG@0=wX&JhKyE^-$Hv02DA!5yd$i8Lp- zT@=U!6S?0eX*qf?n*R*M-kFHvH!;bsvdr?|cDc@&I^-rv{5Gmy%nuGmG>EGH9y(Ua zZzJ=F&w6j3 z^1|#gk14p^6^k67^eUBqxCQ9lG#<{$!ZsXC>=C@5JMa(whmlV3xdF;y`GGo@c=`jSFW1_1(@#Bn-?|9A8+8qczF1M|C|ACK->}ijfhqS zTl@pcTInrkBHpI2y}=dsBvWhA{;~C9zHp18lP(v=N|#uxGxl2rQTP-!R@QB>Keu7U zupr|8>VGWbOG6;k!3iH-;o{$Z@HY+uA0lKb%Ub{fOFY4`>k?$nO^YMmcIx2_zeBYn$sVNC6EblA%+v>eXeY&IQ6QQ_1l047qL&x)k3 zSQq-jaj;g{!3Ct*q=eO0Lz>6|DhbD!h{Tn6Mz^juq9Ey-l!st$=qJtoiJHPs2;cE| zSr3+HA9~5S_ENztETx0Mk3{+T$L-xNk@`PaGj^-DUM>q(d+5hmyBiy;iZz;-j|7uw ziGJ>Gyp|ADdf#v>(8OfwB(RU`rCDZu=fuFf&ZDEC`tvy_fC)IWx=$C|KsAsV^1C$` zk)hjRwzJB7v#-W7FCv%PXnA8;yg=Tr0Dk&?yMsTLtB1^kg5Zehjl}&Rrn500QdqeRO@8^rwa-@@OdcXysJrl)OJppVoTG!xpba z$$7a>B<`^r(dlP1s#RcE`MC5V`HeqiY8-S zuX8p|u)aGs8{h! zQK(uPB8^OXKU;S*Vs~$Gu6JQ%9TRsD@Yihj)kQBE5zsX_eKF+?*_sJ7V11g@7Fw%AOV{Ze$Xz^b3y;fj;ZkaZf>UmQt8v(v_=jVdF)Ncb~C&PKH z3rJ}mg7KoBGwT+fiTbF!43C>T+Y0Aa1oRF)xcJd{>*=#B5-?|CXZ8=ka;UKw9k)aX zCfN^7wyr%si^t>8t#Yzwf1V26Zo{wGrL*ta`ZGQLTE&MP$NS@B^0OcbhUx11b{?y! zT_&C5#F%CM*(TsJ`{s3DDGPY{%wy~`!8n&{^G-2_E!#OZXXhzwk=?|ReBGQ=UkJ_X z8^f^6g!9-Jemnc5#D*2=Emo`K@9Lj5qoGxX0A&=Pm}{C=D%Y9@(;hUE>g@furEzvBd4fae210CwDXn+$p&3_zfKw;#Mvw!?vjcsO2tr6c#yo~XIN3I~ht4{5YKn7&?di_^|r z%LkP8a^LHgfX*V;@QsH(&DaOQBo3MoN8SCd?*&~$8`#O1W-YPrQL-v3HWm?+OX&4t z5xCDZq%FkIxK!u=@Vlth^+gkM#`OkCn)hz|B_d|W8J%Hm&BWW!UZ+B57CCNx z-!`7>QN^SDY%9L~H1zejt#h)(&W;mBEkO#F4jy;xw(P^hVkZ53huiOZ$_RrZ%bYvw zr3H+-JaN`Mr0?*ZMxO|tN&2E6gsVT^uLFKfEz6vf0NaZ^ho z97%a@D|aQ~)LU)6tix60Gqij(e)3gYqwPxNWI(9WOo7V16D3BkSz`_AOjeRKoIb2 zO?3YM$`#Y17r8H<6HhX+GHg zSIGH(Ot*uLWO)%0Pl&)hf*<5dkK_z)gaUgLH4qh()TBlwokua>u%G9N%P8+DD?@LNSS!BBa({#Se1Ho8|Wq2;=Zghviqbr z*8$|PJ$kJQ&!UNEwOkQBRU6DJp2rp+;gb{3tz8`2#Pu5pV@1`CGtLjN9Z8X#^OJ3w z*Rk^4^*1m$u^gg9Q#1rVWZW|5R=8M1-Xzlim z2o#F;^t;x@{@6t(w?y+fGs3N|p#kLY6aO(YRMu&nkW!Ap1_02T5q#B-<_to@>r;G* z23vtUKCvmf9wKsiy{m>@8p@gQ5cUtBT)M-4%qd_i(D-VC5sHDqZ~U$s=%DFovraho zAcV|*ygyu?v1;z=a5MT4r#8MG+i?HxS>0}7`>_&0Q~##aK&d?TX`)V}eXbuKE2L5`0cN)h@|j)l|7+{&gbd|IS#^v_xok@X)H+yQ@_1SdyL>E%&<5?T zLkK_4U1Z?EqygRU+%MS7kl=LVJxUv<_0)2k2`~vn<+;sIj+?4f9C@6FU~o=8u(Nx@ zq34ho-V>lz_azt%vcp3Cfz&oIR2im^`don{rSUyG&r3a0=5ZUuQ0SP#_NrMA!;>() zC~WSL$p`sTDJdjxz@Xkta~F5HGJP9EqF|j_^{f+ds`j6s)1@fLvyn}qY0jJ=`YW?T z!w|k9E)Cya2O6HiDnE4Vbk?Hh25&Bs2z#pdpr>=@d{?5-sky7c$ScO4(LQsR=4S2i z+G%W0^qNWWhs({fsw7fI)HWSo%8MR8hzSG@Y>$lmvH0Ay==0)P!cL!Od@IgH4tA1; z!k}!xm5a9GdW{0&Elmv_7P{8sPlKj=2DU*me_m89lXZ~8wwiDl|V>zp5FQ{C62RqUPVj#a+V}2v9L0sWT zQ_=z&Xw&3I@4qm;2|pcP!9{y|rfKzeh?3w$N8@8kV`3tpzO=zJvlAK5wF#;4zEH4005Z(yvpYgbyCHidoE+%UM(l1Mi>iK6zN{ zI2I5P!|as^IbFo7&2?yt6FG&-hw&0oCLQdgisHyDe83iew`6xag#YKa6(T(?ZVN-w z$YqD_nqC2Ci+X!wfyA9_9)h+&nJhNO0+^^0O0Y4K8%0Wug}%G} z@O?GDmZ-TH>rGD_>#||NWAj@E&Jxo3CI&Fp$_xHvGmJ81sMc%EdpQmKGSbbEi0K;tx z(^OJ>@~uDv)R90FRH%UyY!i)iQDJb3W3eH0w}8)+y{oZdFT^lBac7j291Pm-WsSVL zZ$eV%5NHFx=~1C&CmAkZEcY@GbI3}hecG?(2fs6un(11+5q~|Ega!2gmk0uxrM=i1 z{J!w5LjwK_lVB8-?{9OI=Gkv<_gvV^j&Rwt%{|r+F+uIth*W8;fDg5Eo|+?4Rltqe*oNV#Idy>DH3;g zNuq)d?-EWW7E+-I4tKbO0|~;eNrNHH6tlFY_8mR(;Y@=nhe>yb{o30zy9sCAa&so= zT;q(v9y?Lcm^*vh4N`j4^!{z^J>J1(THfpcFqVNSRHQ z7xt%x?{#O7(f8_J{sNsGHOEsH^^J*MskFe*{kV}X`2jB~(h7*Y{UNSjCKDWg`9zM4 z$?z@(n>{dBocq*U3+QszP%wV+4&Nx&v7i{W3MLMrj`k6l)x^Gb&xLs@^HXG^lw(~6HhHqp*t6#Qx#0z2}RZ+D3CG2 z;^Rh_ZxNz7YVUU|MwxCnfl9P;9d#&Xq;zdfj(Pco!>CLoIxwmKmob1>z&Mt7G4A}- zl=mS;avZoUx|dq`kpZbe+ukqGLNmn9i3h)iJ3=%JuH2Q)b|_kr{g^f!!`ZQ+w~PX} zxlcOme5e`SzXh6*u-RuZe9i8Q=yY1cZT0t!Q-_!(U4U0hDO@6*Sip)FoLDJ#Mge-1X9*6SiDMdSHVa5V)KI@i@JO?qOlp(?RVPH!DiFB8xTd0@Uvh z4=W87zbx@cF|(8D#$$_{y^zbI!^hv6)DB0V4t(Zwf9uuqPJoT9Uvou>i40dfb_jld zTb)Gx!ERk_YFZjnkQd`ntY597|^ER z1|W~x&f%&yxo?5F2-AOP%JLaOM9`ZbzN+vD%wQq}G}3kCU$yI|#s5&lNG@JF=!_Nf zyX}X>r_|jL0j**mOGm5Bd+2+9JK%nkPk%~nTqyB8%xGRP{aOx)L$WUyEsSDFhbs=4 z=4Q&)HVMdQsJPewADH(IP(p&$0BeYBGYLuX2B05VrjHz6*zD^^_Z+RZRdeGia&3oX z-PBsiO5Nlk3NCh`I$r5nvjM@_QP`L#+e5!PFWrN`0ksd`1I%_hGcARQlIn9DC!Hx4 zNtELp2MD!e@bV(+CGr{jNhlwy(Q*bNOJtC482hy7(mfAzfC;g47CH|H7MKrPJ;7b6 zoia1T=Y}Maw^BUEumm)p6`iYb^3t$^MU^1<{0O+28xBWc%wK;(V0|eL4{d9}sox`# z{qS`D^I_gr@~P|Hhuf)*<`&4&uY0z4S38&so5CuLhO`_+&WRS{X5PUP57$;4m;L&0 z2!VogaIT%vRA`1XOS;1s-o8=SJ94yM)IzzLbQ5iX*CDu}s&B{8W#QM;Jyp99a)Ihp zu1_oQ@E2fajON)K4!E(!l3jiO4Py4HwR*$(MouVl#-3jmmV!DM(~B?jp)Q=%vRL5N z4A9?1O$Axy(%6^!OYQ=d_ExKjQk%lOp(US? zF<*>f{Ds|);PuHyOm;6}l$U4812eidKuoUj}Ty)w=aF3t2E z@-8%=KO{@IK~m%91guZBbqF^$G*n6?*DVYP_&1=0EjlCsL!+ZKtr?0<_Cb@Vjo&GrOHt_Id0Vp zc)M(oZPlG4r_nj>zVJZK<6r)RJGOpYOCri=*a#t(?a}TZ^F(c7F!w;HcoI(qttMNJ zX`kh9<%YI1B5J{5;W-e5n2y^wA1-RY)CZIH7j9br@Pd=?&5HCIoARc%jIHb;RUf1` ziPxdZBe6n@qm#o3qkUsA|J*UR)op@7pN?2JR>^Jug~5-HYfn*k5|70jSQo{Z8hx9Q zevqXs`g6_y!_!rVMYVoix@+i;A*CDXnxVTJq)WP_n;|3xq*DQD4 zQHL#xd{M2))?2Gw@pNu#($-s>O*Ob?zdx?3^?D=sl;6gs>6F5KVLw3srf|3I?p@Qw zXXzs97r?2B;3$eIbiApUzb8x!%IHSX;)NaZNg@iSRy|-i>;T1Z^;1y8l@zG)lX;%Y z%OXZ#QW?(S`i669T^3fWExqJG049k9gWv7g^>2_ox!wq_)V7CW#Eq8g{bI0Wu1q{x zXSohW%M?SRohqIpaV#<8R)3h7Y3UAj5K>ZBOjvPX&pC*Fp4{-3($^JxD!Mh3Jx_*1 zrz?s8n@YzaLkZ!bcpuPU{O}<77vOLVFjVr9%>WM%8AOC6qb!3^bW7~dMHjgzdeukI z#dYh_>U!?vdqau4m6NyrG=A0@;Cmp%M{o3=Av~t!Gs=8ea@`wKm(>Eo+6dE3ao?IxAu-=0E=b=S+6=s`ntd`%iKKh*SJ}`bJfa1Mb zF8|%W5f_oWNL(glG_EQbBj^=pm*?3=C{Q4z+CaJZIMsWlwu5+hXNYwo9$-uW(wj3R zj!fbmyl^9aTA$?Wfc3c@K2oi=lG;X2v++Ve&8d*ue)bp*NqV}&WqB@&@Qlau8=|e# z44duVDwbgn%Po44c(H;;g_xj+Vwi~oPI$5BaY`<?^dygdQKvNQFIO5p3Os!EpwG2vZm6@PV{FjMU4EeI3=)+ZC4PrQ*?+tsjsL!JZ zLPI_^BVlcCQ_yhASK@V5Z#jZ~#EaB8Yc4JxB<~2k3;chuWy(|x%Op)|lLnv_8Hx+O zI63%|1SS*lHP2x2cyhsyR9~8Sl(>6XZ_}Xf89*zSutz+q6GH&t297J;VIvTDp3TT9 zbc(vxEZ)Vaj>ZDu+rr?=W=*3i64+Fa|ev%$%Qd=HDsTY2ZBma&?qNb3iA+L7Xp^#d0tQqJ7}X zlezWQ^#;9Bzs557rO#!x9}``Ca${7FXMOT_7`Wg$h+N|S^!=)Z9PyLWxYBa5&=sXl zvHAv*i9MUI={s=D5^4mh4jW4J5?D@~4he!sTMR=}Vvt2MK!XRNYQRX-x~vL4haOpp7{D`p}l<>#-Lz!8^d|NHJY>xB|MI2N|^om05uebAd=O1^br zoA7Pp2sER>1RbTVjw%rkOnt`Sq}?zN97K`~UPI$r`cp@euq-Y;PmXYEUr5&rshgjw z?V2^{@b!4f?tu-rVKA_*VZ=2)LL?mB)-#&euOFOC-akL?OG)~F?xCg&)I6uPH~x&)O_z@EpM_V8 zEop9U#EtWOiN_xMk~y-r-n0=0zIA#;U9MwMkaY=jMmv9W!^?1y9!0adcE;f#a1rvnH$Zr|+adV_Z@Iv^hDxs-W_=;A+8XX=%|4lhNK>bKG^q{I*#>7;zMB)R574H^16Cr*=R>E}VcrgXFu!F9p zaR%a-f{Cx$gE`PXr4Ls*!b;^q2=7~KuX0H_74u-=z@FH`uR{tSkmP=1Q&Q2D{v26> zT1eN>(iyhn$z?h6(JS%QeW(Wq=1&Ayw7q4Kfl|QNk&I)>gyOkDqy{gi&5~eX{unfP z;0Lrw3E(LkwaT;K(CLn4S`QOM2j-!zFZavXn|>Yq3FkT^_P!O=bQZEww%rg&%5|C? zgJV}leO$d~cF6*hF2<)=pnz-|C8`sCpZy9&G?T9tRtXk=FxYz9!&0}@;`PcW$Vm*H0LRg` zesZg7)#xl8xh<|d$o)#?Zo~Wgrjw~*LL5hGh)dCl zD-i->pk-kbD~840X4g$*mnn>zu@@_h(|qioU%hRm(sJQCAdY3}YYZbp(0+aiUr=pT z`?29rQMeWAOm$IR78}825&bS|*VX38w}r#e4xpuLFyeA%vI6Y??Ia@aRa7 zu|S&%*}AwqZo=rpZI$=ewcxjDSOoA{&gc-rZFMHF z{Uwjqi?Gbu^vzW)Qjk(rj@tO0thjW#)?- z&->Mq=xN*KU+hY2+hMWGUsuQcNMUD1+(|5TCp!;Qjc<(e9X*2=jr4Ca*z3A-!@i1! z`4cpGPU@`75ng)oP6BkL@Hn$Q>#F^H_rt3-+0;u+Pv8D6*4aW$DWe4pj)NaKbkNt* zk*Zw&H(y|Uja24ht8$_)qWMkIc`9tDoimYjVz zRzh1FPu8nnW!{F0O$j%U?oFoaV)3!zNvkl0)rJnLo`pY8z||GIZU!VvUtbptA#yo(v1lUY%>=(;TMA4?!;v0!9|x!O@VMz;v{WUXSp%%Vuj@+td1W7&8C(oI z^K4q}5U<_tnV0Xrf`PY2-i&z^-dJ~wJrWbN?ryK>rps2*5J0(MmjpEOeVwJ&rtzW# z8!k*@p--UBBD2c|EC&CH*}AY4ELBl(+>(MW*Z3-;!o52ufT#NITliV}lVkA%ohnUP zf}yK)YrfwvZw*Er{zy-oC4U%jIzm0@59n#(YkrM{peH5NvH9m`ddz?sA_Rox6D!Td zbN691s*$S}ZaLJZroQIwTuk#mlR<|twcJzSdnbesHy$o;VPKQTar~EW7~!+kRz?`f zMVku*Jego8G^k(C{+Qnv0CYX0qQ##BO4zIVl6Q}5eWwhv13sXG7rQ%LuOw{vtUDl% zs!dJKEO1PX1Y_Bu&aEJl**Xu>e@$VxCM8U=#(w_0mab6SyGTb>P1bI+}5g$HP>cSfv$g2iU5hL2&Voy zLvO?$AxpLp1%HjE0?gW$C_yJd8>oF7)F7x1t-8oR)vuNbffW9wy-K952B|2xW$5`B zVAxd<7rJC4)uixFyT+0iGrx=QA{hpx|LKsR*W8AhS5O$ZQRyH`H$q11{Pmh<17!jG zp48QRG#_^`)DbC&S`+CtzR;JV)-ch)C5PFQA0n>(WOrLdRka+PMLlVeSaA(CqEy_X z;TgN_o#9s^Yr$Q-b6hEMW^lS{hn0tC`MIK;oD2+s`~~DASaU=g+^CQQj7dDgyNR$r z$)YcAG;yzCV3yEcY@XpF2k5B8CudiqviTQ-aA}>y1Mv8hjd5t2NXH|oGg0!Pf9UP$ zt1RKme8qDUtbhJKx9|ri6Kh(}FDG7j9dGyTgNU!E#kX3IW2c|2%t6+%KmHn2?1Bx_5Nuz zM6EyObE2a6kD)(}MT%kOaGdNa=94m;ty-b2(o0$p&;Wt9B|sUuPek6#Vqbi6Krqi2 z&&dcs!&d(EO7YVVVuwq_zsNXs9aivvA~ki;yV2n_BVgqe6H5 z3$YJJBvt(4+y@_a*{sW(CjUM82%|~&3=5pZiB>G{Ml8ZP?c(|B zQ?17$bUoWIxyeU?MTgZk_b_tccbVPSrLI`W=+LA-d;=SiAOd9S=NMM6@H)V>E^ft9 z%%s3MdeW(AN0uqu7onab6auf8gUg5MrEXG(5*%Al!;XE^wstp@R>4*d>0hYWWV-il z)@I2t8^F7F_AwDKF!6g*e#k%cO$B0cpG|NvQox+Wu{W7De^Z5Lz>2GVHkXcM&+Q&l zjfFA^qu%Tpm`-+N`mutOO-#fw031jY$zk#6U;SrxP?mh9V%_P2`q(Kg(<9o9gB~)&|e2jYBt2&1h z4)u~h&mB*7NA2)g(yVz}XmB(1Uj{lktL^Awe9W4q)kGo^BhaJV_o{L6a)i6MK@L_C z#QTy7z`h<%;^tDVdOdwLrL9=NA>r`%oV0pr4Fv~I;I$(aeYOU41v z8BUhInzy3{OsqwBUN*nlTwWElt4PZvXaS`XS#nik%&rJlnn)SsBca1EM55vs} zU)_!v3OIH>;APnVcy*J6+FZOfH9ZbId}>vldW!U80$N`kwH&b%4+rb|Ps(q>bYM%b7TeOGvBAZgG<(}?da*;3 zIZ?E1J@1znAcyzX)O1sBJ2>NG+KG5{6UWifF_ZEQR~2OgKRIIr29a+ zBk$n;f{{I$ktV_K69SgAuAr%1O6jkgRKzh~R5`nc{YF|(Dv%grk+CsjKr4}pqY7LY zJ;|9&rZA5RFS{*X_I;o({A&hqcxb|Ii78E?9qUJ*3$mE^4) z7w=96sOwE7=*4Z$i+3eP0H@y1Be$!y23|aDiCAOF^e`e>-v9*pm24G}KmpLR_-3%_ z`_?F0G)FH-WI(Ia;RJfr5v|W3^R0kx&BFIZ*#_uh@05>Knt4IK{6*3S+$QR)iDv4z zqDd@2rB6h;t5@sym*PI8Qev&U&83ay!db#GF3$XS1Gbad+R@2s*;NR8r2_pn`&pm8OtI##sHO;|wR#iE zGm?V);oL&HjrC905ui)zF`aB#f!xLnwr?9q#Ea2ZbR3vys>EY(PvAHIoWVWfj|5LN zRu~TW4CPk0ND)elb0Q1wfuN5|?xq7~UtUxuEe9MRxIiKu%s;}V3i6#Fy{zm!-p2DCFHie$lm>SZMi&dhfALzBXbcgz$GSnSrA(I7KA zNy6cDqML$t?1D;h^gC)RBC+nrpXub_@nkIE!eoGND$alJ?9P^DbCdv-iseA3%#mZW zmw-;oPWUBgC7XbImhLzWs4fvd1_GzxJnSZmH}oKDBcU&B6?$Cl9;1B#iil<_dpJ|c zv9+9SjM+~{#NN<#&23KNi)?0!SLTF&aIn`!F;C0=+{^!lC^5^wl-(H`72K@WGB+5U z*a3&wHOqvv)E@CLX1YsZpPKrbHx&Y^{Aqm(t}V<=lWqFEyXg7pX1DyoXZXS}X5gjX zj|V>oQW9RiarFVkr7V_kNnf-V80MYiho5X_j_UJ0gHNujkqbw&0^znoj3;|*ZDO<* ze`;UxA%!)r{)I8k8lvUY2U~3)kApA!hSl*9kh9mnEt01B2@9d}S9o7=n=Ry&QHfBx z{6c~V(H=(Ff8(K7of(#J;{~?W07nF0zlX=?n}ZPoy4*v5bn@vpa406`tp4TfFQ)ML zG8-5R*dAUbi^%hcDYwfXH(ABw$9_=G)zQ4DV)8XD@|mPUbbw+-$WAxGWq0XZx79nk zwf8MX{(|VCxFY%s&-QMO@Sj|T|FVHqrn2(i9yjf2cX0bp0VsIQ^d#ppy=#WsA5a*J zT(4%xaMMmpapl2if(LBYcwEfkwd}s9YWR_fn;#XYAk5b^52&(HVBzlxHFR$&mH@W- zy3%)=_j@brREc|N`OA@3GWZUaK4r!fOU7cf9iFqr67G;)GLU^wJ_`PAH6I`=7>49o zE?}~z56N*#m!Y#((_LKK@!-9Aj84Y)(lflVeNAUbXaouZZ9H~Anf#_Su@A2`Hw&(=#}|L z0(ZRqGpXOD4GRCoa`9MJ>}C}|I*k|pJ2GlzJ+_O#F!pZ3<;1uOVD)Ng^quRwnW`gd zYq>VULp}BiuLNYAzYakStPyJHrK% z4sA@b=5gZpz<~#|0=69s9BLC|>kBtg*kxotafx1RMjR~s2W{nK}j0Q}$x=7;s1;zWLR-3OD zIAJ30vw9$0&eIQuKX#|+_b36?B^^flvzE1H`acS3+IhzlrrF62{XFf#^@1!vp9nEv z0tTfT@_F}Bj(^3S=5x&N-Qc(mILWh@xL?X#q;D#-wg5TO!ulajqfW}_LkUO`{$#0e z9tM7SGu=%Y2F;nk*xmrw;yFur<>hGBD5i&BtWyZge^eqqDhk1PBKAbzxU(&|*n;FO z@+ey#|Ijqq5wT@8{NuyDb&Eo@2%-(oI3^{Z4uxo}U2S*hYjU?wOf^C9HN!q+;M8Rr z4&PUsF{n&D-C~`H1tW4_skkk7aMuMPTWR)@azhF_(DNkXx4S4#S%EqntTsZaV08>& z)fI6@;h%Y57nDqQGin)mknx3vKhk8GZ&Z)*jSMXn0`NydbPdJ(073NQY%5jg@TS6> zF8$7L2QTk~eP%oobt=%2p-{Hc44vgYB3Le}Y$KjGcjFffTz(6vDqDcMvd5fdg$*Fn@|>L! zz$LH%)RUwBHk|C22FGZ4X=$>U?`=&U_>@Wt)N9v zpfqgS6;m+_GV#;gmg$=dt8yY{!b6A8{L2iKFWyx0LdQuWH|QSZ*NRl#c4u&mzslh- zfTUH=NUpXMXsg5L0fO1n;9VJx`t2{l^spVgJ@Yg_A&#R>43diZ@wEn7T413l?{mb> z?EUOwZm2q?)}ScPRi5DYt#zO8=?RZRc_bE7&r21#R1|$IG{?~kjW$GeMr40>=?w7Q z)kbP5bXYA$?Nd0EnHcXC6j>3hGHBo#Bw~rj{%Cd=2QxH{HlOtqXw$cDlGX5IgyOdA zfM=3eF2X$^Q}{?qUq~k&F3?w7t%c0$*A898kgwbE5UjcA-m}v+oE~}~@-pjrRvuOg zJkQfIs%1Xxy2|E|P4_W?#3GCZ%5iK~wZt%Oa7w>8e3-zA8$YX zI(P6H0k{Ye4}ldAZ=I{6twVp>&ULnk-lX-T*Z`;tGGVSdo zGw9+9-@bdfCPHpJN=;0AWD6SRAx_j1 z1z?-_5B6+P`kjSmaru4afPgakYjH)|79ZS1b6Y>Db4!g~273%W{93*c-PL*}7}!|< zGQm70#o(Y4jc?GZG>EbHz9BzK!k%Qqr1?4V7@TqKL!OQ)uiMvnwR`McuNGB6=c zk_#na#MD(I#oW{e4w&a z`l+4?9A{68*FBW+S`wE&M1uWf>gW9DuqnFI^1dHZpDLr_01j%9TfG0dcRHC^lx$DU z>)x3bK&-64YIVae6UK5qULnGXG;BDb+E!U@V=o5S`!!ToT`rI0=%Ar;S<)ahMS5Q7 zAUZ_}4A;d}kCQx1#qQDI1`wL2{ASel(E^0nkO|s#e9)sB12|=Cq5`74b-1!t_pNP> z1Blu0x*R2)NNx+$P*n}09xq0Xdm;GS@HQ8>xcXEPT+hNBI-(&Hm3TZNlo$?U4hbhy z|Bh{e(VRzANPqxK=$I6__qWK3*|IuTZUQ_oi^k4Cw_r%AA2pAStXiGuJ05Mo$oBW7 z89ydHz&vL90-|aRrozK&tsgyorn>AK1&!53Ws-uCH`5dnusJf-A-UEJc*(-aa*aTp zgUf`V|LgEX;`P8*YPdE~^<78hp>vLKB?uw6avaI%j;wQ<_;u_E#Se_SnYVvX_yKwg znDWzoLR} z>lyHs8m(&@8%m0kE=F6BpP3rG?R$&*^_BQRpxcuhb5mkyor;9oz3WK;!B6J05Y!`^ zSoa@eGk*c>U3Op2q2lRXsg1RNQ(nZ;PZzgUk285rlhdJn81!qJFTF^AR_pOcKYiTY zk1peHX8z~aIAr|*a*cIrT!WP^w$<%#71orMq5;bp{ywNxtD)<(Qfb8pSzgy0I+;}~ zu4|{7!5l{^5`xP8wA}qr(|4(ieqkHW%U!N(n4_BVR$wnCqSob{h>oJY;Rl{$SmYxa zAn})f;e?LsN0d|((=bLr2FgT8T!i(}!8>dc;eg|?bb(RRk z&VLRM(%dfhH4tU^^RE6eN}Tu4vFZ}1S5L7GiPwyY01J{^o(2zY7&=>0?^?*$Veng4 zl3HPPkqkhw-v0hO;5~>s+3j|UQ&~6MIZLB=wS4yNfL*yCKVK-td2Ca(yDa_n zoELRwi8?RjlT@A;kZ<@aCcLC~W4?q3x)o*DGNtQXF2G4KSg|nukz3D_<(a@pl>b%X zTjq;5npYJ6+Rz0q!;?=Ns7+!Wv%NGIXRjQ(SCU6gtiKs0It5cw-S&+`?0FrSXS4iO zGo$cK?leRmUZ!E^9(?BYNFsJ0N;)lSKAZk=9-Bm;)vHRASMVbxkD7I{Zv8zDQBg=d z@4mRtBfECgXQ_2S>nGzBp|`B1trFu2EpGqoM1Q;a-KkICa%b|Tc; zIm)2DU3aT(?+M3qu#1O;wosYp=;9av5zSLNMpbOqXo_$6PE^I!^x*|Oe9$dQpl_&f z0gu6I^(FSq`K>8!oQ0Koah^XinXx?JXW~}VSkc14kZ6g%?d`6?V+R6cB%1H{9gE>z ze7Ac4z-FRDcBRa;Nz+*1n_mc>82Csw2=R0;kZ&2r`i!^?rjr&EYCEy;p?DJCZ#?*! zoC*M5Tau(!3-RDEW7e90vol4=jj0$&XAf{BOw5;Wj{@1v#S#RX8eiJaoR94 zz@I}(wEo2+()rykRCs`e%qb>X#-gYH+Q%hFWG-0}xGNmASf!wOWjb_`g2EvK6S6~j z^Zseq=;KPQ{jA|qpkO-Ij$hVR!o+p8?)HahMxyZjkWaz!tw^qc_3C~WhmT1V8fLwYx{%F?b4SwqY7*WR?(_I>enJh2s;t`L-d1c$j!Cp;O9B&jg*yi= zT?YxGs6FAg{b~h}H?ML1&@4WnB|Q9M8Qb)eQ#kl974>?uh0IBN8#kCniXG)CLXvMN7_mtlr%wkM0VpbfIz`xZor6oRC*`kMds z`HmExqCEG2t5rCjz|!N2U?zP4TjtV5?a(Q+q(=A)YfQkzv%{PL7DoO88?MPU`Z*pJ z^EGE@doLY{PT|6(+RC2iaG3$Fk8O|SldMc+DAs=knK$Ul)-a{~H=qrU$hKEN z0aTT#sUPmGIr{Tcb`?IGMjuQXw})UFo}DS70y0>5pVF{6ab3(t$KWAw(1sjuy$(GO zv!%{U>mds*K(SxCxeaoHx83FafF@F$J=2u=A}Bc*A@5*-m+J0<66rkX8{s$jX=YcM z2X)`DdTkmMd1pem>*+(=Ge_i8qYkGv`B*;Jz-b2(#W+$d&Cgq1pu93C*qzr8JgA22 zV`IRSU+U7j{cSYkD`3rR(6-du3D<*xu$1qd)FmLXp-PeZb zB;Tg39mysu=;7iTocqKiOwXjy3+bKBPxl2bX~N!5(Y(L!xD5^1`axZ@2VONe(4V%m zLS+LFZzk@wxgCDF7gdCX*|^h`8ylP&h2pVWbCJNnuy(BgVe-^;>_Oa3MHzw(Y?=Q# z|4rB9#{xy(8n1fV#n*BUZH~vK2*31Xdq%`!3DNmaL}HhVIMuOh?yYiMa_^M&lO;qY z#@}6jH%VUKv{7SEdz0vWv3t6?{fZEK>4LY`1qzO9aS4da=87#!N|ojwrvox%R420G zXn%-GCIoW&B)Fl%VsJ9@zZL{Al@V-JFL_3jditIxFmS2sL*{DAL+Ij{+qggc{9t^W zC6ZEk@hVfPB4$;`M8@Lal445pKuSmX@{_bTZDpWDk)TcSz8HxarNpV;`cFehL+ICr z%l8sy4S?uRB$$AP$e^w1PJ;l%uq^56w`qL&x#O6x6`ot?*!E;Cq3399aiSvA%BKCnlUT*xkW0cB1 z%`UJlQP_^~%k#_gc_}}U((*SEwRk++|D_JH4E_j#DOoqk@|7gqSc)VkENs=9;=AUH zu@Zcu)~x6P?8y+QK3V7ES z4K6AjY?~eXd<)5jQPm8^d??%D^K6|6v~o|y{$?^(GH9H6`X6iYG?5M-mdt{h|s|UG#V)SCPqZ#l^`^K>II`96YBUEgg#^W-i*YdfMC*Q%Ekcv)9 zQT64tmQkg~p7Sr6Tfe@Z^h`{^*IB8phcnxKd=9R^9nE6#;8W(_`+OO7v+P_>yL^rfS2PWp$(eSq@1TPC3e$L^Z#~ z3Qt8wl1MZuNDVI8?$;gK0t`XSx2RN0^l~vy)N(ry&8N)MV~6s0XMni@qjHccXJ*)g zg(|e^w~MV)R56QLq+L21s@O#3mIcFF&wh&sHmA}@@L*F=&*(gl}DV|;JuWr zxH+qny;#?~yrr^*AGjs2u{WJ@n(r;QiZ{mu*OB6CK)Xi)86#~bb3g80(eb|tu`&wI z7QEzr#AMQug@){D6EXGkw=>SGtRDm`Mg+aea%^qpTHbHY8T$p-uq(=cFW&;>(U-*^ z0up005aplqn+x24HRKlL=7zsv4cxvR`JB{$^6La^m_}Ti%wK@x16ff}$adqD%mw?y zaeml^POY>@67-}<$Zo^P11>|})IzNSo@2Cc_y$Mw7dAe$7Bog%f7$8QIG{`n4^Ppk zaqZAa+mwj%r@>g@(5ME2l5?KqhwZp0d2#sc1cKT^8Y4x%|6G2|P1a!3HSqcAUbfzS zV-}{ZkMAtK$Q_~Z!Lqfq5QzS2EYF-CZ0FI;5NKVKPuKI*Ya30<&@{(y_(9L=E)5%Z zpYF+YYboEy-$vKmG!bD~M!{Et;#1EPZiiu*BiRm;sT?XTk4l=AyL;xReaWPbuAHN0h z4wqojzL9x#5XH*<{nkpb-_@Nq_HaCs2vMdmU+yDDhnL^$X^A_&M5-rylQN~L+sm11 zb^dtA_q9Js$DZ^P*k{T;;B&5$t$N>{j9%Sx)2nVvue(MZY3Okh?30>RH|LOHlg4V6 z7M5&sxgyIl4Zh)>K+Pqo3;}pZHZ$o1n^!#@YmZ!H+&8 zbyC8RdN6EnNm=s?lJ*d(<^jiEB=es8k}><8AcGoAnxn?d5R7w{KJZ?<=zaSYtr$Uv zG;9`aah0RgVi$OGWdIWoQnpu7;NOt=NDZt=GCth*i1vJTeR?WQGo-8V=uoo7!lSoM9EJppj zTYrDP?F~DOpH;9qFv2uDqf@oUcVj=^j$33{2PP-M0_}vH`Q^u*upqB8kNnt z5+SKRgK{xqcYl7;*KSD$3;G8#osk-wPta)}8y=Rawd%^dY;RcYJ`TRgFG3r$n%;`J zn=pEIG5zu&>Ob*;FAC80CI$r*2&D!3vH~j=)Y2K^X!G@5He+nH!P`ft4{OX%cWUWd z*mj%j)M0bD=qo0LxyXdJFFcmb5`(OMjjC@d&NOKoY_I)-*bCY)U*#F_y9!c#(d3Hl zrPw@)*tDE^Xc8rWx~c!(n2_E%7mKoD(1in35m@@ag*tr51v$a7TKAeVh011H+PUW$ zXKBXw%Qy9>h}Y4@K>qYE2M4{o;{~UnoEQZPI!d_ook7Yz1o$XDjLDB!ql>W&hKKC? z+_lTqAMaH6e8${~6p74YZAz>_MfS7{Vo1tam59IP;S=938ufLZ3%8xezyRv&{r~f5 zv|`Ouww_gv(G}8(fBlay^g6FtZMb0Z9KF-x_RO}o+4DVF2d!u6`q z8bKMJT!%!y0z<9ti~el572Zi1okc*TX8%E-!DLwY20sO|_4pQ2F7!&2FC0;s(X6SO zoDhLD3l?YhS1WnAC}`S88hgAd)rn})Px@U5Z$p?YqxzP6Ss6RAs~Wlf$R~?{O(hXh z%wtPD&8mPeZCZIK?}y&L_cmLj+D*+oX5!$U%aitP1Yo94X1y2rhn9yf(uexGC^rrE zFMXGf3b)r`$;QmMu6RL1v~rwMh~v-pA0Y=8HWa!`OZI_QS&JK8vVVoLbufUG@M+Ux z2ZtJ?)*WI%{zW#1C0xlMOqhzI7w#1hXdYcBI211VP3A5?N_w|W$?HO~i0*0exsJO} zIKCZl)lMdt5{Zh;uXxwqfEj4FS~yCFVxn+z+YzhEwUim{q2G&UFwQTjbkwEf>=kw!Iw;+E-@j2ofAs#a72R}OzaU)2IUC24& zWF%3H@cQR?pOig9I$D5*k5>RJoKkb|-w~_Gf!B*kLZmTPg8{q-5zKDT7WxDd42_vL zmWyJ`rY;@a1OWmzIp%}zT%agCl8~uEM=Ty#8fDwi=QMwG%4ypy|NW`&kO~nHpzQ5A z$KmF}o$kd$4^@yUEJg|Y*DATw)T|ZovfT_k6uAv25vtwn)Fh}&n{6Pc=V=&Dh5omr zK;tl=9QOif&?L{b^`lrJbz)d9rW4nfce04!>C%S5Sk3ihH>t0DXqEpZ%6|;|1F?EW(|@)(w!o4^3(MF!nSD8@E^jn2SdzN@F;dQ84_9M7tlTls zJ8MI81u{qd=8JR$^g{m6EFxWX3gT50R*_w;3c87!pH#G3wSl|;?rbb zwXJ?`L}ccukTv}1<>)tG`!CDxbiBaN0|_6D;R$TQX(F`)AD3`qH&Qdit+!K<+L|w} z;aI^8am#P)wfWhXO^1uvvu#fb0d=t1-zxH-h<`qxb3%oD;~{5r0Pvh8e$5oGkjz=x zzTL@PZq)ZeJ077JHsdN0p;m6znam(^3p`REvok7|*jLu~hK+UvZ?VvCl>VC5H;c7x zJHETb%>=VA0rbw%rRCk8Fpm$|rsi8!7l>#B$Ca0G(QL#opiN!=#-oj+Jp|IG(z}YH ztzDQbtDUbD-!C+>uIg3#Jk6IlFl0&=7Yn{Q$fpYs_Q_CiH<_)#_j?cx-U$~Ji0|21yfcJLA^pAd zZdl&e6Ch|O4?un^OrMcxI`jLIF_|F*oi1XuRBDGe)~l%Doxc# zr{g-dqEYQvFFCjrARupTsRhu=uIAM#5}xZQesMBZUp?nXvHm$b@CUQ6k8USBrt4!G zK2)T-2)GT(L}PTt8Gw3A`kxt69NontM{Nce$6v0il#Cb{?-B;kq=&r=@V78`Q8j9+oxCO`pYP;}sKkp@#`g!7}{; zLq1CFWVf(JlpPqBHQgVOI{9HN)5xPmDu>licpcFtiAD-d)V-MTKDyHV4Z4psVy2@< z8>Un!2^Kl#59ghy`)f#t;!VlGQLzMYtzjO&d_X4lZ%OiN1*JgkqqE{e5C2J5w3hJ5 zw{MsKCZEvYdb~t=^lbt+m&|Bh8Im{Vx<9d4ijGS5fihxES!K^{Kg|3giC_tr$#hwd zk)t`qYjYx9X%fSF%|>eSZSu4?JMNdn+QeJhgkG3Whq+`4xmxL24JSv`CEMMB*ZXZC zNhQu=NGs|~b%f!jz-9rD>ewOw^+Cr>Sg;XY^q~d5SfNO4*%|)zrAw0Gu$prq17P_b z{)r$wY~;rIv*DXwkqHCeUp&^Hc5qmwTlKOJ@U`nk)c*W^&4z&w)#y#M5-OZMA>Ej1 zc$x_G&Dxscn0xUV9Z*QLJYqhL>{-Fr)$YG@Pho&buZT`2!at%+u>yWT!*93`Z1~4> zjm>i+0QqnRhV>SP)e72L z9cq^MIi4VnxzxgBr7@%&LSnw%b|pPKml*rnM5@dwPe4%idSsVl(PFpt9;mbfqGJbr z{FSC}NN41xG-03L0=qD@05^DzCdyBGiZ}mHlp++b772k>0qj%Q9|0k@W zs}?c_IWF@~d&XyIm(zfJ4R&jQZLix6+i#w_&W7H+ggBZ=BV@n|8SeYvY5G$<0Z;AT zZ#Os5ucH=G&Hsq=ydVP@U<3ua;@GP#{~qsNcD3VIxAUJg+BL}GZREVz-h<|OBQQE5 zOnGRLcv>yu$tgP+qGX4nd0>pj4gP7|`!cI9rdctm7V57SoYaAAhcO^x}7lDAT?X*%y zh9+-A+lbv#Si=2zD1HO^E4Ynz5#qp!Dk^e}utc&@zX6C5Uj<$^;7c`SmPi{0f!kmB z4S%Tcd-I2ybD8jb6Gz2yY-3aS*SE8zG5WYd7lwE%4HwpZ`oDBQT=O4H|D!8a%2kCE z{F&Mt)XErq8$9Ze(#EbxzeiAklU%#`m-PPncpX>%yTzf-4&$;EF^!@=qQz2?VS^5S zGCh3KRIFnFCZaPY3#ZMvm{Yr6npm#Ly30lMrQck^yYrI*C#VhkawV z73Ep;5uW=#lfI40i85J9VL0^#!Pt=*mC}AD%$h|yRbdfc|MsMM!M7l*oKZ%K23{V2 zq`!MpT`BDOwWwV@DFvvY?RE*@e1tnrYuXySS%wHo>w6OZzbwE%4qp%;z(}WC|EEFt z{TuhWJK$GWxx1x#Sd3=UW$}yUwqRh1`mem|9CS!60YEiMlqa(7zw5^6(WjVjlV_$B zC@72FjvW4cgxP&-)jwDYQ%e zBBfi|WzbV`al52^alaE}k`Ogqt#-`)LZ52bU+8V_2Vfm^@IinJ(Mpu}EXly94vsF& zi5A$8Xjc<2h*38yN8u8jqNTF;mOD1I5Bt^M7|){TBTO~6VIGV`{OFspO4-%!lThVK zUtP*Z95Ue^rp?&1T+*655(6xYa4>#3km7?@{-y{1FCxvDuA_Fv7K3@^eUq6sO?t)U!wnKxcO(0oWyVH5- z4e4X2#OmGM98BpkutJs&>lHEIDGA(s}-z8x249(lWb%TDbFS`gy`CFZaL z^rz|Zj}uvq3^|5JI>pF?+ZyJ! zf(2*u-UEiqnrisRG~bn6_FothqZ=V@V3qsRmqI&)NAGVPX~k4i_|_$a7rvT?>64Ux zJR*D{Gu;ckr3rLM?9eIRI@EMVUG9~hh~MP|LUd$8J~&@5FGi9<75IQf=O=*RSKjg6 zgxnozgux$@8dmkK4_3zN_sg$xP8cAm1HclQUN#lK-ao<1F=;7=dodB~R}3fZvABOX zCtc*Z{qRI4_P?q5x|`H0NRj`iApF4buMHp7yz+*DM_OfzfL7X5NYeoZR^!Vcqh!EV zj2TD4@1VYKR@}Gdg2Jw&hyEHxFoG~yK;o6cGWnd|9?zvmP;+=7WJO#e0C^5N$yywq&U>O3cMp{ zUk#>KGiya{t+$KLS5=Ze&W=KMVABdvN)tJ$@=7@aL9y8$45haMI&3OZy34C+|Jt|AnTV zo_5K8Kpd$hbLbrlU8K$Hz>k7?4ca2w$kfTzfO0PV!3#%WJjXI)Apw;o2j>!3mhu(5!2`9df0w`jNc zkd)Fr!v6-T0_=Vl6fYMeQ3joywX*LD_Fu+OB%MWF={H^y5BwihUmX>77j8{6G()Gr z&<)bvjg*vtNJvO`N_RI%rvlO?jg%rGDc#*6opOJJzTdsyTH_xs9pjwm?DNFl``7@J z1#E56+&?y{4&4im2ca?1v>NDU2(FFrpMQuiFi<=Bah!d#I+64V7{X!0kvaKeIhiMj zt)abc7`S<_5R3pr7y$vE(`vs^MnJID#SnD{NJ6tCrXk_G#KS$hwuxu;N)uwQ&0q_{ zUoWZAQJ_5RA#EaNpjQ(2h*-4St1(ujYg)D^Fck3VuzV5Y6TU)ZUQ+genUu3NR&@VM zouDe=EY$7JZ=|#^nfsF~!~16^ePFcg9b=EJx{N3s(55O;n0%E4sOdq^WF5b=zxy z>VI;tKxOAi9_*!?4;AY-MPQ7k`KVJ5vVY?J`H2jKz$y%9$_6VWWH&1+h~q-18@9-O zvZ%5{^Vfz%lvF>S%eZ}|QEb8f6{VhknbnNgfJR=6zQL^)1u>#bS_yD=Ux|Df%G?)? z9sc(C)5kouobp}q_S|GRG`{Q*{i|mVU2N*LqHPNd{>&B`{;&qYcaUo`oT+UVhFjtK z>Gg}StJc-nNUdI}ZK;0t7MFO|DIA6Vr_8e@f&mi60oYIcr9ePVo*I;2>ss=DF{WYp z`Unx#TKxP#ym8RreKM4#yG1-T#d*On&;3GR6BK@wrulk_nZiE;HAVWTyPK$!L*OEX zA1O4SL1punE~FJmNV>ycri3kny~f;lEX9RH^=%9tg9#s| zZMui+w~Nf6z+52azTaUx+)j`N#LrD1DM9fYLSUjI@Qt&N^CLY%VkhuhdF5>B^47LB zS!C)1v#@>Rkz*pNWukBy3+HtF8}(E1!L-D< z*d_e$>(nBxY`mTr^ReLHKZ5@xD2l@ENXHP`J^!YL$C!?EudKZ>u6bPhaXYa6#A3mW z4YH$LV`T(-3pNvS=1KJDf=lSL7f%zYkxZNL+Tp3t!%n>{!${9M5)T z+-F6~m3!rBFZUTUgZF=m@%Ay&Pv8O~b*P{KNlsr7u9F@oDD_0rRi*NueP-SMRqHBK zkb@Gd9B9Qh*X<+gG&yjApJkGv0Nx1PeM?Lw9PjVEUgUO%2<|`pi$iZQ$U6~D9G+ZY zhMx*;V*$m?NJt1VPp;OyWN$FQDKfsR*;nIm*A}-ZjIX_!3E-lQfPlD=K3a;1DDK?X zD=n^_+zhG=pXz~&`@u0nkG62o=217b!yM`Niw090A5zBn0tORZB6*#7f)p;5$()HHNrw#trG+3i?Pyf-5NsnVprSe3 z4g)wK^4P>AEzd|0PYRpS;yMdnNe2J+WS9XoLY+!T3D58QPXWVk0kC?adUR(?6Z6Sl zAir@1D;!!E;L!s0r||*(C~uZcLF*i`M{KQf7F|mG5%O+7ulpn>mKy%(H7Ab#eM3DO zqKG^ouJs3eZGJoUF3k9#gy}>Zw45*V_#=OB8*ifVURL|8hRN`T#v+3x!uMqE(=rO| zCJ2O0#I~dy+_U|%&P~vwsQRkdqvPO2(KLod6LTZd56d7#fb6t8_4=3n`PZ=N5T~I&yf5^Vu@arxftrZkZ6O)f-d?E{|J=%&eviBZ>ONez-dsEt+uM}w9cXu z+KQSx0XZcZAUt|M)cNc6Q%>0i3Q(QS4u+cU9FC=`uk!Yp8Ab@WqUB_jQk-n8Y<#bj zd9~r{@7qZBuY=!#P@PzE0|%bxgrPwS+`wNfxhan%fJC0044$;Y0@L;QX1;DNM`2Pw z%^??13Q}hu6A`853dwi>5~6@?ol%9uyAHT3?+wjs!1Sf5a@L2q)}kMMGjanuM177@ zlfmm?i0h%f?~og>=0(7r`9MidB{P!x5zYAfnZu71kvb|9uzl?H;xXJY{lxe33lWC)tPPA~c@H4?_?jswAL^&)0$=B)rT zt0X)eV)#yW5AP`%G>n7Eu|SBO8lil65>@HMpYZ>iu#YC^-k9)sb#pekEz$)^UGG``ZRbG3+_i0tO*l$guKY$aA(!WQ+}FrN>>0nJR8>z08s0E3*`{kLzV6Lez8Ai`I#?{tc^$X zybk)@^|80{+U!Xai2@81uOweV~`Q1N=j9 z`|srsuZna<$=4!S(xtQwJqQvgsq%;d!n~`5BKont_vrF)0}N+<^Pivlv~h3r?z|J+ zJa4BYy6uth=+ro`wreC%^@`2=NF@8p^=VE5v`NYd4CvU)iMxkYjCK4m-(FXATpifj z=>$^~JMp}d^FJ^n*Ux;eahJ2A!kxtrhTU{N#I@Tp(0IW>{9F}K3@?WH4+U^vD*oX7 z;j>tr`k&wcAwY{WE$^7`)*G27K!@r32%QN9TTTO1t0v%uz7Pt#0Ify-(;!URyYD$N zT6N@6R}xUk*hlXcY@PX2ngohUvH^^|-4B~`Myq}no&^3{@KP{7oHnm;s$XnNVg?(a z+rlA5Ky>j739s~sko}HZ2W*sfyw&o1zRYw!7$^c1AAtJMS3^4b&Mg|72+(STWXz_) z8R(X<+T+W;d!`q7OrJxtyL>VA9E^RQ?{ekbm#uO+j{9ST>4O5^7z9m!^JaTkU}!)w zU?Szx^Igt z9|TCMy-+FYI)xmnTshx!Ar8s&vA9feI#efn1W~0)s)F#lAyyKeHw>8|L7Fu`qOSCC zDmGFuJFeb~s6eI04SC26$0uQZAsReD@!;4@QRE*Zz5%f603Kf&L;v`Ay_JOSTorFS zMY~b$Dk97$f@Qb!7ESxn(7~m0R;@nM?pkT&F&098%1}EOJ$|oMyB1?URQ^3qq7q}M za#{LHpumaF?5jh1-X_BMz-z+%<;J^^ygO^%*Pz-N(cT<&e`IXg(jH6K0>rDp7|}VZ z^9s=?cG#D!5-X*_gmxBN-cuAW(^4W)9Jp|Ni%w4I@DMoe%q~SMt5_-wHH_y~D zlA!Ir?`$9er1W1p8cNW#3^4a<1)<+A!(KZUyCGrE+Cbd!;*BZ^AMQJgzQ?m$5G*4` zdmU;0mx)cTERGSQ$SnX<*nYX6+c6S@D5TkrrA^7|y_m~c7sW<6^mQO}_CA)a3~6L1 zY24fhAatt1==Fj&>As)dw1tTXv@^Dfu@p;|D-7qYUDhi>ryN;wLk(qQP<3J{?~BLs zu&LKfn#03CkGf^-StoAC-iQG=mPZnG<2~-TD=Q!*IA1n4QXi@mfXY}0loNbEc}$#z zG*r1v-;K^sCsbXiuD9Mf3`C8tP7pOE#P^b6B;F=0sLGH-%gLDnDC+@N#>V?@5M|U( zmfO9Xl!r^Ei7ztiVh`|K6yYU|$zacV(W`=2=rM^WtL%h+GpEm10M&9ea_x`lV2J}mJTtS-)EfkB>)tFKH&siJ%;~`42;eRk ze3^T+R{G_k1CYB}f_3HPrv?DK5@!8eF1NcSRIJAuh8b%riMO&YN|B(tVNcUw3JI7xza2A!*J9&B=^mcXueYc(snGQ|v-<@$_Z|5R+?O1fRn|M_z#+fv(o6G#KFw$O|&z zBNIS?G%_ky5}*wE+q`1P230tS0_}xW(~Zx;%D>*{+dvA)YlU0{h1>*%93%OCBfspL zIsw=0YDB5J!@b7sk2~2=FcjGV##QaxPZm|+dtFzci&`tQZ_>_ww($c_J~8Wj-jNDt zt4#-f^_^c+z3aDhl~7G%zWA}r=&6??>GBj*D|QId%Qcuz^lY@9stI#rD%5K)73_>R z&w74b;&aOd9e?>=_l!gA^C7tkI8rX8BSnOpWS+gU%Z-sFWgw**ty`(SlFSG zZ_GsM@P(9mW~AfZV}`nQch7NC-5R1gyA36Tcewo?m#3ULY>0EOgTNSkF{oiqABT8i5K%BZIcDj-(pr@_oM*;lY^_?+j=0^ z{xq%aB*YdE1mVckD@NSUQhB|Hm+I;4lxpa+n=FrtE#Dbc48c|l_%~OG6unEvQ~=uM zFPN-+No5H%AavEmk&prqZ>{e#{!W9DJ<|3Cn`>0hHoK1MVOXkEh86OgTs^YHY%Te5 z@!GChfK|AW;tlYR&39&t@aii|!Q4a5Q2^RFA*FEdNI3$3_Ee;hGpXRv9HNI@zE;Qk zOEwoKeu=MQ^+)x&3P%||dYB7GR`W9~7dEVrNDl(PI(jlg<9CU&0+WaaX#<9)pZ$ETM7h_b$KCUV zOtc!Z^gLrL+dyk=w5`K<2jHd=9bCB8$XtcR2$q?dcmcSV zl6FU5EZd48a~9i4PO+rJkjEm`vt&*oGs|6S*PM6`e_$dPySN>6VxqB7^JEqhik>@` zB0GNWDo+lOD8!LtjGBoox4%xCP|c^v+&(z=86?Q{Jn#sos0Ray_LnVz_A)xrnjGiP z^NCX3Vjq73NY>6(k=Pv8Xs+1--)3yTro`#%#e&wlPFS6OPW`VWo)+3p28DTakd%P&?W zV0IhmeU$_?Of$ z*ZV0B?}X*A?Vh+rgO9RRfIDwFISy)bt%Y0Z3%?5G%TwyXmH_lqP=#>5!~PQdhsHww z>e`T%+x?Yr&-O3$|A~Qy9ggGlcsmG&4{!=?*#vYu}yR%V0^^?yI{-o@{BLn=q+~Ev2 zrwHbVmm?OLo}_72}^kgc?)lqc5#8fw{_)}*&+$#)}v%Hk8@)GFmwK8!ZwY&8GI z=nM(D_0ig;DJKA5S)C!MOWTv{2zK<8icW z1rKofRo@z?xUOZ;w%2{>bmzP6zsGR}YxfxhW}+ylQMzjrP2?B+e3KNZ_teqU0?IOJ zys;YN^ZrZ@>qW*Hhn3HEin80Efj(&9(hK;N8!uWwVi#%h+0FC%D=&1Av+nN84^nD8 zE5x?GFtS{{;NU)IKNFP8_@~|w4)aGRR?)nqLZL>4p(__gZB< z064K4FNJa$hB*WCpJBoDE8S( zk)N$80#T774c68_ioQJlwxzbj1Na{3y)`c5?>w7|9@xf+(v`8MTX*`MdQ~@dTYaUy z$z|+_jrAWFK(ul|h zycehwuX#V4FTZ5NyMaxhk>U|v40fJO3>&p;BiEdCT z!Dg(KWkS!EOKSaJZ{c-N)(iANjS#2s7|CRhfVubF@JNE1ytyPO7L`lh!6<@kSC%+1 zxPm%J_#LQ3hx*kEDI(Yq&{gCXJ3*c@z!v5&GMD&}#(`;_s&kLr}H2J&BH ze~#8y#j{ZJ6r@HKpwj@jl!hnXblDD#Ab9MrrdO|V6nnd-$-O|Rh{iTS7&+{#lme_? zVBr~plYbV!$E4tQN7hq~!x`8a|7hoUeQ!2xdwBZxw+15AQ9D{&8~Y$5{Vn#dDk;#I zB;s-uAVY`GiP}vW4{`eIh6ca74yawBrijhreE)Ub!>7M}8&HNa;t!C)GDG!9yL3-J zD`H@x`v(c#FH%DB6Mz!IUnT!LSENz!!O@#{$mladH3^5LA*fUss`yjrY!s<=GDiycg4?Fwd^LlZl%6vl4wOG47INipXthWT7fO z5#Lc_K$px15&Y=}%&A{G{=Jz~Y@Y8}-YrXX6xxQx7o1Ewqrec&P8u$NSG7c>ixPn1 zqYVs83>VV=20Q%mpSbiz`CR4U-&HR zodBF=$y#mfZia=X+@T!WI@SvJY$s1DcejRwx&{!V*GvJ|HlXsR(s8R%Cep4>SSRv} zYexX|?0w{yI#oz;N62qc6NgFvB`@0h({1Qb172o1IASPe+;1e()lp|<646mI=zi-W zV^pxDI-Uh3J;;Caa8n*ZA{?;zXOIAmWy1c~*U6%Mi~)_r*P!Qq1601jXM8x#N&6K5 z^~VL-Yuhp4x%yMh1*|P@jKt?B=sbCe0Fd&Kfl*n3KugXGIHU18E%Z1tl!~|)5x>eQ zL^@y*2RWiyv|RpQ2D9Vc4W9lJ6KX3&;H0Y*7bX$WITt`|@c=cd2 zzS?x5T;IKU!0^;d`SmCRz@T~o$Qps4CUqUri2A?t%kux0xxNa_zAFXjT`Cc(#I)xS zonLB6Qi?#`SA?iz|M~G$uE)AYb^*MUXFTt@R$;mCEyLpBxBsc3Z?oN^;r8VjYA8g7 zrnrSn%JnQ2NU2tQK%jUUZXw&E9A$Gf??Tx*^=KyzCscrUhvb&s*8i)`7if^Mq{zM8 z`e!@UB+?U2{7;zITuYOO2|J`0h?!pgP}bhC^i#2`mn~(I=SVK2V-W|7+SkMalNqSw z4@j0_Y6WmUBXG|A8*vVVpSX^Qdobz7Mnh&Wz7kRcJD=%s35Y~JFC333IioawUEdD$ zdoF}3046iOw(P3764>t$-(t5jM(v{?_A&jZw_*%DZX=lJ4gY>Ve0C5d1=TnzN`pt{ zqV(vWjyxfF=L*mcHSZUgPjgbQ0#k?3u#nzgqrd05_xn_Un*Pk3t2~nl2hI)ZZdb4K zoWovtH9TtZ7x0wgcJVoq3%zLM4fu4d*_5hpiuyUwm`SWcS;*vOC{*XeB zLH5>{7l0V^cgim|=V9QeD>%sX>HvCaDlxKpGb}J8FxmCL5rN;$dxgM~xC(8|vPpNt z$zNNF^5CphElyP6{xuQ}J9srAr+JkDaK$#j7TpnNJJKlh1y0@JNqKVEYA-|`#tFUm z2+AiS&bRrOx+&2-TlQK`JvI5(bny+HvChaWNhL>>J4D z{8P=0-`=4r6N5!c{A;y6%n1(p-Qa;L7i{%@akNFPuAY}sUOpff)t27@ADT0eEe;Wu zb)j7-5y`@2n~o}k>cqP=SgDbN7&Fu86~B6epx#*^4;Z4ldl1Y0F-s$S8B(Y?aBmn0 z;BXMa<~F2JgL{9I4rDLCjP-W8Z}l0exYh4`hsyFafAWhL;Iv8^M)SkOFdGQpkwCm2 zzVr5YU7{h}*qoG5Z~ZiuNBwb=KnGHdhZ@)CQ@pQIew1RC#F8titnB( z#r0pJU(KQygwt{zC$|U>Uqj%xvXRPl+#3lWE_BH>BPD zH_`-xiSClXpxf#b!aw^7!t4U5|15dntIwQsqMpt!^D&#G63k4)djA6U6e)|%OotWq!AB_ z5Net*Uk~S?GU>sC#=>Q`^77S$HukH^{jppq>6g&mu+~eUIh^+tZf7rrdxb<;CDsD1UA3r>YhZ(** zSf>1_x^&<=$K}WQ+xgEYXR;!|o|S7YJot|5%bnYqjKJqiv@-RX+dHgW9z>of?;ing zmN1r)+5ar@+NuuM(uQpPC?~V`7{;bj`EJrdt0WDss!jX5;~7W$F&0J#RdN{Cy+6X9 z92#I`%y`iuS_r_+4%B92N5Is8q&&vb6QJN;doOF5Yq_8ca+KEKNr<~V~Q~jgD`V%swfo@`XQRnA^x+7oLt1_QPqSC$Ny5t zR4=jJ)j{ea{;9!SE@m4dFw%~=(l;UKmA-w@J*e$3T}CN!TC*){oSnL8&TbD0$nz9Q z#_!!3^0FN>yf(cuGrw4Q?rHoJ8~As$!Y#zr-_ek8c+*!2KRwQ(lNCNZA@?=viSqxn#QUYFXR{8uGZUx$(NVG6I-ET4h(+f#+W9xrlU^Hx{{xt>K>L<&M-qt#@cU6*2R1_%W+!F&ry)f<7sW35WYvIb z89E|AZI(oxqy+qBc=$=i{%azJ`pBfUd|N$Bdp!d4wn$Y|jslkUvKChYP&M+teQb=H z#lo0>5eEuH^h~$9d{mPmh-0^^{i$h>#Iel z?m$j&la@uHRiN7P_fR9jO&`%nj1v86b`Y1izr?^D<_MJ_lKgIqxM0ms_zPd)O9v0k zC-sb2s15z4BGS<6LHRbDoSAz9CA#<;kJds+!X!Db7V$!oG*|@Los3j~fTXb+@NK)f zS4x!@|ANyP)*1oPQ<<#yy}ooxn-HoX?_eE@=LljjxoFsm<>|2w%CVg*1IV-H!ZPy3 zaTX-AB&Qd*90^iBp}8qe|P9v;s!&;5e> z-SsAd<^33@-AhurV2PBqHOzgtkR6xvnZ5*UKUP+_HbYYp66DV0?avN(CR(sG3zWY@ zwEz(`1|Wv+=8*o^lG~o@WBBv;y(_0Ngyu+A2-LcDw-#cFK}tUA-p20d_~*&f98ru~ zAK;SOroUGt$g=?*<+wo`YGJDGW)2_3O}KEf5_Xio)q#wr5~*=FInAErr-*Pyk&OOZ@znY>$>ggI9QanAy8B{z|gHH z^m&dWGC*va%QVg?ROL-=fY21Wy{e?OtoL^y+v|q(Cj5N=F2&-YY^sGm)9cr#=iAD_ z&W&k8XWM{`l;4j!p>%gsmev;hrIDf?7Ftq}rrbQv3SpfvMK;8JWvn0d4#W03rYxBb zgfJ0Qn%Mil23O4tPBLrb;kQD=|)%ce5Xrs`hlDLdGjU_v#fjl2`JkCcr5j{D(K=)8*A>wtR zcqQcWXfYt@wI9dYZO4`0#qNHUmwLg3Vr#6HD!z|wj_Ph_Ucg9~x7>qXbRHnNZrq-j*HO@`{SKBU0tyD zW}0e~S)(CRz^kY~9ONoE#VIv6j73p`dBijz*xHSbdttvHlaD@%n;vjulNTCYw+)$`!J?OAE7?nlI=i+gCQx2e&&_U;m1%6dId7V=I;IW>7=WJW6w@ z+Ay-b&JG{U0Of}pMNE<@?^i%Gq=(c$%K z2>S-*ag;aSisq%Jm3{1-Bi^4*vrr_#X{#PPgAM0L(G{tNpS6@f*~_|q%akf!M*QJx z5z=yO?s=%+^xoM+QTo`@>y~1=hG(#fFoVKc9Gm@SiyJky+Vy7y#_n%4eT{<9_kIbK zwDnpC-Zyb~-Wm^9{3()VZI~W*u&m3ermu=rQk;_i=)O^OB2uGk6LCF&8(2u8w;@ns z%JO)PtT@Wy7M#)rgJ2Tf*+|^F^XV!TP2cA#Z_8!6(_B)BNIDe;K`&u5ZjM^KQw0G> z0Tv_$X1X-;Ki%EW)&x*bPe%frw(?X#o28^f8rtQpuck^*^n<*{U7M?+AVt>69dfQkIzGT_m zM-JnWVt7T zH;k>4zlqhZ-MQ(1nj7sW2(Tc_Y?m&OM30 zTjc)U!c&OE1x)#(cT)i8Fd`}0(rOXHyW!ulZl+^qs#OdIm7FET$vyl;1N>y-`^{5j;?QB zWw^t2G1^+EW~r&SDhjZ^FfQmQ?r0>uAjbFcQnC;%b;V%4_@0?m4v$DHgqo(R$RZGu zr(egEtRbpk&F2}2SB5%+wkCGGZ`PWbH}(BnK;2FH-ELdj4Lv@`Di*FqgEOPuVrsVP zL+-7<#~w+K=OzlqPP39;AM7g^ScG%jYwH)hz;mfM^Dk>Y$OlKrH~^0!`}^A?3_;>Y zzsl($bW^TyV^>HS+YcKH?xx@L{uyc2t=*z+-*5d~S6%V}sXbn0@QYqldJO7Jsf*b1 zB>B~2Wq1@?nQ*-MQv5xGIVu|Pd@Aq}4aiM9_+*@v%D8HLyg$b)&p5k5QeI;zAgO*dC+a}}NH9mc-QtES^9%lDc&ToMdx5KX?0#;lIPnQs@JiFAJL zjmR}=V{X$`gID8PxT(4s?_FNfn?#H>iHoOpmN(q(6igHJXHeALt$ktpRb}S7>GWG0 zrgf`NMH-8O@e}*Dg?d*4#LA!$;%cXnzNbCur-+~$-(A={? z!bvh{m7cGFKD0hsS<19t`->^pe6zs0$&~=R;Zmpu3%oEY-TPOt}Cz#U69TjA)!W|*5>+L`iZXfja z+ZQqtezx{#hXh)EQ8x*a*HhEk^1VB~Iaxg}wJ`u})cH(&p0H?iqq}UyNaI7$d?sH< zKzTEk$=}Hg@`nK-gur+scQ1u_c&^^$r?~{}>Tmj@Tq$CHm!|>Vo!{W083;xmfu1}k z?!+-NWg19ivKm5v-tZdjOT|XDV7}#OHw0F7z0j`|)_G%?OTyiH47jv@Dst7CLg7gU zSy_6!1d%HpJ$C}sI0Z(-x+9_Ts4@pr0$Kc9E{IV%PQV+>sw}Zzq@Ru*W>2H3AFQjN zO|03jzN)<>7M2~==EG{$)OgXZe)E@Y_cA4W!wHoEtY~q6u)kXGB7Z0J*nKm7M<*|E zCv%f+<@dFUp6ZIhiCRL&lH>1{=CnaV8pQKhYlCGw=N!JL!~Di6P+o6QanAd9{{s1W zRooz|Mmf`14QazAW+xR$8YLjc8*^+WjbOYo$jsf@Ub_tPqO};gW>M4e=`^thSc)MA z^DObB^j4 zALn&Gr)5js4FRQm22PZ4{5?^ydVpvk^o+VAJa?xk_@g>}RtX2mxRa-_(x?k8cJ1`6 z29+$ZuNwAu&79~nbSouF1agZx0B2`k1;&*FVf_;*7~+UKU`js5iWg8KXB&N8gG9m_ zs3y(In?F@hvry=IJ>u37O`X^o66Onq1Gef*f2pUirnD}c6z;xHh{~Im1Sj~hPiZ&j0CXIz zeEHF>3`^zKO{x~Y&a9f8)&!hL>Ca0>?%TccRHP6({lfl_KOf2R|3d;H;VZdR2_V&w4|%d5?ipJ{Do(? zFjFvM=tSUY8GyqcB+ilr*A0OM%X54$jB9_ZP{8s^(?zT7gEHE)eJRFG$J*5Jl)Y`} z0@?!$h!ZOE^iBVC7$KCFkq^!FrhWn1{5`66(yHk1N+;>F0$`}~oW2Ho(;}N9NrC~B zZ>BZZL~eFQjN(>)EIS$;E^75+XUQ5Jjbb(FWFqt~gX`-PFAQ$0`V2V$)-&r0Y7WiD zg*xgsOg6qN7OVAIl>^xBDPnP;@(EbUT#N)9o0-X|J9s{3-SgxSH5xAy$9meoF^b*M z@XFKWl*`Xq>2nU93BNJQk4F!4(dq4*#l7*&>3Zi;4LlSG@a(qNq#|QQyh({}hPaI%9})=rH=xRK!|`E3>*!$l^0+S_Y#)2xRD4vxDIVuE7Q?+>A6F9J4B##! zzB=)`=^=%F10kie?c#1XQ0IBQ%UAPFr}I;MTT5~8EYVMh3|YZowdOb7?TJCK8;xqc zS=WOZ2fPu#BpOW%!lxC+`4?r5Il_Z2gZOfz4XKEK`Mh^Um62YD-#7ATYcmAW9M3b* znVjz*kL>eU1gjfe598983~=h&?}FQMxZEVc;g|;&3Pr&XH_o1i07B->M^P%g-?`0s zvMJlj*pd7o3h&(^L(#L!{Pw-EYvwt@6UATNLk4rTe8M*$4&9p8N_}JB7pR>8XB6AS?>nkPL60V7tjx*J z@)Jk95-H3^^sAPbBJ;#fGesZI_fOlu{OpaIu#APwAwjc5N+YFC78p)o_!c557RRkv z2NT>t|MBj(c8Kq@-7f`yI&nN)=jgl?9cq6hC%i6oM+3>YVBw@7kC)pm^z6sN+(V&R z+yQ0bY}e}6;nJ-ShGe>2ybzhUIKO>gUBe{MREz-x2l<)0 zKA%uXYj1mOvDBw$pk}(1-_Fs^j>?DILg?X(2|K5wcHJC4P%BJO;zT5TAjHRAeAs?+-s>wSQ&zOwakXr=%i$OC!5F6~}`}XCevu@tVce;geT1;v35hy6h$?~+1s{Ehf zjr~;0^dl-C<*4^UlD-EDdAvVr9(cexN+Cm_AD2v^qyr6v_V3fY%kIyu^@+3)`WgZ$ z=#``iYN4Yumq^%R?7f%im96#`#IyzaEo*BZz zO^!2o01JHuvD+@3aKvNY1TxC5QiYo5f(s3}KpK88%uw-EuOY+kT>r3{%Y5l(;j|S> z%VaLVEICu?O{FrW`Q7vd@iGMZ@N>$TAjucff6s`XSgXsg!1ZtT!88OnW*`M z0+U;(E|ptRQ$linP5H@}iMy*^tcr#lYjCjGqpF>XYIofqZFg!rE-rLHanLXZKAqIq z)^ONU$WjO<_Y}qcMmEU)oj|2$In!&V`9rghYC8Ve#ZE(6RyG0|j7?=r39!^!!5eZq zP3lBXJJc!dFY@M1{Vt5{Ql{!=B2I~I(7>9aIOMDl#o=E+r;cy4k|Gupp-bC;ggx}TFFegUPaU|sB%e3jw_QBn zeZ8T7)xxyAzoNT`7rAntGww)DGHo|S0X!F#nVGPH4Aib4zA3&F^Kl2 zXXFwiu5M;UP0e@QOjpq7J5fD}H~*;L*;NK#Akc6_ zO|op)vF-VrQ!mW3kbu5;swEi0-+$nN(3Zv3#OEM5t~7Y#!%k0v*7EgiVNBv3AHx0p z&%;mIuuNr7=jvNP&EE_ivr)4lvcDd<9U`9%W?*?yWfRzn5^b#~E?w+?9Gq`Djp^>9 z1fcAFFEIdRa%5xwD#yQj6$ko&NSW`4^-w1_DdaY2I7&>Y0_zwJMwEa-D2VePfPXjT zMI$Er27L_j1V4fSz=c!qaVDWxy$VyYoFdo2lvda}Vx^KYK#xR@aa$G9`K^k*Al>>> z!Lz455${v2tS?;!@Cw2{jKnX3-x>_vQHY6lix`N&{zFqb!{;AeQDme|dtN}=mBmaZ zzsDV;Lra$uUK8nvVY}~qZgV}j!Rnx7v!t%d@q;uxle_Dld%rP8f~l&%c~FD z^wOo^xSRoR0gwz{PlJ~nTlVR}`$3J}xQmaslLO3NB6gy{bFSy>Ym3I4kF#~p+P%#5 z`+U_o_yNp8Q7IpL`CstK12niWF<7jSa+o+ABN-dEEi8s{&>+=9qN zHIxs0mJb^4!rNb_*V$13w=I8pAeZ2;f=9z;^*7&6@GRl5BR$A`#X=x$Z>1Jdbe^rB zPPV5>f?r4EcEClP4%{oV;Z1JRaS9D^-pvVr4Tk6<-xy!$F_@%i#%vfgyrWZ8ac_AR zac{Cz@~ywLq4Ewf>#dtG9FVWM42K_Ka9H%#b=rsW^C*xddDQr;(dN8wuYbgURKfDh%!22mI_yg8V3SM|)jb)H~ z)(bMHO_^?DoORG-t|KEJ~W9- zj*lzChM#GH0CGs2bv!lvN&J4gB=*R``|EL)gH__)hi{R{K+?~q!F0SyiKmWE;oYX@UrVd5hy4FrN%4nBDO4?@?Sq^FxeGN z4Mlz}32v2E>m3v{1nL-%-{d9snzFg{Rf$W7Q<@86*L;{RC^8}W0VR2P6TR0D`&;x@F2dp_@+`(KWWp2 zT@+TgK>i`p3w{CCuBf1-4|_OS{D*SfDQ8y0D@&a|$q#L5K}5qoZ6^Kj!4P#q)|4xt zx4{ou_TMHLJ+&41LYprCb}Oj8)IcN91w)y#8)&ZVR|fptIg6v zA0AZGz`%l#ke@CJ|FEatjXAKD#vCgo&=rD{2A(%7+XZ_2BD=pkV;goCSIHS9{_y0| zANo&RQ^DkiW7R|_tMw`r-cCSM}< ztU!0YsYY((|L!`RET!gsS4&Q&%G*DDXUl~cU=^5%FX4G2(2ZURhh7ibumF5;6k78qn$rBdm)Kx(L7d=$Skdl)o!+|4O&ngr{3BJ5gi^ zeBIuT@)1}E-^q21k29xMIYBnIfGL$%#1yA`Tbr)EX;iJNbvs_9F0GYRQ=zC=o4gm{ z4c!?3h4g-dP)*3?AW00T-?l-{0Q3Up=!N_GCGQJN>z6Mh21 z1^@^}%`Klw^WNtHjJra+saHDudy*j=(k8{B=Qf5ZDweOZZ|h4`SogJ@Hqb)E#Y13% zAx!mTbEk>b3j$^_|QO?DXXlf0$gi)wA3kTVyFD*zgf}?~6R;DD05XleNM0YY&Ihp*qbjJab>7nANwDTEud&8%B!uZQ8Am>&Ji{(( zN6!cd^BoF+4?GIGlw_`+^GaI&)HKQ~IS(ipV_}Yanzjd-OT@kuBczm*!Xr;L^x{RY z9QHOzi5)1Hq-#&_s2^guK5#@sdS#n#`ypVGbmB+I)MKS4Z+@;5G4_UMLUcHKhh!9g zpE>LjY5e@KUOE$VVtaavvM6m!K~~or9by9VH-!i)=WU|9{q9AD#ggYy{xuPajD8{9 z|H(;}AoBfG zGl4kSS3qTnHBk_+Ou@Fe4-2-%uAs0kT{PO4XNu6>jL|)bvu6Z}(FX(yQa2F#)a^zh z;T|++%{0bs>7R=pBcEa}c13N>lmI~WK`1;cO*k^WN*0&~lkHfC&fDarnZ4xctXaJ19lO{nqI&u@KSjurhA1eqn~3hQp;&$#DRbjnohW7h z`Lfx^kqR}Tb?*B+fv-oH7)Hx(iz5Dr2n6vX-a7A9k@-aK$ASj|;y6 zD=hpv10!d;(vA^;-bGU?GTCxc%qH#}5#5ep^hx0jdzI5!f2x-o72h+nbut3xj`UCH z@4wxCu)*_A3^7@D8}SQP1Y4am2WEaH`CTN;+xbW<+ zbgFZh9|hc1trw0`Z+%5@4dP1RHBpxT|4ubl6=X%~;*)oqeiKprp;-zXPE`l)9h93h z{Yw6ZQ$-TS-Y8bWxe0Y-DQ$IWT%+JiGUFa^HHY3rnb!@@rv>NPVYK@Dn&}D}%QBiU zc76J9auBk<8Ic>WIlj(VU_LK?7myQmFKgwCF2e>rgzYf@6BO_A_2KArYnHz81Q&A{$k#GA%e9 zhARVpVS)G`sJwp;etTwVxO6n#Px z(Zm>*Pbe%>R7Xa#;-aaw`Cvyf(}@J7Od1vh-jPKzXF%6O79&}-hvvid8eNu67@I3X zIBoT>7Q<>{*R{eq zq)=pp|D9`3PXdIemm=umPj4w8I4?3RSdr%fxaNPIv1>@pi;XdA+ef*MyvM$=c@rg~ zq)YRON#n(2)qe2MO~5h)+zmn?y?xShol;%JOROL=0@&d`;;D~_FD9O4?o_6ce#vPA zfbKUvC~jxPcM!<%3HwRyo)te5wM_f0S(^+T!zri8)B})tfIXIqd!kEm^F)_8H0Q?G ztIhd9_#2_dQr^KY&hqk%Z>`PB@L-!pzCGp`-$5a6-uZ3Kw3C7_Nh7CJu1HxJK?!`Z z-H|jhLSUnZp4X!F1wN^>eE4#zys|s4O>G!8{}BQ*qh7Gk@J!bAf3Cm;WJT`clR>h= zwE@7Q2vGU!fNk=?Zde9~gLb08PnaoT1$9cy-x4YT;t2lXcw?Ii>LVK-W~j;WDG7qO zrIre2j)hFH)v2QftE1zMv(_xDyG%_)VIfCBCz`DS!kHZRx=k^N?eFy+;y_=e((Vu} z33XljY1FZ1@FYLJmwzgmd{_AKVtRI>r%eV_UXG5sScVQ^*=v!z$exeAR+ehpj!}3- z24T?w_xsUl&3+AXXBsH_^y-&UyTAwhGUF}{@1(1TKKVqU?%g~esKuu?bOeTAPo>t< zuD0N-HroH5ggah}hbZh{PnGL&f(f|0x28gRUe>d|MwN2n+tu!UL4S zJ)%t-*k6lAwD}6u4re_ti+=g(do=M#)<&LEJyp#BF*DZp_W&g=|f|JyN z&7`6;ABK|!_zf=8)l^U`jSw4_b#hT;Kq0{+yfhBg{a=>mcxDL&NdeMzSbGoBP@C6# zW(gMfSh2QEztS!;*MfA@|Lm|au9vj~Mzo?a;B+78Ek(*(YN&iB z5hqI*26Ym?_Fe0NzN+0fcrwiPZuL^k4m!@yVy5UpTQ`r~OYwjX(0|@z^e5fYlgTRJ zsUB@=8=IbE7dbr(vzgYSRx@UD;1U*H)l*kBh(Fn<+242dA8o4Z?M6H$nOyC`%z$>P z4X&3Fdf(OC$*v;FPgTQMp{`q{-SSczdS#$huFA&MRN$P4#$r zi(voJBrKHn(9aYGa3ddyAPB5HsQ<&P84%$v(%mKdKMCLw){|h~=_T^30KeXh&_4_L zmG}0_|01DKz2U*^q&&BT7cbti!?4qM0eL0XVpHzJ)TNTrV(wi*!60zxl@sRgozde7 zjV8S<23$>X)EE}}nP#uHYou=P_GF|Y!yL-=L)x#ZbQH5|dYpD76;RUiO4RLVPU?1_ zv^QF5a61hL>w+VbM*$d1u>Cpi__l0r;2xJ{PVylRrfG9sJnjPFS2sEm@vwKK zAdxo^6*a|=#S(*a(_vVCEC|mu?-KE%WI=*`%4DpvgnQn9Mn&Q@JuH-ww-~9*TU^i- zn{|3q8^gQ%{^dWa3nV&lM#6l8OK-y+xZs;q>AxR=44!(jj1Eis-ZEc*Vrq=WA6Hc) z8qfy!pF5KlJ}+r3P;Y|5}7i>g0yT> z!X5_pDHwx|6)2!-FJhfpd{#5y^@}nt)2{}mi-G;80ct*fFyNQUMUWoeU_on$y3J6| zSoh%6`p~ZV=ibusMlp_|iw-+jlX^v@-w_o@IQjE&ps(&ngY6*nsH};)#)_X5*oss< zf(Rdz17_l3Pfv_eEZ!;0{{nPC)TJh{_IhDYZ*BBZZP z<^gJ?qNwWPmJ3vvyOx3ks2YmD^Wpg_i9rC|?b2FnPAQ%L-i9s)>)2kuu+1a9u8nkh z_^jAbalBYJjNsXZSNb#YVpEH!cnP%BR~ZqYr&+w zz3Q;z@%7>D{dXZmcY#%RUg=oVOf5mZjLl?&4$z#su?H7zVLE!Rp z_5{8x8dybJvMz*toVyJGB<2~t;4U~!);Sm%VJ{H65nXz4Ikq28Hhk)?7XT?yFvF59 zz`lBuL6fw)KLc<{ksihSdO>30mqSC)aRz4TZeulc#xrfIyAnx|E&|^y7sxo zLFlw$_8o{eu(YIdF(CE9COXbVO3CKkZ(oX+g(Cp*zpidw9%sVC4!j#-vTpr06}0A2 zGXfH|`(X@rU@O*Mepmb8Y>A@i>1;un+4s(K5}bjD^KJn>p*k?kn^2*G4l2XW&lXfG zMGp>Q8iogyR*P!cMu-9MYk1v($cQ%+uOUe*a9R6PMRZz{cr=TI&)x9;M5!_}4Km67 zdwdX80{EIE{|Bj^_05~H& z{%$Dm>|U<`#WU@Arzt?fWEA+vj>gM0oa?S)JH##i^P&tjpZ}Ked5m1o%7r7{`1RyK zm$uXzD|@m)bSiZ^g85+1vrt`jbe%BFTMTyKdtsqo?Ec@bhSyVCG91s&%3nTy%ntsD zKR06bO%a10Vy(RO%yRp83>u&}7hja{%`aGe;AMcD&|rnBcRr|04b@9h%?Rw^QCv;J zkNNzG2r>_2ZdOkLj`-9 z+81Q=8-Kh~9+U(L1%u1!m0Z6|03#ZkAACUlF6)bwwHN$fEdYq8o8Qph@p;lRBB*CV zc;PYSzhj5faxLLg9Kv_18C^LMWKQRQ7b~9cqWB;U@So#|>Km3ni}N9TD93u{EDTvF zm0>O4@e+?G!_<+7yoBe6xqd8zhUe2xY*{=85!fQ3ZEIs-q5Wk$yy$4Lo*bf}OXR~r zR>J8>)6M_g7{Nz>5aT29d)7BWVUz0nyaKWLr}v0Y-1hiCvpkA=X#&V!K1U_ShQjBF z^lpF#xC^5J(&?5Fg|Sg3`IB7$iI>^n>{Ttp>=5;Rm*#A?Q6IUd3O6G_ub+rJ7BcoV zqGrD!9@ZpTg4$R8oQd^Ika!-F;`*8b@O)~rI#lB2jT$Nmwq0GKK;xHPzjLZ`wM@Yf zA0xPTUp*JSaVqC!Y*g1CMy8*n-=eGyB^5Ok)Bybw1 zmO#H(ELO6er#O~`FCA9Xjf9|&rKkl7`R&Mrtbc;S|F1;=f%Mb5eansCA183_n%g2R zw#D)^c=_*kc?zmx-2!S=-#ekO4w_dTMY%v(1j)r~rgz)|G7ZmRl2{$!+;>&`A`|aL z4t)wMlT=;@tvR5HxuB|J(b6G*%dbza)1+=7=bqJiWFk#ZWsA0;MZIw!DN>M0baU2| z7SGTQ^_>}BLrtJj_kG{9JOYBn6y;0)il++(5+!y|-gK+aChJH_1O)|JYrcKinAEv% zZ%2=VMcq6X>eeOtvMcV>JQKWHMnG)yc9Y}WzBvmE4(0S=R0?}5^O6jvd&PUseQrE) zAM5TGlYUfHa&ER+sk0@1B`cHG9x->>Q@eop>x`ct#i3$vyO>lGZwoIkydaYe0_Av* zq1^UNBSYQkebFx;b|uta83jb(W6a~imUGsD33*=!6Z79S>w$hf!NfrRd+uQCMMQRk zx+0073`sYjlTF%S@%N>}4hp0Bm&LaQB;}{hLwci0%zG-&VL<$A3_u?8ee`i-iB0V? zAyIOL%2rPE$vXr=Pq`;3_Q@@G)8#V)jJz|t_1j{F1l8!g6ShcsK`oV%y=~FRoL!W6 zvQ~4tv{9R$tYDql%5u0nQUIt4vsVXLXG&QNu=B?JBD2C4s2Wgl!ho zS5y49P(ZCDfof`zOp7}-G%>PQq4)QD7bFAEt&5U}aJudeO8-uqCaZ@|MLa000#$YA zxQb<$!nUR9*A9`Wkub!=V7N@2x`6<(CsbVM&sspyyj%!9%AZyeI(D00+{^O-UE!>> ze+vhHi%lcp?&3JygMqcTRBcWw?&sl5PuAytjDB=7S54G!u4MXXsj1a~fg$v#>7YBE z6s<9zqTVJ$BvzSM^%az7-&elUln%OL6)OWeQ69IQJ8Lp>G{0_8-@Hz$?=Er z?#Q7!te$h;MZY=HD@1g@RNv)9hR@rH)uZ6>4IN=?d4ewXnDb$F8G(tC&8NtyD~>X~ zz`;G`WaS;zps=go6KIhEZPIiUbQ;4y6&l=+Uc*3|p#m9Rg&-BqYVU_IO1mQ&Aw%pH z>IVgOMlco+N%;?~K@-zFnPHo(^{7*RN#+2fD@kUMjIimoSzk-T;@0-?N1~_J?4SQ6 zGQoaaXeJyLXyG9cF<=Frd1fO1TQcyE%0FeF41v?_k8Pm+TD9TKZuY2agalHJ_dv(i z_4~YPp)P!Kioa~pdpjG2?Mrd@eYp9qPM{byPQk-!WHATauU&l%xdapw z99{7vBF{^6wu&n2OU|zbZN{6m)E1b+^*tyu0wyk#26P$ax8w=0Um*@?H!2h^VFhU# zjFRI;2G|_!-Y4PQBxIFMnetEv;Q?ltk z3YGm$X|?YWTD@qKK%^ByQ!2$dy{+%r)g@OkmLTzENIv^Aiga(8X&Cs!I ziXsR{%5EkRoS40yFm@nBq>xv)!j4sgCLLA2{Z4MsCe%^DRYA{saNf}5vb3K0JurqX-!;79F`ILd6 z?7-|&SY#)pVx6vv6Lo(zh_^p|hlV+)0Ef1f=#EfRw!f3ZeV$7>!qn!#VD*BVs*A)W z14_)D*mwjS8-!zkMqd^*>Ir=(P{=q@tSfJhj)=X-9po{B8^@>S z*`sE9VINi4lanU2N`O(Gjgb-H15c-26tO(-T+fJrC670o-{Yl zM?JaiWc3B14kghb&tk<+qlYGWy8ZKUFLczJ(8Q3#pG%^1K7j6=)5|^H%Botde9^A6 z0a;?t{K!;YY2P@WQCnsmzO<7IEq76S)SEsul$cIHT=ns*YMm0ht;TE&;$f6_LFQcV zmTkl4zM5>UJhgw0$SWc8|KuO|cMw$^Qmqn6_wgQ*4>0@>2MK6NNE4$J z(A`1}$}yI2%eDOwap!}|jU2L`h;-doE-w>7e}y(GD;1@CONymwVL>gbD7E(KH1$Io zet#j{y}@wqwV|gf*g*kC;b$5&FYG7xvdGJrSGo^ResWl}e|(s)d?revZ&bZ|8Z9Qf z8&wdEi@Q0ew2*H(#&8u<;Zh*>(Cu!lZh-MX$RtjSW z+YOtqjJ02NW+{G)uIJr+PFl9|v)QA=@uv}Fk%u~V^gG4sA!xPC(u6Sa|w|KkeO z(5XpmOwDku5$6ap`PMxWVGZ{MiKny}h@CR`)f|X#;I>j9_S6l%VnHPP%O0KY_Yae=`qg_s_*+ka zKgOL~ghbxvw7PdS{b1-f`)MTqc=ihQqlA5Y(qBS6(Cpt(MUTVIF`aN^n`08P>h7y}nt%TWFsgFOC@pOS5 z{%=Go(5sn%rKqe!jxQRLdVP)1V|CRF4kz+v)QFihU+xSj~08+*IA5sMs-aYu(PKilJ zDuM19I6>!|TGt!0_{*9-^vtQ*j7jq`u->TErq}Mq(ziVeQgw?(iwlwJ++K%JJy?V`Bn!!ixc! z!pW)8grm-*Hf;e`mFe}np$uc;TsX0F4iZ{As2NaP(WxZ|SIvj&Qae68Q@MmJmLh;+ zK%YZGybHhB$CB}4-BG4`(62a_UEOR9-`=kq*oZnr=(d86ERcuDo^Hn0>N8%4yf5 z`D}ZQNqE&1K zbg;(Tbz=o?*fnmHw-*oqP#F9g{YVrr`~Hu}XeC8Z5vWf=7kNd|OESg(!2+y9{=ZMK zAL8hJ;Y2X?CHQQBKD!!lbv-^EXqb;Z?1*}|&NteFyY%Y*vp;HFHhveWhCObOB@`?^{|l3k|V?rj3p2R}tv$ zZiv0W`iA?m(kgtM=ncSf4etb1V6A94pzWnFh?)9GL`HGDx^|Le`CuocQ?9W65%22( zUH!kE7?u;HK8*aoHUux=sIwSG-|AfK+G+rl+UW4&4{4{Y5}l7t%(dTfU;W(@O&3)6 z!oUHZP8#M0ta!21l#}+kKB5%_Fz235OPhvX*5R>O&%Kz6EwXnX$%0>_hsB%}z1fp? zu!)}q_Pr>9OAKiy5X`$NPhzlXOrY-spc(3k*d4wPm=4ZmT!ej<0+MB|k1rjjXp8a> zm#?!%7Ccai3o`n~OSW@{fkP$0S~f)HL)%6FC2&sbY##Jq#%L&t>LsaX(4y?+P1c`k z@v;rUH#+Wbo~r%sMs)b}<=*Hj1aQtoSJf_DcRZXES%*91Uy3SRjZqOqVh3qb^>QWs zsy0n^#(XL7{9z^p2iJEXND9%ZQt7m3U9(0nPtmuBo14vx=I)LlsH5?tFD`5x%$r2P zGc-B}zdZS|r$AM>7-$FmO_AEzXTyD&f)Ji}H0z~$P-pViSg8Ov4YXRg4Se`M+ z=y@KU!|7rw|0;G6)-oeKrB^5|1)@l^*n-S)Z`dtnugf=nPs}ded2}^JfuDq0>TX~l zDs>;v?!oT0lmxqTNGov~?SsqtU#HS*xCN#QF_nQh6~V|%;eVVwud$y(#dO<_%jsB@ zQUmW4Gc#s*`zrcUU$h8V5?fEd3%L7`IkONyI$z?r5OiM`>f~0&Nw{l;v4zf>Uh8c& zJh#n-;eNU6Ke&Hy0j*dBWH)x!;4{K++|EOJMOz&+b2;zhf)u~4a`OhDiiw{lbmIpR zNj1P+Ptg(%g8GT7kTBu_P{O=#YM?7ty?(#5d2COk>4uIU6foK6+F>EKob%+BZbP-C zFSVuzZy_--hl*85d}6!iG$vd)S$th^R{)@Qy@j%6JDtGj&v&g-1l2H`2-nl%iPF?- zl>n-RjjkW_R7LayU#F6>dwub^r9n!gO{|xB7+O`{uDs--y{a{g6-|9*S8+B z5VRBQKYa^9vY0>MIOL)JN^buaN`_jJ^GGZfZJoItYfvGY*dwPCn&gGkne|lMJll*4 z0h{!ci&0z#;WCxk$?R~OZ9I%c8#j7sPl5sZqF)cKr-IRk(l^#I%dOUUr#|M{H;miO zp~rARzpp=ESQfP^VP`(xB7f$Z`w`1NOnNdYSYJmpn3EJQjB9^zKc&7?zTxS?S_qCu z6T~V`md}0p-pROFNkA&UmrN)eaqR+0s$An;l68V=Iaf^lAmEDaM{XTX9Af$EPI|~t zs!JZo-<-oP`r7+dC9c7s0FQtP#gH!-Uot5^37RhjlAoBW`MoSIUW9p8~^_BP@3*&6Uo8>NFPnxF^KQN6O@Tt0oY)?kPQ%`F$ z)cj{woN!y(7zn5jZbe|N|11{I_YMm`D0=xH0+zMM(g&`S@#s7H|C*2>4u`>rRxq=H zvY+mMW|NLTXoF%H@HFd?gsi?1p+$}*F;VGYuyy@L57#N52u+QX#TCDs@XhIT3ao8_ zdG3x$wpJS$z3dFClAi>EGX0mIC%T;Mw?kjWP9LRinOzn9AYbT5){U?pAbQRJ<`CpZ zyvKzfhaJQk2|ZTRRJD4O)Z17zm%*?jU#HD*G07Ndq* zm*paQ2aV*7_KPIkRf*J*n0;PawZbbG_LV@fI!hh%o=`VrFSt)nKl-H>A1{ZdZhqtB zSh>YS_{`E(1f-EmW;ZP><1s(oxtN!|exOwKO%3t}? zbQP2h^B(UkXQOc$E~C0w$)k|#Bb0XaF+b8eY1d%)mCC{jqC*~*r=#F8?J0ePofwTV zz$D_GU{q|QU?$}704Wv@F$b}V!tp!v(J(3{Qeo=IuJM|`fj{jKKiop z{3SWI+K#`EYM5gplJ$R5QD=P)W8WND;!+u0cdoM^{54ppRsTSaWHIAp+z;;gqY=01 zY||cS!bX3wwZ(YZa>J@`*dqI`{%iILdChieGbq7yiUF;TgSjeqn=glxUb%XOrrt@% zogRj_7Ht>}R6pFlASZ~a1jKi1b_FpKwpKk)tO@*Z#ji50a&Z@ng25P2zxP?DCN)}` zecg2`reM27GEg+?oYnAVg;;(y-t%+E3m*huibGzKVRk@{Lb0JiaF9F?QFG4>?f=0Q zusH!Fw|hgxS0N&ETegDl7k^x7@oW`Ou|+>Kn$@n7w>F1GGh}~74e7V5zzNdadpf|~ zWd!PZp38b9<;EHjI%Q6Nkb%>NhBAlN_tM3Z?<${tgYcB-$+>==Cm2t6eX|neh(QSK zy`RFC*QrDn^M$+q3_|FXq{o%$ zY=`j7jtGg3m?PmLj|SZyhjr~!FkX1e>MxS+>|huyDyxL zz~Bkt-=x*XaqF?H?<`TGd+edX$Yc8%I)^OesYoyc+WGed9C78Ir^@kS;we^X<6C_5 z%ylsIBR9PRr~#VuW^Vtj5U@T>yw6{@k;$8~@m-H0yDobW*Y93&l;2wz+dmuCJAM=I zFN{r)cUuaX6x6i`^u8R_8*XN%rg}i#iTe^&-krYVXMQ@o{h)j28fbeSKOamt)v<5K zcq1H1J!5VDm9V4T>YPhgA}x)byy@|X1%CpTGGzbEf#cm%H{L1r7&bhDxP-zuucspS8qboe_>pV(Gb_D)QCI>w;G6#$QwT2TH-xjK4UY z?4`;0%jYawt$px*gE=v(=)Ai!m;#O5J(IaZHhaRr#~nvgW}*d7DI>CMw-yn|*Qk{VmE}ks?QxN^4d|>NX>X5zFAZyjIOhuCEn8hG@2C z_gg^I$Lm8wK0Xk(!V6~v!r=y|oLzWFB?Ohe#E-V<`$4Mwda?a+t}NYX*|KYCgrU}Q z&Ui-AM^;HyvnCIIU@w2-+;{NSP)BlOrip0bl8`w}gAg;D+-yN&mlr)A1p*2V5jtw^ z^N^3B16jbGCD`-~8k@%K=Y1$d;D4VEO^pAXH(2~~NqVbPh?O~XJAoXgoY9Rn;QEyp znp*ZP>-m>$YKcB-Y8=pxj-fkCOc0=F^J0jT$sU`V%H(G%q(AdzgAxI?GAHSI#T#|| z`|k);hl#|%cG*e7Aw-M<0T+(H5Apvg`{2AbZ@N!?;Um~w)d>IYC6JlH#t5zSh`pS8 ztBxm(eT`gGkehtn=-r}K7p>zl&Aiv-yOA0lP)Xw#j|uPu_UAXN?B;!OKBJd!oy-{< z8c0Pckjv8u4sw?dn5)*z8jD(B{y%pK%S!1WGtwo#{5O4Jdx71kM57~jYBCm5j7=Cc zBs0?vw&g9jPd}rJJq`Sh=;4ZFFK?K?$M2Hh%Ew6{0&jj#_wP6?J(KI*dj}pJM=@glp?ML?=ECx=0PDggQ8 zumhd`3;F-Za)ghJ4gAq~Zmkt?2wv1T2JD>gh7`?~{EXYGbnQ~vmAsd2=G5|qPf5yf zKO{&HpFUU_JZ4??+KP#Y5?1fCk`SW?UDB-;z~lq-OZH^cI8Q80C_bL+9u)IVF{EW+ zT8yi?cD|;uhF69cFX+3|tOA%e0IPH#abv{uz&CQ6xflng2K#$R;-fzf(`Z}AOSa{t zr6lT3Gx;EMo#Ygyx(WcNF_0u->hT#`+_QNGA8_W@t(@Pc!=96gK*@;ktRjFIw0kTl z@~TUu4nj8t0i}qI5e_Y0jFdslUqAjg7oaD{Uf~IPN!01Wi#M}K>nW<$l{G^du8k7$ z6>2!af+3V$GZon9AIw@qA2AaYCC-gKQh00FHT~dT>vxRbTc!Ks8A_%-azdXr$;c>) z%$Q8M_iju4{}DM29ateUS!nD4FmvlK~8)FW*z$znQoPE?h_(aF_h;Nc05e{V+O9OL_js{CxLY{adwV zqH}FywT-764|s>Hq@|u_QiMz`f&Rxuyoe~`gUlLX>>_Sc{MM|inbHaN+MI_)dn@`s zHi4mh1rNIbdw{Dl**=4sWbc%BVSIUG^|})$edb*AgJP!x82W$ISTB}fzs&Y~s;6iZ zQ14;y+HivsHvHD+S5w=+8SSQ<(|a``I8{m_gLy6&F$`aD^&rD8E2E4G=7PrlHMx+K zxNm0>$@MQtE6%MOWu%}0XT0r~UAi6>#%7H(+wE3<u+Pd zR@uc~7VhwM$Xz6%HvHSIcJL$i`^VE5Mch{G?TC*WEw9E$7U)0zqRa%N^-`fYRx|Ih zzLyM#yE#v;jm*SEdt`Ug?V3q z+hNC}7_(76Bv>CKz<*o$u|&87+u__Z7cIx&{s6=TvYN4X?WD@JJ=64ZO8~fyL80%k zJ&hDo&!`Xp=WRz$L5~RQPQS72wE2~G$rinM)$tp-bKW~zdn!G?%shBZtm%#%SyK_% zWKZB~2*~h~_Xfkhyvg7UPx>japn^@rD&HUALV!$!Mt(`y*kk_Hu8^~N$Bf-)(uHw) zn%~~yH=WAxw%OvQsE1uZRA_~|S?HeA=)D~D3|!kIHbHc68JH|SiqwZ!efV$J0m^WT zWBtN$l$0BlGTlEOJvq&#R<#*Aj24pS$AyYbOW`Y2*Vo8)sRQ#lYAc!Mr?6 z1NlLdM!6vd*A*9>lGtIgANbmD(wS1vE)W<@Fte?h39id*4uoZUeZG8vb9?`Iv&&)M z{++rq*whayz@N{S_EMc$BHG_MeUYhqe)L z3AZsY?P?hH;s*IXZYi58ds4VF{wilrMZ08<4=2 zMha#h0L2}b^}Ky=CBot}4EF9674!(HfVN4F3`I!D1W{u}9M-XRy_w})vssnOZ!%YV zTyJwm#`C$PkBzVWsFIFf*}_^XilF>O8vhFSsjdYZJ|loXu9U9?jM@UT%7AGsmLXh> zkXO&QAjp=l8MQ)S6#*V*Gvg$_Iuz+bwTOZDg2Q4(7u1aRaYBR2!=bhbN)V=E8DPql zC6&jl7tfh#HQ5cIJvoTvm!HqV0Dg5(IQgSP_ke!QR5^{NkD>{_pzJ)Yh$w(RjLLuz zV|@>UiF~O9T>r>CZ+P$k`CH6`7M`(NdRF{RuDNP+gqbDI(TEbykXEf&>$kS1a5DE6 zZsYow0VbM;#3%186WA17+MGsPPglst{R5HvdB;h9lsBH)I?PU=)g-&HjuoAAWV4Bk z=7S1eSHw{4za?GMXQIT77_p0x$B!%=97jw+J7$s^)vWFWyFAH0gX5JSg`+=i#W(%r zFYyfCtn;ato*O`AWzmFO^*y0Yonh32zZx!67o5IlygznyArFhp# zJrPi_2${I=+8;lwUk%B15ReDMS@Pyy0g(3O)%@WYQ~woECd#GIS)M;AtaUqK>#@=^ z)@Xg7L&3f1ShwK75bxWhc{%dc#pF!S`@l_~H{@f9c9)4Zm`~UFCEnmnB~X&p#zt(Z zdi!L}T93|fM#@wGtzO^_Xx^Os;@Yw11)F{K3-~+J3K+v>D_oB~;>WDLOv<@kXO)NNjS=B&ob}~{q`6MGKhO3UzZi|%l+n4yY*cc4x?a4lYaR=(>Q3fY zQS2&%u(J+uxu|54O!}hZmJ~!yUab#u?>3Yn&*%L3Grl%f9v?N8mg^#m?Q)}ilMpVrnIg7+1|U=k$y zr#cj4P{3fOH`r+@YKnT4e$KUA)GFM4p|~-7(%)5*uRn40*;I+F4x;*ekX;!J4Rvlx z-vIq4^E^)?BM^C4Ua@A$j7S>%pVSrEqQ}ai2 zEbsyQqPo3UT!x>`*aLnthkY`MhLY{cV5 z>dP58ny+Uzs$51x8Ft*XZ8%*C20?so7|mABdSzTGailFV@8z_`MO0cbc_V4Zhbr4; z3Pd!{qZw0bNvQ82G;z~DRJbd?XhD%+|8jM&K8cjwF7$VPG#g}(_!V(9;pMIO`M!|a; zwaytI3MS_3gl6Ed72k0W4TE%=4)0ys&FPh%hJfQLENLZyQ-T6ta^n~LRgi4{d)sRE zO}b|im_Y}|M&+P4OyxjA^J*U3_1;?@wRV1mzDMLsq1{ouaPNHY=&IPd+VGT|$Du9& zD3>!~4B*#NM|!2zx{<~2o;(=8rTG6UM#QiGqZpaWGVZELi8Ah+u?cugc;0-qmv1xv z(E|V8aoaSRz85M74;n%b>_m7gLVQsERAyGmY`uPp}g{P?V5_003K|l3J$(@!lY+(NJ#(4X$&tPQr z$!VgYpNn1j+?zC>LB%4X$x_r9E9D=XR@(0-nhje*4w!y^szX8yd$$N`gm>7j@nOJL@?uk>CT=Si}FJ7IdG!}Dh8Zx;KxywK3a;)YDS9xQKG=2J1gG!{Aq{+ zLLtk>JtSb0v451V!6r^7Nr9DufMe`~I}rK;HdA!`vVuG2xhCGEEY0|@-AQKHAnP6G zazfF+#81m~pUc#VEuA%{hzGR%;xurqc6geU%Xbf_NBfoZT++#iT+?wtdr=s z(+PV&H?8L6hZ<`S+#Uw=i66*Vtx}^oU0XenFC50J z^l#CXIT)PQUXi0#N_9^18p+1hnMlX9-39uW1>5_X1<7{HFMfp0N8M2kalSK8HJio9 z;RLCz;zmXa+;0gBALb_1uF%6NtM^(z^XJYZ5T=PR&V0C-Q+Zd|B&S<1@5h{nRR$RQ zN@*NgCG(*)B1KXr?dhk9M$}Ym%*wJ3iJ+}0E&|-SUg`D6D~(pqlSiY=$}I{oYNF14 z$G|(azuYUw37cIQbF@&t@w1DnD}PJ9m$=&1{q7ohCk$1%-=nq&>F_myk2y;hZpiU$ z|C}zl>V+xuao<4d7HG6&HlMRC@I%EY@LH6vYoh@7^d)*0#koYp_W9f*OmN|G6Ul*! zy>scQV0dPTU=t{nJf0+rk^K^SXzGJK5XgnVb?uDwuIg2uX{{y&J8uI=i4Ap+o*5?A zAaRJE+q0ixMnXC}Q60S!6Lm*)|9w4ZvB5NXeP?M4l|x~*X&_4O+B z_NEa{e;SGZP{v+mUHYSh@zG=CPRC}M7(sEv3c!RBk$|xbugfC*$3z~1s~u@ZZER5s zD3BqUP9`gX)b7h_ukP#00$KcZQ7}`RJDHith1C7{3q*KyeQ`*A45g}OZlBZI$<#3k zI`=WvdR~bcRf0lhMdR^JJ-Xz)i-U@;8S*MA07ri*B$V5%RFSAG3km2p-WxZz7!MZs z?cEMjSJx!K87La|_Mr06sjMJmzD6y-lZo=IRY5P}_*>901IziY{b7eHtx<4;Wgjl2D(~b6 zi%q}V?<)Ip(9*u0@GKMYV?i2@9jCdV7J|;t^)bJ)7g*=53t-Hj%eSvoq->O_1m{O+ z1(zaK*}5MV>N({eObUITWHoA(z4M^f6I2rTamG7aj2hKU*g;=ca0Q!K9}AKt+}=r? zemigdKjwVF2-uYr_^sZ!UX|n`9QnngCGcuG<^CjA)m|h|2VKDx^zzQ#@*XgK%)W;u zJEr5_$r%_MPgNX+=9a6T#bUdI5E&4W&Wk7#KrL-<#=CoJ*(<=YtckAt{L|gN{r2Vj zYPJmmn`er-O=>tS>_&~jRhGKHJ&h@_`?)zTz_7#78J65Rf|qJ6e*Apr)-NNP#wWu{ zd{zlR2zl@i2P<1@s9Hjc^>8D&n;%t2fhptJVA}X=J(dSgDcj_^NjdH4<<zg+pTsAFyGiL64|R+iund9Qp#q{_XM+g-gon+n- zgirl7zSJqNQ`11C;zZ$8FM=NUSVDW&uhi>~rD{5u^j~mcqfqjr{4O0^f5kL<(w%*4LV6F4 z3YRvTQ^*}L=yt|oy(WU*YyXyJ){r%V`bF_<5C3%RI1WQMo=U2+l6 zwnPO}m}g!`Acx6pd>Et*O<#PK+0cJ(lc(Y@`7pwMFphDKj4he*Y=5uU+aC7andM~&79K<` zAGkY{>+eJuEC+X<-Oodd=KOxR zA3dMj{KPT|EfCD76s5BEbdk#P&IlCDH<-tepu%HA?}D3h18LJ`Xu9%!S2E^kE}y?N zzk;-EdZ({%RI=mG>+H_a>#A1b>uwyynbz2g>wLa*_VXr-k{~dT>@~#Ar_eYS7P}`E zzyMQ@S@T*ZcoArn+ev`N>OlrVq^793GY(XgY!urxIZ7d=#jpFS&a1KU3wRO6s05c| z)K8R-UR(oS<+nFdJ89<~3;jl%)VUnaoXG;bv+g(3-UpAJMnVr}4i>SsFK?&BC!^}X zhD*aZr{%j>RvP$m5f2Yi{MzvDRNA8#oEzH%^$1-tm7C%BnG(I~2egAuWjyw}JTS+x z%`!V-ZKmaKu9zCkOme`w2y9zDdVVrXp$fLJe6wp}UtIgWKY$M17I<5Wk+ba(C9eLbHIQsO&i@}m__51$!-tQP<+@YMk&)RFx zHRs%GZ~ffblCf9gk;8WAk##D8HEIVew9;PP>2YE%gZ5qEf#1E|U>h{K_I5l_x%#;e zrC0FPH%|F6pzr>7Y&~0^Run$A#vso%1))?};|q*=$;F{=!!E<10b+P-8zZaqMHg6i zwK24DDH$dor?;~DlzPwqU?{Y)=x;99ZSdTeFR!uVc2sD^Yl(JjJgzVYOboctbIV{t zzpMOnx}%10SB>J78L1`MbP0UlTONi zX?)G2@-`1)2yo{^?_z+L-1bceLZK#4*u0mhMVI{pE#QnB8OP<>L3=9}2{)g!mBT!( z#Xs|*_nn)a8oTA(uBh;g9IbUP&TM$b-B?l3*;obmIZ+{Sy1Pgh>G^zfOS8W4D^?10 zaOsba&#S?g?exFXf_NCEYbc)0un z-+NBKsN;;pHnMG}(;naMm1T5%n14mkn|pTTcKa*36geG+r$Abaoqi4+j;z z+viuG|ACr)HMM*uQMgFDXX{V1s-0qXj436AHodQLwzmdB@fANpY()Av^1C(|0|o)| zzs_vSiF2LRlD}PB7jUt!QR}=0u9uQ?cO=>$s`k4d>KEW`L|jB2lkY1u04^1h2LRLj zlhaylo}Zx=mKt+kWz+7C_LhYN2EPe-P35UqS?}&Y3R8xJsHZ>SfL}EHcqE{9z^GiC zppXx!yrl^iU~!_MV>;QK?L_2t#pQw(+|6N=iE;zgy^3~oB;9!VaAjr z(AJE}*QdIIgsD!?m242OIuy&^;+PSlYS02P(Bxkh_WVGAI^p15o<+(N)|jb%;_|xN z0|REqC4w2-&r^&((4R-#Ww*N=N0?CsY@1*jQH|IyUz=9xa zbX%Fn*AY8sudVw88+)@32n-kSkOMfh2EK@$q;JsY^HlN@Mko9I=C@UunCZhEE~NS`)vKRuR8Il zGpzb&)x)(l=u!6DELRLL<(r}3BOC#mZ#egyIIePq_=ect+fxE3UkT*NsJw%t!oMGhuPL(DL=p9DC>8@uiikbPu;wT5ql%QsBG?b> zAJ&9c8KGhxOC4zQoP%L3{T^wq@fu@jL5~hO1ypf*p}tn|33E1^qC$MF+)#Y9R#(&5 zH5ArCgj$#)bVt$KbK578e(SF_yPCdG+TbhGIZ+R0h)X8`f!#KRC9eC z;0jnW+C)XP8su5yTMN)j6hG4f32wx{bR~A*N|B|d#q7N(6;TO#dlDY!t@3+rz9!>y za7*g@aE^hD#FKqO-4ZiPP%PwLk+>pPKXJdhVg=TIb|$BS7EW16dK8115&xISV>sj< zcELJ_oQK43w+09KPhB@A4zyQ*7#S|HXC*BVc^Zl)e?g!oaVMrAOKo<3uFKvHXs-y-yW zz{&M;k9F-U)Z={W$;hi@OWNwZ<*u{izMIy$V{cv7aopDhaMw{8iO)K5H!@RKww3z$ z=>kd>bYu>KeJt1b7Ykx>O!-nRTk4DGliH6T_k@@ zWi#qnnxDn>X*}OEK9jrMVp(#V%{yjFGJ_&}iRY_h$yt;3(%qU{_5qoLB6a*dBqAvV zZ!8n>m?~l`@;II**U;COg5B+>b>#}`7aA-bJ4@jX2l&G9lhVf&Vef45(b#Ah+=kpJ zKc!3H^`+s*LhoJqZ;2TPy;eV8wd+y`+*~#!`OYvg%gmdWaE_ghH^2Cz` z=lIIgomd8_o(`Xzl7CnC$iKb-HtTgZ$NkYu(Y>}R1?C6UF_ht5+H_eYBk<~N%`&)l ze3qdS84E1im4%6hs-y9z%Nv!@GjU_;p59F==6n@q(96Eyo+`QMo#*hM3oOizjz zvz(K-GsGD;8*v+2yPyBoQNGeu6IQ42`vE3JaF}+h z1kR=Q&S5Bn#arXH`L(D?foAIE#X;asy-6mLq$n9Ym-K>3s|y#Gjl}bWiePac9l59< zMd8rC^&l^k%c!+j3}SM;Om!`yT_tI{1evdspGVR&l?i=#Kb$l9WQPz5DBsJSc8IRNCf!a`!`kYyh%1dJ)2AcUa=Ukla^KzGMWt?Wqz#M4&|qn< z$=m%620>nixa% z>D>>3w8R7lpI4gJCdPq;Tq%_3#92lAGF9~n>p=uN7Hh-k3Zb2JDT;0-BEKL+mP`&* zDKyPO_W-t#C-6IAqbT#C_qx-wXziIh=Hey8@dr5j}cC; zQY2kw2Re@;D)NXlRW^B9N>7&BZ1haMxWDkL`GUNq|Ak#vYNdQT)IaqGpbev#P)<z(DW-->fK3TKor_zXJkZ*nY!8JoF2wkH-QzA^?{3Vxj) zUp$c8QTWR8g$9r}g9&$>9-2mnByV;<*gTYtHbT0H14>5tFe(&yE zy~m$k(yDHAP|D%4am>)+e&l>XpDm5AzOjoXsxqh!E&B520Ahf@AfFoohYR5JT>n1f z+Oqro-Nbl#h%IjD>v||HncL8FsPUJUJ|a==ADOI?>q`P|SjlPBk`$26Y*&Z^*!{xE zr~TOf^kw^;BPKc>WuF<8EsjN$>(yA$)Oku!&Fu_WY4TYs* z!hkQF+;R2tgk>n})a!V^NL>^57Qu!6`(nEL)+rlO$O zfLD(9CVcdg+jq;9`c?nwFm6sp`ZXWkfR#Lb=nsEMf;z&`=*XB^089Xz_M8U%9d2@+ zeHHJ;k!Kaf4$ddytA@i@Mg^}`{a+Pj5m{{i95{`bUxv3EnWO#GzEDn?a%o;V!RkJ3 zbkIYdyUAXmv!k2k>LR6uf_|NBR5UF0>Ah3y`C94b#zv&}@JUk@UDGe(5yqNRf-&vY z#1Zt6Y=W0n$gc)5T)L&cHztT+V)cCD@dtL=>9jcv`}Bu!Iid*DAny4J9FEZy3fa9M z;(mWfJ|c+_lP_?uc<_r2gZ*tDmAP}$9GuYYr|(Ht!#$mjxA$rqZ7zaasmCh??19*r zCvhN8&wxGN{AEhPeeQf$VkH4OS*Ezft4Yk(=XzQhM)b1&_YoVcTaUHA*edoCHN0x> z7)J;PQ0f9$9yq-w>ui0U0 z-vzv>EsN7;>s9E>^-eui?&74pVAJ z#{dN5joa=5v-%6LOnUqimFQ^s-(@^M(Qld@4y}+3LNtR^b>&mG|pvS-!oGRo~8G zUKIpaCRIphdT}@06qI7kP#CK;kxG>01xy5Wr#f62%9@e(VrEs?Zd)s#es1G#G)2TU zH`XQ;SP`~8gvcWC4?(a71c4XAchhNa_2XX1+Wqh3pK{e1wvzAlv|X>WglYe$x| zx-xxE(_7_nza;RIep#gYefIrv8;)d&o(zGszZV=oa7uus+!uFvKPXk^`Z7kPo!ULU zVl91&?`*^LC6856!4veq@RbwJ{QrWiwvLj0WDGbO@aNSy>tuH3ZB!?o*Ui(Gh1#}-(b8OfbfsN^4Do`29o?66jyq}+v5;%f7cvy< ztHRDJXyD{=#wJ^`=_GK0k?)c`w!@?mi`*6`4I}aLn8>uq-NF0pB_k)$u0Us18GSZy?%(At>jXBp6OTJ1;TmdjuSA(Tsx;jUD#fn3LmnWt zvVU95bD?Z%+nn}u?&w1ytqgpA)?SJIUM0;(0%0t2Dz`#>qf|X<`}^J1^#oF?wB^uh z;;Ouv$p9jGVu;kMaoy$l)OpQ*+&NWc^(`e`p%t7`BLWun&zF9`q+2_SFIE~ z#x*84_y~1p0X&Kb@uiD68Y~YZspx+diymAYhzCpb^qdO3ndN4gU02!IXOyGb!CNh1u4CC z-WbC6`|WBB7p-qC?=28FlO_jtlUf?Pls0y{&Cir*)eR6Y1`(&A3**xV!(Rr(3b(Yw z)vT<9ncR!escdR}g?Q<75JrA-t{#6fK7cEAY(tsSY0FCCB2t{@d=&C@Y$ z8KoMg3w}2dbzZJH9HH-D%-kQRj^8`pf}bVQN9PK%yIMZw)7=Pr5qnB%@SCptQVYCs{Fvi|aF07ni`P=E%=k zVArP%0#Lr_ocfi~$2wh;Taglw-YotG(@Tm+p(JWr)-Ip5R1@c_&F`=)@*{#!Ov>&B zJyv3Oj(@-CQ%iky_Ld5KOUK}HCBCag$=gi$&R2}-b@j}y;^vO2OYGp!MXQtJI$3#= zEUq+?$0`w%ee&vOmRmW6lkZHZ1v`I%X2hxNzC7RDuP(3zy~%b}3r*Cuv3_4$f&m~9 zl<}|laS(3ylokhfFJC15SMx82-DgW)`kKVNA@VgUw0av##!Y$`^%r)GF&Wgjisyzv zi@KesZ}Jz_nMpZJ~b61{w5@kvd}r+)LPRNRgUs+AGS zp{)N%x}ELa63Eglm|@0IM2fO|%T_8`skL#R0Jt&uwc=TU*8uAA+z|TBzFOCkoy)CF z_h@|&HKhjM3+Z3J_FmYEFkFkmu;i&Su_=7ObhexVz>_a0H@r47lyddN^%7voEKA|P zrAvy-NAqZSx^KU-m^B4Nx9^E$LATe5zp9x!+%+2xNarYTByleH zGEK)TX;Yi_hS0642hPL|;S(@`jPz4H0$I;1X1y0f!vD0XnqO-I}HzK>){ zOqP&q>9W9CnuoL4abln@Yx(D()zm$~Jv0r57N$Ro?(3b(EsKr!OOt&96)`sFW@a{v z>fK**N@u8ns|US!hxX+ZEqkK|#0$I@ZzU+7$o`%WA*W@u(0;zkYPLwH5(6SiF|sg} z20_x0(xk&*P04AYs``A@=DCreNv6B$C zGq0f?lV;ZBNypGyDq!GFVe2$NX=3FJlqTgz`JDesllDKQ$64-qw@GXx)LpJMS$6&62&siQ{efkyUB{Dp-&yp?=$(n$z_2~ zT2^tnVuvTOEL(6HrOadw6%rbZ#i&eP^pbNeW2N|pDG^IYqGp+@>& z5m~W2zi%TSkkuN`w|tv&B^XB0Q~TR=)zq!L&jO{?77*L)5cHDkW%aH;fE7u1IOl8b z>tpf}e8Dfj&0-9?&-&w&ihg-RMEaPPj3P{ASYsR;psry26>`L%7o6GKk)?amesky1 z)I2w_@O^8)l5yJC3p^hN*d(DN{s|e7$@GobA}#RZ(?Qjf;8yDORXpG(cVfcDvH+bl zt#42<@n0EPj7!u|pT=RU5!LYtfKjL;z|0u#PXq{sn2Kw8OUnD%y7GvVBSdSPUy83f z%$Pi%_?|`(9xrfeg9z0Kn3!IUr*heGDyc1rCSSYLOET?VZ;R7~zNcjUT?v3s(TtgV zxx8O2*cT3G?`Itj1aE)?;HulkKY! z_KYyHxQKtjyQd{A<}avLMg3MqLDI>=Opd(+FPzLAEm>mjbuX{d2*-pi{S|6F#exJb`ECw3g^0 z(#+XY6H1w>%%53?-O=0bV|Q2%Q6WX4ji@s&@07WpY8!8hoP1NCjOAf2F0`Y;)B-|U zD>nq~t0#Arr-xRn$WVg5pby05b=%AuG>ee=rVKJOoHuTAR{4~%of}2ihNMSQTC4O6 z0`Fxv)l29Alg*-K!%fg9?JcTG`&sgAxrE)T9hR!10Z_RnuiGb$%25FDOQqMwT z12OE#i+gs*@scFOHZ}JJ%XB$51!n0uQw)YGpKi1If~*DH`*(e+7A6EM<&7H17%RK| zdj*y$uuXUG$11{Mg`eH_MZVi4Ek00?v$5S%5n}$25;Z2{$XJ}-~n2*oPGgxN6>&y*`D;MZ>Q=n1a zVY0Zb#Vo{8Rp?kMbAm7t0HPT-4zy`-@8Aq+CmVIROcS%H)+Cu?*CKw^Vg_kaq|At$ zVS<)6!FWae!c1Wigy-TSv&?WE7Q5x%zeJZB z=yhXrP(%9O!Y(dn+Bcb(6ne$!4=yumRf2K%AC?AwSn#jCATpBt?(QBDCrK}f^LC63 zHvk#uO^UUvOO^=c3DP$ODs(1U16A0l~;)Xfzs5JHt>$IL^ zag`|rglo5)6+08x@-7<}S=d;{iu#OnA0E9esHOBB0tmAUfG`_S;(5+q(L?(t5$L*U zZt_{$ZPF<=dN$j;Y2B^q`mq&{^p2ET+=V4e7Cms^jrxi;d4VxjPp21!Bn}GXDDUiF z4d?y`lOIPTfj5!vE>9bRe`?N-TOWFJdD0D7DO#RXJ-2PRBQtT0l4uwk5E*jWWdeD% zd2*$?*|bXk;V|O2*(EZxS`YiCxR2re)Ft`on#A2GdwTm@dbK4BV)xoAYmZOefTE*c zw8tk7_aFgDKo3&QnqIT+ldir$_ytirN}?p+T@exlXe74RemvzAjm=uKttN;b`4FEp#!-?C|u1BE(_Yi1|;u|&NIXn*RSBrSrau%5TBX`5^<6~qco z+$T+}Mz?>#$#q%$3KcnchPi`ON{A;(=1SAdEWAHDx{**&<9z+LolX($F#xI}C@|Y` zeA-h+(jl&QOHF|2)nwmg*d8mQW7Bw@Yk4d(t#!u{dhLT#eD>q@!2ROY=C{r9Trme? zt5ps#!n?j+CP~N!!@Pse-KA3VG>MC|S5{PlXsdT6c%tjLymy?U$@+sXjUV*A|IslV zqm&)#2VQTaOp%yFLLRMukog7h?3I;Dfh7lW6`c<&pAG@KN!BD8f42*2b%-g;WoS8@Vbn$lphQR(4| zETZ&7N#{x<_vxvM&6;|BT>2dX?cimj`*3qY**?HIa6VA2tf|C8%MWc0n%f9RC=`-9 zlLm$s&b-1I_lC%RHCtUUS7NlU1Ov_t1-AQX$>oMXjLd7S?kfERHnm05aaPmP(!erN z{O4*Vb4L>U-d!)=1+h^CG?FjQ`0`0W)|{o$PR*)ODw3UOs85XmOnqT>(ZSq{Ay6|jbW(uqMy>hRFj3~ z%V{tsb*fbX>XXBi6F5eAJH@mfpNm~0&HA1NqJd={&Q&FI#Q1 zrTc=R-;ZjHC9EW+3~YqLUwnLYc4E|#|DX@sTB#Gn*!Wc&A!~^x03-2tfy3&l&<>KO z2qpW)qEv?+@aW+!r3&VT_Ycm89dPV4PUZ&jNk zuSPj{z`Z@?(2s1Wd+>^A=gOvg0;lkfPq)qPja9@>Gr*DV{3<$u&4>}ggl!dC{p%PZ zhwAZWHI&}E@L-Z`1evJ=KxVv7`7nKRcyXhVGT^=4b2XmlBAUgMFIS>ar23nQJ&Eru zU^7-JgGf_Xtfo z2Ge;lkprVUu=TBDkJ|UfIox+{X=76d+dtGPkUqsfTIBHXxH^Yt#GlM-TbSvAF25t) z!%`*p7sNpUV7SL8*<6YBNLoIj4hdK;RDBalCe~&9sRGcDPfEy2)zrUBEOlWfNW?r4`VEGcvU)=AUP>9PEa3PQ<0nsj;+06L{k_{zDOn#e|%u&tww9D6trpg$@2N zE}VATmeU-dxcDMz##5QyX`xrDmtHwr`_V1)X61+0%*w_xO(by6q>aut)HqfWxK4MnQ# z%`H1`qi%ih&~q3~T9fA(&xbgCQ28K*`9)KLH+im`TtRXf&t})qN36J}^VPn*ZGCmI zm{Kp`_#ZF8z1%E=-SC7RLJ~ePJpfTP4SatnQ_2I}<(g3NZMc##qKrS(Ola{wKvP zy`Fe5K0i=ndod?WXRa|~ZC>^aHDSnl^t0q_@{(UA7yDS4PS-v#_(EGuS%MZj7qBcEM8euhg!;zS`|Q; zVt~MGNwo+7W#$W9la|`{hg@BWqH67}NF-MC@H^5v%l3#|&2_CiI-9xdpbzvz-y#0J z*voLg+l{O5*%gZ1R($CzKa;VJU5t0NFK220b9f&~u5Va4MGvV=?UGEXB>&oY4Ddck zjlW!u{p#%})QAm-AVfFn?kZ6hUDywh0EJ_PRj}1{g5XLx9A7`ez;I}1ai&z`$(4cz zM~(cTOdUf*s;%0vGxe-&6w;gl9?+q!i2&p<#z;svFj}@NQNG+85cXRyYGhuCeB^{2u`a(+bn8mP?e{RM;C$TqIIvq{Is5U@mZjncO%0by`O zCE%~yhh~)b?Bnrjk(Tu3YC7-UM0I$fYYI#;Jch>QNIzZ4M>X>aJ168}{HgZ(OJJi$ ziDLa%wkk!LSggBD?AkogfnF3;#ddN$lx25}jW!m;N_;XN;6LMloaO-Jw55`}v{rlj z-G#0fOuIl4P_B&`>W__JTFyn|(^J9p_1Vk=G&H@JUvd;8ws;H^E&Xc|CGg$OFOut` zL@==#4KYImA6QbTBu+Kt{a2ubmcrczXfgwN-0CXr^MrNA&@Ri5q2Tp+CvEc_1{Yos zci~)rLDZ#6ua6_5zQI-LC&%4J)kONX;tv*=fP$U4hVnEMCUR?3%{vJ(`8b45uYc^i z(9k?qv7*(j`_e0BJ>x|NJ!{Q6-_duHe^n=lvB3Fd~2yZNy$-i^=B41JuWa zwEl!A2yvn0iC^6;^W?F2XqC%Ank1%Q@}M2kk>z07kA%P zJ|7z=N!WAg+@&`L6v;WJtmW)FZ*QbsFRfw>ghOC)ajS}z`vWa`I{_WGhOn1{pGk~+ zik=dxtHWn_E#3R@{d1N51CKhQd4S00EPR6Snh}lqoBxmIP(21=UO8dt4a1l%_50 zaEF`@&^Beg3`qs^(V9$~TjELu?+Yjfj_Q%P&nPc=`w%T-HY!qhHrzKgs}|cEKcp-y z&O0YbabE%ko2n1{^tUkvNlJY8C@9S}w|8LW-cCrA;$M3C0|~}QKmFJJKVm^(R))2B zx>wAA@N`SAmqCH`kwe?o8sSMS4OYM>sU%>e0dKJIPiMtE%!X|n80DDMMK+&Fq2(=& zPDs1}5I4DE{)%!(uO-(jmhTEpuE+q@)AIs?!9#PP4I@MXzG9Pn;5`%;1DsuvC2(-!53~=rP;N-8@XmDQ z<&LPvv~$JkWs~Z)+kMqteYYt_RX_IdnSU_2$p(0=4!L`5f4ab zhvoaFp(2=I3Ra`LBl*FJ6#@{eY%}d#-;Da{_ z9D;b=cWQ1Od%;$9`}m_xg)rrbBg((y|EdMFP?r4y2p;oDZL*quCNuftJ^>6R z2E8dFF&$S*-0)F&4+VCx2qEdsbZjLyCfu3Ba$Dh(ZZ6myDWMq6DPAEJtVL6R@ znDP?~^S@&_EP8~p7T|>g$U5nal;jw=4{YxW?k2=ay0No2 z_u71=x9D;q3#AXq$GW$ng?k7h*q4>(a6H&JY#XwY_qrShJlm>mwn1d$6{42{hEK7H zOw0Lcs_vKVhz8s!d5eQ~{(38T5%mzRK0q&=-(la6C+!Tnx?X{b6Z_rLCt^ z!QhhpOwo6}#+=MkOYOcQFQ2yUg`X0`vTqa-!93N0$}-+mTVns{NPqw6&}T4qNWHJt z)|Dn5Ty(BaSf*O}RVPmCo1?gDQx?MvJ@}c~<`S!&8)Gzds!Bx#=3c^Yzuz;;Vss-D z3=nRgBeQdtTvwPJ_Gw9=NIK&S$XgZ};d!~apU7Qe@YlE?*ouB{__FGdxKrWW;AdML z%**9V8QM7K2ivPdD>K`rg=a@3D$+z^BnUOHf`B;<{M^B4R~1XFPV78C^S5>i78f@+ z0oY;KZo3*G9-w=Y@%EXTb~Ru*+P*qgn)SPpY15u4BxWVD?dywEBmIO^#IGNRyO*$! z#7ap^s^;ldOqRsKgq=TE((??cCR}NhfD3y6E-F4?Zs&QXD8goTB`uHxGai^rCyKBO z&cPksZ~SX&ea_Fr71gBgckXat=0NrZO^3`_@de7Pb(^M!d~7VGboQRQC$NAdDTeo| zc_m#Ci!#Uk{Ivl2E;N0#r^G*gr<;QI?eHBosSb!BbJL(??3tAPh3lnn(|!i~oM}5? z2iZiRyQ^Eye8` zPxWBe!T(DC)pIlEPYqgf9VYg-PyVt1hoNPWn^){$Du<0)O^ehhd}6%FE%GX!ML+-el7J zneF1fdR`I}Jq+yqxQRLH(^QgOs6=m(FGv9QBodqL`#%{U4k1?qaP(e30a(Oq23nMW zuQ2tVrf^FyLgAL{2srGNssrx|6x!*-1K}B{Vy&0o z%2P4V0SD(T*uw`dw-SaUHCsQ_zvjyEJ1VxW7E)?g8eiVfvX-Y9{fUHt5rP4S#=K&; z=qKfR@6jL7px4_~OcA(xl~lwyVG(Xif`vP5lN{}HX|P1(VSN0=hO;5!H%aKxl%5zd zi%D$@yHip9Ak3$^@ZLhH2AP`7ym|rQKm3Ju!V#dh z&HH%qNe}(#_Zamy?t<-|w*w|X@*(0Wx~sT3Aumz-ALW0LLe?1My5UQ3LbxTM9n~h<= zbH^c34E+2!fCdjJs~VqoT|+%6Tk_c(;~6Vinuk0 z`?S7cvY3rpb6i~^tAb8eXo&RVF(-?FT%jn^MyloZ>1-uhG7|%PI=1#wKWB^E0=Q_ zqcH0Se`9Ot)WVBwInG@U&y?vTYu)OSK1$<$AKH3e;zR;`(g4M zUZG9!q>3Cd1ai_;2pSFp_9Mr>aA}44uXG2~T)s zf+l_P3P_lw!&9CMl=<$Z*_PWd$}#oM7*kstpFhY4?A^X_KK#n&(wnGRqVk`SOt8SVfhR6(mNUmw=;rnKo@h7LB^A7~9PE^{5i8XXC{T z#hZW1cR|TA)ea}B`fmjMs?phb)!Kx(RJ{XDeOq(YH*i(Z#>wN((#La!Ks~2ZxRgca z;A)m+VHH*R2q+Y-TDBR1MYY2Aw@_>nXgUFZ))JBZ*!r_4=zJYmR)N&;{{b!keG?te zfSKP@$bX9PUBh1_X632gOoj^PYuw(6*n|vn_10#Y2S08ylA#5oh4tjk9bYUpD8KM1^dijK4O=IuXu&9R<qe?{-fLnbGo&LvwW{-QE@9)bq!l*u!#~wz1o`25=)H*n z({-oY-U28c@YlYQ_xuFg)YCcwXTj@7Ba2yAw>M`l8~XcnpS6?B?GRu`U;b7f|IZW! zU;ywG3*Na@q(c%G&Aw~^k$l*5H}&^$#dQm=6L7d?qnfEt&j@fgpc)|goM7_&(N%ve zWCR`w76;eMV2KLt%J0MW*E%$Jo`h<% zG%1!E@m6!{-1}!3B#K>dp9Hc0vo!yqKA;CT@EHaClNctFm1tnm-~HZ(m`r=ODi)Lf z=QM$-#$v^-vsyhs2R-oO^A&y^fV23DZM+Bb@N);!$@vfRI8Jk}dSHLsQNg62{sJrn zpq2#XSl(-BZDYeYcc@l}q)`v1hsE_Q|L2=#2^edZ(p2gqh9kcE=T z;Umt$HXxCzKX#L_$F^{(DvVSBrH$G8?sVZT@Ydg%(wHOve_Iv_#uV^b;Mw7rJHidH zzPkK`?I^!m`5O4#GU`$u=kv1bKQl=`(wewUQb`I>cNdWDQthbhOd0<$L)JIgKgD5p zs0vyKa)rfOE}tOf3L62hKHw)gmU2++96$cHFU#O$vL$g~ffU|AG`W=LdauUp*3)Ay zjB-c@*eJM(qqzv4OOJ4>g}w5VPXY{{ePlX_h%784{fOka;ZpGSx;xI%+T1EIKvccq zC7JXkwn3E=wRd1JphT%!1io`a5U{GF>}ShpHL4TcxVQF-*-L4f_iioBy<|3v{9H5z zu+~It#&c3wqB7kf;;@wQRT<#R`9_dR(T&vj7N>@HXwcxY5P%#W8kzS0M-CjqFeZ?8 z#Nq$!>h{kc0XpaKRAub)gRQVhQ+wgB25ke_7GR!nJ@0BqA6I!+3-zM>gwbN)S|%7g zCAl)HmiNEk4y*F8=I_!=^HGiQd6+OZ z?FwOS@kXAs85KFX@yPx^S-tnWu25JJYH6Zeyk>NvWDJ58^}E?Q1=OFY_Oa24G}&-4 z|2}}b@g$;lmufz?xYpiNKSHcKQq*|2wz2n~`oqHP+M4_>Fm~~1uLn}$=EX(tVRwzg zGD>v!-mA&JWx7UUxOUhMm-Yqzf14eVKoLx$#|XJPK;G@(eFX4F`08M3o3qo8-aK_+ zdi~a?U_jXIQEdl+;M0^`c_GP&=md}GhFB@e*_1`>V=pJSfev|2Pr}Eb`Q<-Y@G^$j z%FXPD3Hi^~OxKmtoBxPeJW&I}E2P zbYS(&_)B2^zfcf{3@O46pORqg1HU3{q8LoybTrQCS^|=3*xdWYd3Jz!bp!}lepR0> zFRKk8t6D)Sv`J~9lotB2uj_)u1v~zpz-v8UFCf^Jj-Gnqb5QY$nq9A%o}WXkkbhK1 zsTA*L(6JjQz;U`WJj?nK8@&aI$(kmLO@H0TM`-kW5+sS`Fq&8W;3o4-DZrHl&LF)~ zXSAeE%zjY04u?uG;Sly-kbGFslc!D)%MFPW`&18m_A%yj3+6b@BYk3?@Q3_~$5(dF(x+jm< zEZs`*+P{iS?F}DlST`94QTRv@god*V6bQZW)sX!kiwkJCvOm}YmrXEh4ayDE$ILX9Fb@ALUG~P@ZW?%zF zlS+g;f)M!|ms#JYwMF*1ekA8g+FPcwoJevKXL#VGH@BCi*dw@mfcJ$ikCsp-w_MD$ z*-BoX9+=AQqwWjAQU$Vb0Gc>MqKlWDyY=N>?|8ne$L8&Kdvo&AKC1R4*T@|lMV}(p z|LxtM!)k@B0tZRWS*~%f1-eU%p_~OTX+W*Lhe}Atibha2eIo`y%gxw{H#3k?dL|E) z@a*{L)Z2D^r=49KyPn@_|L{n-ou~DI{*r+evc{$+R%y#+Hk2N}f<5ON&7Uezw?8cqD#o&(K@>2t5fh?jpJYtv@?NA8QqWzT-ZHd6g zRW4j^Hab_!97+MQi|O3nia(j+mZbr6-jLCI@qfhR{{SFHz$hd7_Q1b>NfN9B)bKj} zR~QOC(MYA}>YY=^q$FJteaIa|u-q{%7=HzvE*`gAz48{FuJ}U1d{)j}!X7@awDFx$ zykOoOibA`kHu}du?P}(R)DD@kfSm)P=<1e=tO+Xch%}6i$1oefZ$foQ`m$OAf402$ zCpX@u*{~pcZ_3n#h?3O7m${lsOsgk)%D8$T_iH&$2TckAB-_IV5n>M0#+=+`^}Hz* zDOj@LJRjH;rIjFy`Sk+gdN1qmQ<#Jrd1xDt6x}NQ`yiB9qVDGZTXN)uogDmbw$}3g zWc_zZIXq+I+D+-PO2Ztf^-ERpI9T=Z(#Cb@d4yHA)a@T{pvu8E#tuRZ;Bc7HoBXdc zuylPR^K2tn?S+lqV?-@%g`z}f@8LPxH$FV7F@VLqTJ4RZA8-g}?SmkRH^P5YV^-hv z?^h#Br*@L$$d>o7I+dt zHWV4XI(ecCbKL+Y0eeXokJGSq^W5yJXD|Ldzc5ma>HqTlFziSn@+sSut`s_>6AhC8 zJukszr^F#rkvC_K1$_23Z>aJz;0(QCd~yK9+0rJoY0X=1bM@B^HU=QocL61UNrezn zIi~g|+evoYO%okBn)D5Cg%!`+$x`HBwenpPnD~{5%)anwU}G?oDTWOuL@dCG6Q5L) z;#C&>*7@u@6f2Z$m{45nfg1fc$A9unpcb%NFd{qd*39x}SM>R&_&3^cEL6)xLbSv| zV-!HDHICo_Z+;$mS1R)DpL}4$hQ_Ov>yK2NpP6Y_9uH_t(JmzvmHy z7A(de_>6a|4-!qpp9l2iEr!v=A*KQBU>4)C`aDO;P8Ovm6+<@VQMb=1$v1k2gx@}V z5_kGv;vWeX{<+^S_7w{2d_4nt=YD>!ive_gp$YENNGc#NkQvp@vJ15I91YIC1`LY{ zH+7p|rh{uc)*>8qL9D{fZ8a(YDYD4%yN}+gPZv+kNzBM1NE>Ei!2k2Cum`3BSjRN; zl-|^q3yIv2CT6#XQ2?QEf%fopq&K~3M1TM8-xy(k41m2>g_=}H&OJWTErIqKqlw{| z3<@eLk;uHcV%Ud{usy5OieVD&#bc_9B>7E<%lsh_yR&zOy@xw02o0{+-;~GCHVSg( z5C*&^X_Pfgc^8zzsCq>0A>?^h-kW4bueTBZZI&1eKw5cBZF9Uz4y^m_u6u=leE|rN zq=20W0pn19#o9Hh9MA`S1zfd&-91T3^62ixk45Bl%!lYjj(+yIh z^@5ww-P;A9TnWb}G8Q)ltq?6S&=?ggA|l@b5wYwZbzz&?0}5^vmnlLTE=8Jg`|x;QXro4~O|bV&xDPu_2j}6-aWD*JgOl6|K^> zjx63}kmU<8jXTH3(a@M6Qf0w_uGbMX_D8Pchv^$J7fspB?%fF~DF1N}46ZS$d2_#pmT{HYr zl}V-6?UIuyU=TwR?3L%NmSvzc{Citfa#hcdT81of--;!i6BEI>O_5-_^(lnNBNZ-XJ7 zhe{$^grh-iy~XEc;%UHtnSe2QDN_(4vN$S1+c0jFp7E2L`kE3UK6*E%UDnV70p-m-aab;>JWX-oXagDRc|1t1b9s_w-&}<>a^M1 zVwPSgmOn@;%rR^`_c}2-IeBljCe0k|ZStF_+w?7q??Vr_6hrxd7ZnMI6s_P|Rn$hQ zW*PJeQJHs1l>_)yPvL+)_Pd(RPpP^<^vc2k=66~R zu3=EiI>>@Lo}xjc`RrzWz@h71-Ya;&1rqh|^`@ED46mPOQgQXwffCG@$w50h4G3iNKlQ0iY-B3zD4NKATL*xoA{e~ zpp6Vvax8f4)k=EXK;@f*lo{rJz6`1)!t!*M-?@JG6ex?GX%@)vj-XJO2TdQyT|?~G z-C|yf$rwhC)R>o+kgU2IoWkvSX(P#t^d)RNGSzQLJRyt1zqdGjYs^TxB5l>9)4Pi78tKBJR8CC&Fl7W zp8x*jOVl>oYh}=Z`1$)ahPG$ppFH(^=4J&#n~^t`V2V#ETf-rL;sk@*wz_Fl}_^706wYFQ@NO)6uuV zE1kGd+uYGDmPfsev;*7FMZGHOi}XPDAGNSSmJu4DKwC~K@5D&j?1&$~LB;vxgEHTV zGhGVZlJJc!mS!BP(3Xowbnz%27ILu!ix7#M=}9cL%a@yP@{Z)O=_-9x0p=D+b*brt z2t#ZIc|NYTPEr@KmF8s;@G^0XDH5pSQA-@~b53u}Nbcw8d8nbkviST97^K3{7BL|g z%b%NKgL8Y~;k0|W^UfZ}A!>AuQ4MgP#y##ot2;=l(;%)r_)D;?iuv-~m4g=Fp44e_ z#6?{`=i+)Q91#@YKoKJfGv(Kko5O6yxkOqJC;~qtOJoMv3@972NiNntzW#W?<)ADm zc7ZyAKZA0Lt|V2ybFoXKlU-e@pybv_C<+&Ta2tgogz%5LlL#v#u{lg+K-1bMkWW~) zdi23ZSp^!Gi zFT{8f^58l|K-o)mn0?Uhos~W_sCw+%&^P|+LuE$4v;Zl10Tp8%Y@`1ns?!*N=e*_v)xt?4zbl_uo<&GIi-&8aO7*o#`zO{Aj$& z0_tRl7PT4d7=m{Zm2M?z`B$U9<=9u7zlM7|Xr;QM@U;+3ujDBYHDw^`}H+4--8 zFGt^fPxY0rirN`!%8#VF@&fKOK0bv5%JR@9sXoB9!p-CAB@i7Ii zHAoNoxWA05-Q{ij0)~8m3f-z`=Al!k!Er+-=uG}UAu_TC%46N&`xc|J$3#THw!cqH z196l7@6iZ{l(^N%H=SK`T1zr*AOVKwARh7n2jj}<$)ywQOW+_4_}`H|Hq^^5**~)f zwX(vMP_*uAYN#$^%Dt@!B+;iR!G31NBR2E8{nx;cKUZuA-U4~JMEm>1gVUM%Dt|Wa zVR&bSb(|(oh((5$h~-h-zC!P|rsfBI8thU&bYIOdKA!-lnB|8qJ_!1`h2f@9;u*+| z)@^HQ@0ne1-d?8aU<**Ct`7bX8KP2iHK2(Vn?uc;;+6axhJSz3BKRP6MhSmIF4zxv z*R=crPv#Kis(=j=l4A_|?gFv^`+ph9r;2SJ^(={lbWA1_@K>h(72nwCDWAjqSB!#fk^KVSTU%`xIHXcNmD z18ss7DahZv`t=EmVg_Ds^N!>xC}qGx3Hdjm!Qgf9Na-s4Q2g9Bkz#ACKT|W2!d}yB_{>Sd)E2z;om~pDUEm@QC_AG?SB$@OL%1@I31ORcAa8it1-UZN7Qg+JQw zHvroBl6)^4+Gc-DK_MW@X5KFZF$DErV&B5rEx^;$JimTB3Y6H6`|;o7`ejpB1i2*z z`^Nodo`}!>oD3iNu_H6QxrO7$^5`A=+6n94wFmtP$Xw?*ylAT-{S$0l-%9^9pq-+8 zkPZM|gTpx}NCsn#;-~(pg*{254DDo@r@L8Ut6M#w^&p4}=fe)mLykN+lK>fnOB|J2 zo}6zxSPm~|Tey!>b{1Y3-C;i(e2DE36b*P+mN=eFe#FsbbMjPJ&c_|_FmPxg&O5YY z3-)0z!nM96cI0|ivVsb<$jG$YJ8eOIeP($oaW)wgihzKXon({2|44lugh9{s?(c$( z)*TKZEhu6@c+>_@jdi6}RCpV1=SMim-#G=^%*#f`^Az^wG(PR_Xazlca97Z7RafFd z@ge@Y;TC6cP)i6P%qV^GwgYHWyrKHBp_6JPzQol0*;8nC#W}s(u_&f<{3-uM>I&d* z&x|&{h&j^8H@D<0+l4cT)_Rtc6)T#83pb`HseXFA%Dl>!V{IrP9?X~3hxUl~2kZ)h z&vnD9_D$a3xdrPeMyV5Di)Wei9c~#8#zf6eHVk(pxk9sLqM1G?OA73_Xj=enZuWiR znv?bgy-dtXUJxfQ{6|hoP!29vf3W{*YFT(!Q2(e+l{c5ln5ow69-rAu#gL2(3NBXE zLuHqtR3%+qn%iKWG>Jl=`-%e2nf7hvBcp5BVZ0Z$vR`_>kqkPd&&{Vg+-y3>XARDv z^{-#b5$4(F4Ql}1J;R(YSS`Jsf7SaL2q{sQ!oj>P_oVWfV8&o;Tk7L+Okw1<{pZOk z(1c-FyXn5zE4pU6+Ec+`{*>;Ns{oh^Hvj%hQ|MyTcFx@P#jK5y!g;b+UhnPLMi`w%*n~0^1PB|D!IlEf@_M*j{ zlP(){(7)pKA?w`Ce?}{rUp$0oxBv3wgDBv0ytK$!>jRKYCBgE!`kSv+>|v7Y(qhaZ4Q^2Kh5+NClX}Y3P&RYc~Z7 zz>6SuKLG5mvZh=1EKV)>wY~lMCS_4OiG~admu@*jHD(oXOut7#0m^dB9)+d*9KPjZ zI4~`Om^k6HI4(Tft6S}qWBlM=VeZh%va1^f98&~{*O&isY5$*(B)o?DHuz2{{XPsBiIY&F zqs(xnRjC2J>3FV&{Zj+_%L4(Rj<%+Y+8Z+#2PvyALjNxN&q))ng@%1Ls?fnI2*JxhC=MD9RwF-! zo*t@Vwi!2ZTf289^j2Y{#z9pmBgwAm9Y~G{N6o=*&lkDN4XP2VwfxwVU()EJxPHcx zj~C3>5(Uj8L% zZwx9;CNo;)S)@7d_+s+Ah&Ey_spx_y{E+{wDVPOT@1if?XA~VJKh~qa?=+?iGTfir z-uaOWf=dn3HJaPbkglOvf6Wj~-EE@St^+3htbs06z0d22NNVg|KVv+J(VBlB)H2_^&2Q%Z(_D~QPI64pzn{jqhrzuFs1Gr#$I{PxbFGvOcF^AUGb1 z+XirI`4AfLQDlzLmo(-0n~)gxH{V9fLpM>Q9O)ce1j2 zq;}(3Cgv`8ZBRfAT+3gyrzl`L<7YbyIWwU$4G4F@S-E783c#K7VBkra*MtBx6V+tx z187l$+BU;D%QzZz?#Au7`o($NAf{`Kn^^=t0d|u*5%n$ImhZfF68zd})GbUzrNyK} z$N2!9(_51iw#Y(62|?z(7r-Cw9kAy*d92RAh>>b+m|Jp84FXd_n|(?SFRqx4fy7@t zNC0+p%}pHk$k1&NAf%ju@QnFClj#Z+<1hk#fx&*9PE*9Q3+#F5#M-_a=3i>j3LP7P zt2Y0$gclqfAYZM3ex1q@&n_LWu~%<;9kM_{F*N)u+N$`m)M9@C7+m+N`EEneQZ8Gs zNi&OJ29%1-0ajoZhulT_RPd4-PKp?HClGx`eSs_%dNH*rl(_^R;EkN!lla3*5_-<# z9Vm^*E-Cy%%qobQu)|cDEv22&nGx(W?k^ka8`nyX#LscW7XO%-lu71?W(^PZff*}M z&Weyy1`}=^8AG>%cyNA}ap)$pO5h?VQBK#M66N%wC$xAAIZ-3Ptd&y+mm%My_$HF} z1Q>hrO~Jbi8Zqj3KV{6sa_VSxwv-MfgmDnoNqK7J?{Ei4YETfA2#p4ZYGueZJ0Kr4 z47GvCH~Y08@mq$sP&RzRqK4iFzrP(<3E0++c1b-xKk{V}>_GZUOJMaz|E-hSbh;qF{IK^`=4BU;rXOO{pY2hko?k zO;8-u(^JIKbnJPkR0C3+qdu*kBsv5Ahz*1kXY4GbIKbjhfXX7x=?gihLBQJUkYl)R zFbf<=dEUPt^?O-~a3tjkkETMy2o=Kdfr|i8;w&~O`AsIODll43BL?-Ecwz)DL6<^j z@C7auiv4dK@r86GH-V7^046;i0GMQqLZzXB=M6alT8WjKYp$K3{s)Y_L4vFyOR(R^ zbPoxyRs}b9fu3RD-TpTd1D5f5@5ZaV{%fGp*Z9q=S8pZ-)bI52jJ(eh^70;INdiY3 zi(SBh?>SmgS@*IYI*_X|0&+4G|GcG~hz8DjVu~0*Vb6n%G+s^;-2Ob_QW6Ed|wZK+`rly{Cz`R zn7xt%8h^cI$TzM@+MrJv#q{(7DbJ{2y#B3lK!+3 z?8dq2v;IP5&9d9N8PIHuy@7QhNb%!^JOwAv_Sij5<|=z?k)iL$T?-`DFku)LbM()E z{U*ilpL5#H9^eU$N1Jmenb+?*@cuQISK7`KlCRN1>>TZ_PUULlWq#xGC2w=H4M)p3 zG^euXW-3N%gt{ zSQbQ58uAN~mU!M<7pslND1Nr>e01aY5($h!+a;rQBKhAKWw&%-^tiF|zLRgJLO=Ls zmT!DtxGT6Yn!2(+WqD;qRW@}R17oC6iVBWCMYc?MJuQBn_0F=Xgf~3Y=Nk3T>e&8c z&)r8!WH0^bj!oWgxF^#nH7g0Hro8rY{G3Ni<=+q1o?qFR*6IFS?LQe=uKV*j)n%D( zb$A*q5UJ1Kz(~SdICU6fqmu?*NLZAo2w^pPIYglrS(qOoF7UThYtG=`dJnuc3#S(7 zGnd;wm0z{~V#-I+H_=1be~OuEK+$)_qDy)g@8i$_T%(BdTXGRK?#91#cuXdRur9N! z=z0Hdb1i!>iB^9!pWWkpmY!4*ux+r`{f3J9xW-`GX>nU0qFWu$%JM(W?^u6N^I^Hgx2x>ecNGM16PzI-ygm3>pXLELQ z;*FX*Kq~XqI}xNTefJR0(Tvv6x?(&7(L=D-H6Ep?dQn6lA1M&JG6pXut{woOsslF> z;6fF&)%LQG8noRBn6$Q&QdH3H7I&hQ09Zg$iiv%}O{&0H0m?M_`~DXLk;d^oY5%{A z9zAtOY0pf#?X#|Z`}XgopQ0w!KOnzp>f!l&Km<|*R-!`*0%OVF<-IPK+ki8@-#56`xbp z%ZRSE;Kf>D`;S~bRE1N^D;OfPL%92@ULZ1$m9Q3^3ij)IG)^tl^oBe$>E3Y?56HK1 zYOg!>;n~cMev)b&2t-!iYZh`Sv*vn0&x4ZiDH9?ZRH72W#*SUkQYW)93}j;^0Ql5T z2*;rF@Qk+A<2LP?A=O9KQweOcLM$$LJcuA8M}0gO+CF!lXdzu1HW89qBrl5Tq6Y#y26z9k zvuy{ZO19urN-|NLVQu_&j3(r@jR+l4N@o~?2Svee^!6GukqS-#LWVX^jm4zA

Y zlauyI@Y@WHUXovx;3~IE54nq#{P<+LA$%wvrxx6#M4-Q>8J^ZM`Bt$; zceMZ6T4}l8w7%KFebrw~XL}Z9WPE2QT8`w|T1#H7B;78;X~8~q6POYN!hLwAslZiw zOQj~rnP$PY2hn;(K=_;-(VBK(zg{f=N#Q>~n0>vnrKq0jNa|e|=Q%Qp7lFIMK@&2O z3*W&jR*f$FF06tFApSCp0%o8%~MoSP=Hqhm;~V^RK1?=8YWrA$ncqx&ogF z%HZvw8k~hsY<|aMr|16q_7_eUa*;kCy<8#<5LE0ynTpa9`9(@8wX=lth2ZGbFg; zM)&nbZ2rcI=u5WMPBhSZ8QJdULIgAvdlEo@ zT#G720Uo7{RJ}pfuIrm(5EBU=_*~EVbsR z-;~yTMx1Cu7_gYN8#^jAM2eQE@HzDNIUqA=&3D(<2Bg`DaGfIWY&Du*Z7 zmYQ6fX#V!?n^RQOPUQeJBlg~r0PPq@9Lv2U)TE9NP8#d@)OI7QO!LKcqBQcxuLmN( zDP8d%s9O4Z#HM6Q;S4LHbXn=en4TpE)^{nU^> zz^B06xV)EyZn1kcbD{&^+<)3$sm(N&>@2Q|!l@lTU8n~ep-iLyx|ZaZerjb8TNJ4a z@GcNtNT0dUQQK)3Nfe(7R{AP3TZz<`6Aif*RN8VjlxdzvRXDrSg}AQiV*yaTx#Re% z)n7k@EfY%}%N(qLn|7GTRV7zOZI(KCu`ms0O>b3K*n0P(2I1-Ru)h7h4^Azvgd(#^ z7)i}wR#3BId#q9j<33}mX-Ru$=R&xMzfzNeKh{{!zG;Kf-0~@`m1Qw%@pDM4VCURD53Ei>q;D~Yb-_F(6*G5)QsAJt|2Z+?oocdK#bbH+O7 zzWo!KHi1Z=6NgbCX8~TdF0{WRa?VqVg!gEiG1ie&VX+{)H1c3=9Z!6?V6iZg`!pTEjI0=wRQ?ko88K$x&Ly*L}Qr zRs5QXb57O5CJzLy30r49wR5s(;{!d-`r?Ol;9*DkS~`%YMSlM#Q4?&bB3^S$?~#zo z(MJI!_3)h)=w^hCUP(G#C#J(gqOi1(s!gB$;A8Y7RQ!6CFLL!IGi!lqE=m3guU|961h#lKcubg4~+^qeG z>sxC_PT$0G*q2T5JhqDxaH?N^>GRz#_QisYSN_@_D4{glf&_QA*gs8^L?3EwzycO) zo%LWtaQ8su`iu2@H&%vBI=XtkaU8Yv|72l#r`U2cOg`oU``0?lDZ{U)9+q&U3O?Ls zyZbM1K&ykxqi@$E_0)j3E6wTnogzeed3xv23;NUY;m0?FwL9hYK)SLa>7N^vQhw_7 zw@PflHaa{=_-(!ENCUg_F5@6&+P$v#vPqWi+8*{m-LE-2qtrLX*be@Jc?UyHk5}}*y+iOVgCci3$$&UTbDB0RJu(LX_olPx$iIqW>zCvBfnwipc6<7pl&vq^g07m| zAQtGdviA8mE|Dn2KM(((1^@3j^AC%DfA}8>|KYd&Cr$moROz4eAEHIA?UttRZv%hS MRdg_U%65VO3$T4DwEzGB literal 0 HcmV?d00001 diff --git a/docs/source/user_guide/images/sparse.png b/docs/source/user_guide/images/sparse.png new file mode 100644 index 0000000000000000000000000000000000000000..ce5c9919ac6c78a5bdbaf4444f52c43cc81c2613 GIT binary patch literal 372764 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

J;^M}49 z(ZSTj$TU;v{9fa3yrE}taOZYj;5hgb)3D`i-KT_{H}=bOh+GRW^JFSLJY4izl$|P| zR2}>Pr`o2ub%`CKW>E4gZLEsXI^UCOU+HyD|G<-#v27*Mk17LqIKw=|ee`$b_I%5O z&2BnM*XU=90*RG!2!SZ{V+v+3m3su->E{B0u)Mu?@fTd;cZHOS9zr$i`>DAL??~v zdRDZP832i*B_#5Zi3LUfokS7(CcH1B7(G^08ND5;f%H#LY^aRsyb&RvpXtfnbEsZs zqdZ`FzMfEZ=Vx8?^KTlTaPO)EhNZ8@Top>9u|@AIY^xVgTj$6$UUUs0bvjC}9e)pU zr>foP{mSGPBJk&80!aau=6fRC`x1oap@I3L4LZ9|Z!-F^EG;{(6u$_l|LIxfyhfoC z24Szq*7~!`7u@voAF$;PLVojxm%NQhW%euzMbxf)-59Od(YoT>#tr1jdw=R8i)tAA zie^=@bM2TgI7fi>m8QAm{oW)<&l+ak;dYH-Y3&iMHGh^8t;Og>Y8v;q z;McfX(UQX+x>(Oma|06+gJBZ|MnR4Zh%CDgp9K0&*m}V0OyAN_;_wy$lDp!YY1}6R zrXsKAXZx>XO#;O(1txlbd^bw=n1Qr9{NhCw&<*9e#>W8Y1h8?hBQ7-*mi|;|egqkX zTs0MKBASK3GP=%3D013f2= zav$#>0eqd0nVnNaHO$8|NYZl!8Yza? zQ$=@Sm+NsszQOh@^B+(Ymr&an#UH-vGp^#-;k8+z$r^rg8QV7zv{?m^6h#Ijo3up1 zQq)6hwEoP(bEe`_DFLt#xr|@B{g|m7XJ|n=LU6UtrZ3ki8Gb{~a2E4RO|ejh>4fI+ zhCy)No|O7J69OVSudzda98=f=psQSqv1{1f#v@=kQx@x`HmozI$ zRW<7ZuKO@aY7|Ou5a!05qZ3L)$dw$gEsnWNup4+wNF3kC;>(kTQCa@P>}`%ui3$Nwfc@sOO%5{(pQB4Y%;Viw$X0UKSSRC`(!!nN`4?-YuVLd z1>Rx*cm~rbmi5sN0&2fM-R0+xu3_cwms_eQwlj~}1gh&z!mEzgak$F<>ag6JAwQTLa_?*oc8_(iSP z+Ce(|0~5aKyf7kdv7PFd7um^e7X`hAp&thca*|1zD2b$iMKfoqm z*Ka|FVcft6*+jnT?v3;SwBFFh9RFU0!O0X!y6Z(ZFhX?e3_(H_E zFUFne(qsR?%a`ndq71o9C)9*N)$pN7I5q$_lZJ=wHLr0-8du*f2?yCwlbZ^{W;iCc z(#HChH^Tz9aptTwG|Pk8u;CS!k~))CM#K&v{Q)^DD^F8 zfeh}=KV{;fxW6`iA|G8^piB{W)Py(3bbReOT3iW_<Yv}{4HyIZiV(z4^r(ebX zhK-*+`t461YuEd^rm$y%liEG*tkN_ehY_>>Bd1gcxkObu(;mm1SRPz#wZjN>8?&(= zD*2Zf+I89z$M$>2*H*Orw7OBWHGueDFl}s|yzmMP^arG3`>WZm zReZBoskl8^47^jmCOgHad%u4q(*BDIYA8qiNTk8e`Z{-(28#9}O~>+kSnZN= zL!Te^rYZwotc)gJwfNA6WlHPzYB-w@eluYtewChAtxSa072KahLl`j0+y7h}FR zw1Cgaf8pgQINucMkIxG>q_eQLv(_ghkpDHIdk2dL9i6hs3{6hJ`~~_7&FYvkN8y-A%@{OIMEvyqN0n3^~;$pi5^US3JFwnH!77 zs_V2+j<_H()s3QfgfAO0;Qc%6POE|X$A(?jHlKlk&Dq3!EOa?+B5ahOIfTQS4fD`U z-ybC~qgJUWY4GFLYC!frS3ysp!s~hOm=9QunmljoIXF{3JX76p-4sF$ z?ldZ*j}G%3p&wM9XSQMc-?KGzAPnuQwzRBAxcl*!ym<|p(p4Tq)Nwv5O05>EKS4}P zJmtlXMpAP+RwVJDS7ANF1^60{tm?Cd6)t;0S6YF0&l^*iV?GL|z*k};K_s+5+o%ig z-Gy}o;H#^acqCbgj8H_jWXND3XtH8w9*JT2#&cL*kBjKHzau4^!z6gHIt}f z+uV?}&n(6wl??P4IlpizmadlQ%Oo{BMBYGJaJ({Ur5tcwJJhOEY_K;3Z*+5z>$xKV ziRnPpRQ+8^21LyUf^cq`XOaHjqox`mY8pP)?UmPlGekbb5Yg7{CkXu4(inEQx=m; z1`i*}(q@k*hvx*Ohkm&hRZ;$Nh4_WOujBoPea0Bgk~>KH=w9r2Tec5iy(_D%^oggJHb*fo1rj$bDfnozAHd z`>)i)Y$%+JDTDNvrKO;>W7oJ1+b!zR#*l-P5M;ON)J?qU2nEUB*uh>3c)^s&?y#~S zCtd>IKH~JcB(eH(Q=yQ8rKODMPPYUUUVBK_5h=<`#LSdtvSEs#e=*PFRsan|)4JO+ zTj>RLK9zBysdpFv6R%=?GVS<&zve6dPz_2yooy)j>X8=h)aQ&*_+3yCSU4Dz*~K=o z`6QN%*n!)6v@!EkpqaiQ>VAlb-oT+GF2$b~H)%GhBB0HB?7UZfGwaH=wxSh{E3ujF zP`7eue~|;^NSCbhblBAu@M;GvteN&5t39@(Cvi9v)(D%q#H!CkSe5IY^Q1W~A-&q{PfRZAK>9-=%&F%orkXM*n>-7rRxUJs zYs<9pI4FDTHFQje#@g`f9E$Y5I{EI~tRVnc0}U=?d~a?LTwILn{*er!0|X5CExEB6 zpe^8XGeK@e9gboCHrD*B!u#(A9j_<=ET->zJJ`~EL*E2|wHeduZSEuB^|iVk{pYP8 zK;peE^j9{E1wwGV=Hm(9G(K_hTvW^geH-0zN zg)0=zUPUe+0HYmfK1WBA1hTcJKTTEw#5Klp##Bs>n00hIh@Hyjn4RpIH?2RL&nOph z+(8v@Nh&WmJ@{Egg5B?ORYTo1yXkYT-RA2>=*m8E^o|E9$WDYMxfA<)FpH!iFm=pG z(wlyI$8nX8F&z(_nWq$@kw;jHI3N0v{F`~2hZs;$d)2@LEf$fRF=P{-DP+Rqk_sGV z1nDb1j^fPwgsQ}mdsRe{8z+HSiAdhE1mkYu!UfLTe1ii)IX~he9tb-5e1Aw)qg%HV z-!xL2UT)O8&PzNxy#P-@GK^4w_Qp~ZsyQcS$a|+z$gTscBc^NtHm#cSF=e7s68|Sf z2*&GdbdqK=s}mwfFxLJUk~g(hLiUZ$%W6am(Pzq%1vK|;arnaaxSk774=zYLEM

    x(Y~w|d+F|+Tq0@OO+!dM?j=(=3RJ%H7`+s(lw26`5*r&np zCpZ9Gr_ntM{NnsYV6XH@7Ddcj0oma=3BbvBQekrf@fp7jlv^yQ{+R}z(=2|%9tZHH zqsRo2vSqL80W~fyZkU}xlLPQKMz3TAxYN(mK>?!@-M_lUCnx|RA?T%cdt)ipGW9W8 z?8J*^5RKAUaK;R!I5DmrMbc5(5>nsi#q`CfL6EE7_rE%2P=s8wIfM7fxfZ9$1MgN!19%Y^R?B3ix;$T46fODjVaKUHZE@Ashbb zq@-e<27)`_mae>(OpLa4cl?k=u8vyXaNg>`fVz1M4~LNOzTBU#zXDZzBKx@>2+{8b z#a}$SDS_t0a&;tFmr)^`Lw)1oiJ5pwV%Cp~SXAn~sS$dEezKS=Ku6zmRBDEnp~8T~ zAL-@}`xlvg^SJlX^Hg~join7+ou@x;_rC#0U2TO zMU}seVjGcPI%Ce8hZm&)!0^8hrD|l=qo@NnzPRMWD1lx_E--4;T&SV`DZ1rx!*v&@=vCDvI)t<|^+__x(Q7}*j4V9|*%1_qD3a7@Oz5bdHUr4ed zaSiM{Q#6t+N_3PXmW$OdrBth$NHPZI;_~Fyej6eVP)BA`spTBTIoZCWE&Br@VQ*6s z;y(~EtFVm(3}{tD!H-GfYT0J}!)0;WkFPL;Iq^gAg?W{Po98s8jtb~X6!8(oa-S!6x zLx;2=Dy@VdARs713-Te&jdw%D< z=iK}G+&|>QAna$ay;raAT93|QAj)B>xXqtbi24KMS#tz^;3N5oO`7WlDZl&6$UKKU z9SMRm!cLB^M4h&=fN>%Zk{@OLql5r*m_idFT1CW%q>gCx^=I(Jdva2=AI;5eL1ZuB zI4D-;r=dJ)Nw?=zu*LLnU(sZXiP@2<6y?}kJqa$ZoV`fOK8lP_qQwvslX?^;_YK1> z_Pz7hCG^SR)!9*}LjCV(pMW9MQ)e1?$F{f{cGb@7hGHd5zetn?^wf3YqBs)9 z{kp>z)zy=)UD}o+-^~I*+^t5C)q+s-^UDqsQ^JX8E1np$F7tNFPnfg^cY9{h9vWT+ zl!RqfoL%FLwwMldoQx#~MpFE^#?0bI!VT#GtuM<+=Bz6EA5uS7rQ%w##whbGGh6w& z(UJ9XwT)NiX0|O}uE|>!{Dh~Jf?JnqDOVorj6GdGuAT{9^5hLme;`D2Vcjm zS7FdGSFTtf3SOMe22$g{%ewPT0=C#8Sc({6u!QcEF-Y4~e}?$();|=_UmvlkZ>AdA z`{hhE3EWpS0K-HCb=?99gOYUVJDml8!{01{DHEOi()%zX8dv+I`dQNgp4Y)*Tf+-Y zPJE2ruqb2q)GUg$5V$H0tNEvu)xTE5mff+sTl6s*=qf8cbf6jg3c`F>x(5-!|F3Kq z@syDUurHj`Wy_&Z9J0yF#44}(7ljPdkl2jp*>i$92Xd#j_v$cly3 zNpzs&ha}mB7cWOUEMJYd5+J=}F7$18s_~^Tw;Z;H$KWqcDCaQ9y}J4SD$CVX=TG(R zM{AC>5+Wncj(=kT{7M;rn4z{W-$YIuxeH{^&zsa9)!vUCXGY0$k?_u=x7YfoAS81grxAnM9bO_~T?GK=8M|i(+<={5_Psiy;_+4qHi1^lL?GF)+ zoK{lZBlm|x>*u3&E6c93Z$Do0lHA1yA!bLqVWav3_&jCJ%;tXkYWee$>&T>mRJ{o~NGhRR?-Hax%@b={G$6N-* z7Ze4jmOc%d96}4cS>q_phg4SQ)iM7k?B}tt02h!@)DQjeziY`;#OS)yoUGI5G2?ao z*j_O>@yT!D(>+~I7&VmJtfQe>cfk6_OzT&!Nt`8La%JVlJ1#(cUzoPBB)gqR|95J$Sk5}-TQI7t< z6|ZPYkbm2I3-@_v1$r;U19+G$oG&{sc$B%bWsi~DvzeRT6x_evoi1x18C?DLbFT0; zctX-oOlj93iCEbXF7m)R*ZeJdzF(lPf6p!L=M$^=)AMP&L<{z7(gGe0-i=;86#Euj zM)WR%IiOV6l9k7z>qsm(@sw6zKr%`Gmpclv?u7dQbwhB?cZXaXgZ~WqOHO9M|Io#N z$Pnpbg<3nOhV!YV&V}WY!&@!x@3FP!-kaZMP+s*6Wv|4Da3(#=@?40?a9SnXOT6$T zpU1qq_wxMkoZ-#pR!f!{qjEMareZKa`*6Q9_{O`ROMDTZyo$vh{p5!cx(}S*?5m{z zwzwA6)NrT@p|?VBJ6<@oiOo0d>u@zTxe@5^XL;l@GJi@CyYnnIVdRUURC~k%HXxptdIj)GZ-Y}hu__E|Cn}Zr|59O}WwAh98hiOG zY@{6Az$Cs2YUyx2OMiFR+$}rH1+1w7hyAp*OB95})gh1CLU@^Bz}RzVIT_C({d)}S+sRwABSdv`d#pwtJ-(oW!{qn!uxK_U-=|_Dkb~k^VF{91Tj94n) z$Uo5*NQ*X1d z6QtZY`ty)yZSYpMFV7dBWjw>7NWpTcsIpb`63tSJm#q98$;fbDrZDDi^g}I9**H9Etk; z3u8T1n+I|^OVSp*w#c7tVvF;yk8!H`XB+OUTo7FF1RY_?et9L0c*{0RB>v?jb5&|v zb1!|~9m`7W@m6;_9=GDewYDlA4VL{R3BaMV_O@Mel3HZ7$^u!^^(t7u-*CGQj!92O zH9bAjVJ;d259oM;{ABqV=VODbq)Ql!=@s{q^Xn$^=9w`ng%kIza^Ua!w)^C*#F`@8 z3Qx}IynJ_Tq(fyrp%FHk%z`V{(Iug=8+rSjuV4;w7e0fG6v-{;KZWoi*)VHOPvc@k z3$i3UK21|AJ{O@4w-N9*TKgC$dig=n3E@iCDLx9H8KF*$M`EPOT4$&Ff63-d8%?II z*X_Ga_45}n0S5UC@0(}E1R-R~K6}C4UZg2tk;2iCK4bnLYy(A%*hai@+1$k)fG&R% z`&aoz(OT9UVvuos^MQ>swU(Q34V#6aLVtoVHIL`o73W0onz2jL;m0NZTl)lQY4OI6 z3zf4y)2N;A+I>3k$3%9>31r^YP?(DSp3|KpThDa_l)TN3lSu|FEFgwK7lJ`&DeD$|yK^s#_2imE)DaP`o}!_3+5 zCiRQF3&dgPwU?*HIrl8M9*`QxKe4Oz=yn0Xz=o4nCnu!f=E6OF6b~@CC%eyt9=HEq zAz5$QwrjCexpXMLf5TIYa+dkY`^wURB#na!-*22`b_uq7D<_#N7Oz|pv zg<8~04kIn*sZLTDUM!j0F`Ov&h4^0Ci%PzC+6N=SYSiej=% z!e3i{e8eMUUNpB;FvH3t$aJdjWk@mEru4QcXS)x#$9tH7mP2n%Yj(w+mWAl!;?l0* zEO+eV;A1P!D0jH1Uaej*KW2sw<$w9r#~xS7p*9uP`!_bSebuY(_EBsv;h*$o-pGct zmt857wU?Wvm97HeZs}J6 ztOZGJDK?&-%Wu+aig>h{`A`EPSEJSW8i%env$BPz7KtjQH6^k$Dyz8n`C5G{Qd^Yz zg4F|aA==yhmH6L0pRRr7;hb?+d6;bME0h;@s7GKJ`Kd!!%3j0N?oLPDcE8-vb=9&x z?h(7aoU6A!i2*djBd26HxugEv_3Cejts7F4o9$ncDhaa{lncM<_ZgREwuE6G0a(k& zB3CAHMtCyUCWictua8&6?XaWds1AG$T(KAqWECaNq|t$`iA`-uGK##zTq0r{^57u` za=!Cjm~6g(Wbz|AC9BNX!e~EsPI{d=Z0_^B5^b(>{i&N`6J?-HxF%~OWEbGcZu_>J zE|%bY+sfDa#z(0FTai#ME#!moK=}vKamy4NNHOULd%=}xGqQnV+JN+ut0xKZ-!1F^ z0Y*n;LZ<6(n8`)kyvdxpdxV#%v18RqWF(lRK=7A)R7G z{7P^x+@oSOrek^BTc2xXNj}E1#PCJuPHN_|)C^}`vzIWBA-9|G#!92he|>-%Fp+25oJBBk?LqzsYQaJ zNd7s&BJdl@gW)F>d?gr<^YHa>#Ygh=2rwm}{xb?!c*mKWh6=c{%DH!6g!cFirM+<+ z7R#XU^Jr74HpIpX6^%R2IAp@b&Lms94m;RV4R+}Kg`QDKgbR;B7TtDEqpq*@pM zZ_ZvQYBZ~jM!DpHYOI;s;lQg9fk?YTq^YL~rX$j~Y%6oDx9!!`uGdMqn3Ycn39{s! zCem`(mpd$PAS4vzu;5ub>m;$`@*W(dsB#}K_BpFTEzt z@LVh3j1m@|Q{LGpWW4Am0olh`S4RMM zCT1a*l0MO;RlhvuK8+D-@*C));r2a?!RpwT3+lK zWv~ldE0?Fo`PoiH=+Shou9kn2E_HJhpighlxHc!e*=apSuZUV1m(TWkGWANBB%5oK>17|Xe`nvlfJnB zX`9%oqyV4&v|5<^%=CQkeS~Dxa|z`MThNdz5eFHgCuZyyLwq_xcl-xVHPbVWll6yY zsYpEm{25rA_(;by*8b^IXM?KkI=w=tWvR(TG0dcMy*k^soJpjL=s|+&jXUM{;$g9M7gNJ>~;gUf1mOtwt2d1ujNnGNp z3IS21cz4JjJX+@Aucj^U!2}vi$wl9zV1)qv=dTSh>Yw%$O*(zrqb@Dijuo`11_C5f z=};7&Gw<86LP^qj?i13Xdn5Q;$=|NGJOe$x-lMKWDyAB3HPx}f6>@3oBZGagOEEaT zr@iied;Iw=nS>XOQ7pzNfDLq6Ef&6Ec4+3sehryZdCdKg#!!Y5}&8Vk#4m>np{ zJMk9>U1qe|S>mX5W6mwcD@%-^7g>9m$+Z$NdXv;x$$NKr|7;4DPd3M`7{kbzbA{7d zCi?{|p9iCOgmc8QE|*5PaXXC+Nj{Y-` zzS`(%yW^j_WZaKy$Nd|Zysirjbo5VSEY{qSD3hiu844ily2rxfD2 zKW?`k88wc6SMem!9>!f$p0Ly~7Tdk_R#A2qo6+XxqyD81<*KbG)Gri*3fNoTK8Q)E z*uW3vxf%cRO8zdi{>yI;f?5@XwqCU+LM_=UfL&;)-A8N(%W)_@?=o@4WDQ=8qpW9DKJ9C;^16ChGQzEKA0C?4c@J-V1^u`yt zCMa^iOA!c7Rv2t$UoD9qgt)&QX2L1!y!x@(P5y9R9MkjEC%*8c=Zp8@EhK|w=(xd+ zM%5(#eG<1-RWpe)b{L&#hj`uopvki(wQyk zNwdq!aGmr{XLjE)Weu?XuA_m!0EM>;w7+9hvQ9k&xNs&tR#4g^4fZc7bmlk1zyAuH z(3;8Sin)nH;MU5qYFh4DkVQDCjZ;n;rqg3o{ewk^Lw^>Rta9cCFCplXH9@U$>$MP7 z$7Pjy8t<21cIQQ`zs#k#u;k;cU94&9dg$^RRxdZZS}gUZ)*tugd%aoms=^ghlV!Y_ zyqDs+Ll`SJ@t4LcOI#7884omXFCum`%P{lRlU}7QtpkZ>=DAjRV0Ih5^mAcSjmA^) z^S|8=4W7qq-KuZbF)-UJT9#g%7jIIn5>Onct|-e*ON!)BcWEJKmliA2;SHaCP=PJHIsFKZoHN zSrixcm0FYs1Fp7t{t(Ktsza(mQ=Fap6Bvr7@a8oHXhVO2z~R#$3jU{8<3*eR6=tl* zmgLKaHyasPRPH{|yMwO|Vc`Bav}t8=-VufxG!F+u&pexceC6o0bsI2>5lI-dRy6+5 zWLwhG6GwXgHW9TmL-vf6$)pBO{SVU%1aH5_;6ClhUDdYEB_w#rYo8~cc}`Lc!(R&Q zMXzMO4IQ0~2Kll%SqE-mo5RVg4YHHci|WPq*iaD~C^{pP`##3?wAGgcSgW+tQ8i3&Vaxo>KsSZsG9mOZ8J_S+%g z*Lw0eef@`P!blCP_YPd~55jgcH7~*A1tc!bJzcL--zop2d(UzU z_~L39y;xaO`JJa>ichck?T9OjAZ~LXJA`1(zi~cnPExp>8ie6kgx0QqeWr`=Zu*q` zT3HRdl21b%uY_X*X=LA6LGiq`m()iaG@CKOn9vBX-ifM?J%_m(or8Q7&n{yDFj7lz z=p3JKHjZk%vCZU3zHKLmU!ED3EoGJSJ;jrvMPGYgkxqZ%w8mWPfG(X__M_ME>1Iz^ zw?ZN}M-A{ssF(Dx!0RqmaJ~5)eXswwEegB{bV#!IqGyHp@$VNCxpcOaVf^dJkKJs$ zDO@JIH2Y0tJJnW=`*VLn4+IPD$P#}eZrhS4rdAB8J%dnwkbp)v9dT|o{-jp2ZaEAg zPD>6B^Hn*jxH{n=OgbbgO$01bewO2^dZ};B1;n`ibTYbz3->Aq2VoOuxS_RM&=@&80#YKJ#CUY>ddP!M!!mBAvxEI!@m}MjAEj)n zsN1E%Y(%G7J|w+eF&X!0TUPSeB-SBuoP9-#4torj5rhwP+(!1gvf*4r8pK-%05Z z&dB4`N*r>@MmKv8dD*amI7fgg6NP`om9+9^^FUpCCK2=eRqwj)uUEx-=1Z50r1x z0@Y95(cyRbq}}oJZ$M)TrjuLyNNlde$n&{)@wYs#Z%ohAjgyPA){%%j+B)}WY^E9*Mifd(?dRf~)wequ)--4=%OYxNj|=%uM|PK8oX&&x78?vJk|B zTHy}4q_!uuPz&|0W1CS4H_ILgyfN;NuxsOdSqgDRN$ z(>@#ST%C`SlO1Bai(jJ%9nk?k=9)gt(G{`{|xDjj^2*Uy;@px(&0uFiho4Tk$d z>Hc-fc>!)vgxQntdZc{i*7TRwFJADgI@6l?8$nQ{9)|2tQL0|{QNV*799AxfOK7eC zaQ@3d`j2mL0K>^WLWAK0A)bOFl5#S?GKdpmz&i6*(QUlp#BCtrn<3{#o^cXDn1CUD zjU2cr@w`dL3<#!JWj}h`ZDTX+y!#&JzWMvz4=pE@vHiqy+I=hG9M>PZ_1{}x%S~IV zP``B-9l$4!-`^C%ehGO_j@;z^R%we6ElWMB#U8&@uPe_(kxB5R2nQ~}^|Em=j;Hs);72b(h5@8CwYS@zjX z+y_&+uCMAvwdu|!Z3s7!L~f`nV>vkcNNaas50Mhy|J>{hVVV*7<7<(bKVIEr0`R2_<%f;fS!cM(tjm-Bmew{bGSkkL)aOf1tOzP3Dr9O6RrYZsrt zBx&2ZXza(;KFV>i<{ByXPQlc@Tew-XPtEp#gDmlVsjFxzh>7V6s!p#GE;z$QO=&Q` z&6d5TU)@_Z_$CF5qmZG+wSs{(?Lt|)T~i9p3k?(Dt!E~kisKe$*zrA*NA>CL#drZk zPbt(?aNn&V>HN8n1eRR0GoIl-;cwFQM8J5)!LlJi0)tI8$CVGu`N-cmN|+`Xom>x2 z7RyH=crPBENg+ReY51{x^f{Q_jm?-xZNK<%rS8tSH!ZwfY3#pQ^sLr@b)Plu0zxLm zncr3)H@}@oT$EsLW|4hthc;%of4UE*Z?rx8uKwnyfZ1#sQ^A@~`BC^o@G#QV%IH%yw9LLMk^!Z`8!Kf5h=Tpzz& zs%%;O$p^Q-0C8G@{?33ym-EhcsjlKcRcWb_q9$L54dBfGTqYzJr;b$+Wbun@_6xuT zqtQTvwVKyHFCzi@5xd};j-w|ZeeRm_DZAa~`Br($H(n8C)WVyQPt8S$*G^hisyyT{ zJU&wIV0H~x^oM8Yu2mwDFC&Y?1|yh2Y;3Um`Z$bn5IxwgD44Rq3Ur3*{_Tv z8>sDWA9Bu*nhKC-J?Q^v!V8=PK>)>#nzNA^NF6xS1)*WL$a^0U^ickZEI6blu;Q&H z2sZ_9>*-;UR;-J}E|QOE#S)|kn@j0<$DUp3+>io;Lk@op47|W}K;5j|F3%^itFlg^;!T;HZ?jl0uV zo1D(+VQay*ikBR`KPrzaW*LFn&D>v_f30815mSJ$uFx9eV60&vFd*wda*KTC*SrzC zLZjpreU8&|An0)~rRgFmRg8A<69Hy~n#17RCh_BxL{!i!REUH2XfkRjI zHo_SA6y1gKhz&pv_OlL?my^3Rkc%?%>dDPBc3$EN+#{;BgM-PehnpEp=D47*9j|S* zpGbV-S{hl7GB#DD;$^mO@ksa1hSMc7auLkeNMgz18;+7UTJW7S!uZh6(nP(vF3OK` zqhGaKefIW6qZ-m$#m6T$Ye*T3&V&XK$b>p?< z+Q9Rk+eK=&pgo;+8``wwqu-VpdGb~8p6#Q_fLs|GgjOV}9FKZCQU2NBrCYG^? zUhnK!gAWKNlKu+5KP*YIV#ZUi7f^7(?X=+5EM1RNMXY>Av%Tp56|0u{v66C#~h$VW(hC-h)(cehnOe}!f|YbgK3dy zFn=S;Z`_o7iSo-Tg{C{jkG(4o)ES~zxm{@TxXop5?6Cm|gVGqLu(gA4H;3+7s0t2< z7(gz-CtTQ;Pa$XGUhI6-+FK0xpHG*Z^i3m}3CZU|&Cx=nQl05eGH79vk){m(ig7Qb36dx+<2+7aGm3A|S-Y z2%_yX;-O#6zyMskHCGI^f6;a5n#u4R#H6uTrlltxw>dvEI1s`M4$22*&klVGZ7yY> z^i|JD<1R0{e=K>7_>BOA{J^|$9J2LaI5&E z+UgjXk#1+ma<=P#ehq}s=@j1-e2Va1cjp^$K{sl33v;Yk&BU+fiXB-5sp>M>EQ zrDdR*3d{h+bxJST8Bkmo-_<2vOK)(e@52S@l;#1&a#r|mc;=6K5yON6)exv8r9_h` zDED_Q>b~PQ5K1u?hd?Bt$srT+H;@0f=6VbDO5(_>)D4|yA@Eiv+OrQMo`Q<2o7@9z z&$PfB30Rxo{bj%Fz*735Z-`~z0>>~kN@9Po{vyC9kV8BWF(OmBPbWvfvqALm0aYL`>1$SETzt7i1qOj@ z^+84ocV5~D3kiqqOIO_vGoOhxR2x2AT7AjPd^K<9T1-LvZPOjsUD5%;9%^n%+~kON zymsPo5}b{E8~otSE~^P5A8=sAtbq==Fbv5kj+{e~EAeXEVsKEm1fFtWNgLxrcnzt2h~apEkA4>ujO2G%dD8h#eN@9&kht_e z>7n9&hhy5J{-c`vq(eQ5UjI5AL=z0t z9l-Q`zL7%BTj;+J2^aq(>sr&$Y2qV>XCp(jRi27>xylXS8Y(bMgV(c_MQZ1*IkHt7 zx;%b~-KjzdblMqWd493gsWA$Ro|6*Uc0@`X_C8~=j5Pn^7%cIKO29VaN}?h+7n>Uy z=0iPo2lU3b?h-8|bvMlVMj@$)`KXG(hVPFhs%Xl=Fg|*AoW3|P5_<5qtg(8lh5tIq z2S(ry-FqEp4v!gLHS?Z0|3D-BVQp>{4!#F?^CS+l#hoN&kvOPY`p9|cPKfCYz^mJ8 zeCBa*?a{Zn6v}obuVme|AhXy15b^r|?g#qrK_Tqzb>UFtV*9Z8d`OW9s%NJ{`Z7E= z&FEl;&7zF*n~n&lKPPa1Z#|*lXQT zqR~RSTv?UbrKQHFZtI-L5BErD7k?JGc!U%YS*+6^=}JGp&9v~!;f}rJ+VpWt^SdTG zV^$Kk&p4>{U9HP6uT(R=*E0JAE&;IuHsC0hK=4ly2YaId|0Nu1y7Td+50f962YYkBR3mUu=AoQHcpTMCO%|pPu(x~z z+&R-^{o}cH2Avl^`OEh0n{&>uMYeMHIzBW0yD9?b>Oy*jty*%StLn)kjZLHXi_*8X zVz~|+Y1Nz+VZ95rO)Gs3E)|2f!-w_?W zEh175K2YNgHmnx;c$Eq%!0h_-5>q9P>16rFTBdcj3>;{*U(&rMrx zUu~tV z?&@$i=VJfyAyVguy>#gVR8?-=<n!UEJ=|cYjXg#eNd$f+$vWQS;g2^qm_cc93^6 zy+99`CC~~mr7L0L?f<^aSWVb8^H_96t`W;g%_TS2G+y4VDLJBfFVkV+UytmqJBhM9 z4ISH$N_SsSjqvv%{hX@?mg=_zqzybKxT74+^Q-;q|Yr<{b!y!4U?03^`AnsIVffhzY?zKO|w7$PBc(d-|>TJ3XhgA**LGb zGu_2-96JAw;g?bv{g#6Ws478b4Y6^!Ez}6CX4yrh-qBTOM6<%CD;a_}qG`-CQu5TQ zSDD>Pd$u0SKRL+x$%du%Ll~g^+w}`S<^I!hK+KK7ZC=WgvBQkFxF7iRy0K9hV3Ob$^)5?u2lj=+g3h=WpkOfdSf{DtTM~ z3WnJi;6269`9qD4?D$!v3RQ*MqxAT@Ett>f`WTI+{wcDL&aLlc5%N z{73CHL(T^EkR`D@5`R({bZ$cEtuIGTl(r* zlatbFmXJ;TH*p=~4a~q02taDXvOY_2<)rKB!UW{yBAi+TF!1XvP?CO6k3O(Zrf>jY0sPp-6ES28}co&7W zdpEb1ak~>vx@od6eKX6!FW-st8^^?Z&%;^#`kIDpk>Hi|1-{MkELdtd9et`i*#aGPUzbOt8HOXFWnEEx&7_!!4;QUJV zpWpm1qFAXI(N_qH&$+K3M2Q~_R$1+3?bTYe+uU)13%M0w2qy{|I&L`y+DX_NJWE#c zf)^@13PK+a8-N1~viPn6F%$X%8E_ERCvI%*k@8hONc9|cKhiJ?(qtUnzwzRX+cvHW z?p{f;{u$kSEuk_dh|n-FtJj+7j4TD>E7z;t8_8OnwrTC*gVG)~=$9QoqCe{}5I#JC zO_wr|IE0>pX|3KKyZzlmSE{4@g!-WCP6TGs@ZhG&Uo8J`9N>j8!VjV!vwQNi_Ybd8 zcSw|q+c5=8xq`5NRri2nGq3COBx?LFg2w>YZjgHlDxPH8raw$X$A-#aYc@i_C%Aqan9{ zpl9zNgSUy+2wgrSK9@m3-Rp2&tZyOGxb1oULT}3bEQ1}pb4$7rlH8BjsSvS|yX=rJ z&WhGL0umMsqLMLA(`KM6=z$~CV#XMT|KIn_et_ff}J5Q{7lGS;0!w00>pu@A=tK{d#C}Se01R@!T$j6JSGUa%O_whF3TN%q*uZ(pj;jve<^J^Vgkk8X{1I%`OlY|ezp1d~`TsT((!}zu z0wB-7PDt>>71Gw*GJ_Sj9cSECLkQK>;)osZVYOIne08dR;x?sas%DQOZHz40Uy_LR zdwOOFD|W-HQktT5uBT`?CmNLi9cIheyDEJ@5<8u8NvK59MjT`Kn%| z#~dPRCmJoj-|N|Q1ETx$NYPamgBw2wJc6h3?WEZ}m`%@^m!sT;{2a7n)*!$~93_0@ z*GOQCpiO3!jc0q>S?iC2A?25QBUe&{NMNmh83ubW6YcBLrci|@@-wI88|JgKx8MkT zi?b_l@1bPRH&BXd^Kd$FiP{-KyPY1-3S93yUuF2mRHX{FaX|%crZA8$OzY!~pxFUT z{bZ@&hhHu51P)wC9OP{Y7%9PmRLa0Q335GU`-kQRA3rkykMb}q?2^-tfaQvzZ`0k< z`T2RGwA?$T$0ZgoSGJ{X8rD%r16~;B^JtkXi^)swvWz!(q9dbPuJ9EPd+M31j@U(z zayiWA{%9)eU7ZoaU71=YvHS$th`GVDz%$h=b3ufcz4@>)7ob& zuUQ2|No&3LpQ#FMvCIo~VOJag%2=uVH~U}(blIk~WBzk@g!sUk)$$+^)<09~XS(I= zK~&td=ogd@_UZP2+^Tj_J*$HisCuL09XF9&``L<9kL9}o3qu-9n(6s>KEOxHFg$_7 zCmqj_^QX)`jQTq83R%H&+q-MgB=+)v z(WZ2WIo=F=Y1!*tp}C`J?^cRB%(dBET*zO#gHk1XfpUK|r7GUuc+i;Sw-vj+Bc^dT z;32D9d&v5r&S-w67{|I(&CN5zeC=!*Eca=!3sw<91odsk#z^mO0~>2e%;iF~~Q;u;t24)KYS8-XDux zk*Nsv<-l4$bO`mYdnU`nUE#*JjBihWeWo{c5kHPC`yjci^ia65%k`7Ugk?(UBawzC zZg7-Out&LFukSQ*{q)&3#`6hE^5u!9^Qz|bT-j5{Hxy+%s~Nk}L(L$_c)7Ajl!hvD z!R+!4HhhooJUh<2_I|PkW2gX0CNvI;y^tVS76&av2P~wwZ}XS`_Rhkfph{|Lp~`wX z$n7sRGTli(fjF6#s}qF{<(M*n={mo=E3%rhIFd!|dhK1I^!%Z^V+X&Y3t zXrBn&*DLo(mU0-D{__Wu+t*i#vktt~aA>i* z?H3pix+3wW>++dic}Ua05tqMIrruIXgkMSlLW-`nQ!Xu}Zx{Imr67V{3{OTyqprz@ zqq>}Jkn=aUNP?Nm0?Q*45_oRe4Rgi{QTOUxy+1MG;@=^?GA=cc>sR)tpitc%_BPwt zI9H2nGKWI4!=2fXdABOhGUpMi%i;=R(W33H46Uy`k!3$$m(7n4@0>;(ru{CzCTU_D z{|ru2W?T21%oMH&C&(MRGjKcIcC3PXe*g9*=b4U^Zc`_DwUv0cNsJ?9PI#xy<`}lf?Km zI?ieNCo;3ClPq7K65>DgzE3QxYO-@feUoC>R7+DS-Y&FM*&ox%NClEl_q-0x!!p&% z3$%?(P^#})Bs}NY!UccWn9`%QsU4_4OhV^_ME?IgpCL{O`)#D(+>mZmD(^7LE2?^# z?m@eAM{!garq}MUJKS-(W$|Du)>+>$>o!Aee4rzF7ZP1NAX7?F&af+jJahk2-c#m( zy@k=4^yQI3;cY8dL;Blp=dZ-?I5?2^n{lFNtdm2nbEK7W;Y! z$zNyNeMS7P)!^qb z90lYZZ6iNAj?I$7!}aw}k5@yh9^G|AU2D4d+d7v3c)+e|iaQ?85m-gOII$*4s|^J| z2v!ZC$jhLR{@)@h$Zr6O{0e?Co`BZ(I~X((BCx!c{BJBkeI)+<%kAyfOZ*I;jt#Kk z!-eJw>4NuEc1Qns>(Gnn6-OjRGP?u=^UsNMvzX3O6$a=`9n|>lmR*h9Ma`@>T;Hr7bbK4a|6f>7!rAF$H_KNFz2-b8^BS~oMxMh5EoFt`Gt)D(603m`O-mA1J z#Zw@15Rlxr-qmTpe@3$&P~YjH0W};{x546{^@9}1e?guJEAULr1};E8HWl0se?+d;>M?ZGPE!Bdx*b{PR) z#fq(6Iy=G;b4Ii7l+iW|7$!&eKH*1*I4WxJd}>`{Z!}ihfQqvU^YD1c&r8{L!Lrwh-S)5DC+pC7yhb2ht2Q!JXgCs>MA26 z%#~W9O3l(Qvv~ePdAK_@15>Q@%zqG0S@K3e{ zY?cm^jkgvL{|z zPp)qAKIUQyIP}vTxLUQ`brEI+Q>x^&J(Qlks9{vYzXJ2)O}s9k*alAo33@}y4~L&> z1FD{9fF;o zxSRj-69Eu8VyKK%eE#N{w*2?|Lo0yrbVrw&?1lcg=8Au^X%RtwS!EgrE0?AG!M4$- zn&-477n-NL-(xRnIzc*h0?h0?sVs07=g_H`{0_YWALvQ@!*q39Ldbbz@qj3i z$?^ET71wk94`p$^Rs1)pe!T}%-0yGO-^$lyQ*ph`BH4K_1wX`)$JV3p@#@{G>)Ya) znjF_`t2R|XU+i-UFzx@UKKwh8iK~Lc3sD2q8^fm%Z)$}EukKa1M;Iqr_Uqraq;R3@ z(*`vuNytNzARu;VbR53$KO*@+P>vF+OZwrdF?eFR06RS0QM>G`@hcpGy!=Qg=&@>* zk1~!}Pzzm{%>J-}<*BS?$^~M;ja~?4@Z?7G?-w|sPdG3cf|;0==ixSqA{&h<-F8C^ zWa|7?+E1;wRuU}(MOJWA;yCA?{CJPpf!R@V{P+>VSrzDBW}vHgrjo4uYepV%O0a2d zb1(1R|LykkzV%7+m4f13JIdb|PIY+h$;SZk#s?B0he#sBrvddGxQs88St=C$MX*;h z!9r7^O}z!1l7DjJ0_oYNusi$fOQ0`sA`G^642I~Z?8s0>H^Z3M+Bw&lreqhl#OIUE zhFo6f2NRKoBY=8#WBufXy;xI`6i~(iD|k(9T{RVBsLZeThok{88X?S4H4tf!WPWm* zmxycZbfVk+(t3P0A<1tfIGu^puR<-C>N-e;k@kCFN?7xV-~=8Uj_;Pj3dwk~w=Jya zE!riwBvmdt$*{Qv*bVV!28&9~OIA{g-5;j(Te*CJ5%>_-$F|BCJ&1yDaxsAU@_=}s zQJ6mtM6$qy;|F(0=hXpgf|BU-dw5m^#0u5lU!a2K038r&$WPh-!*^PU4Q2UHC9BD? z9KXQ}jJX5K$mYRxG1;>q}$e^Q$n@N^f}SG zdgHGPqidP^!mQ^fGM8x5Ri3g@X`76ss82Sxrll?<_(*Y`f4}T|P*2-`WMw^oiaJy} zm|w72{n~rf>URC!Fq`vmmCK@>8sJAdh#yUpXz^*-z%!{^B73g9!92Df6~tXh|5gI( z+*I5pfa!t+aPj^@2J~@*W6X&*EZe~2nKc1Af#lXO_mn0&VfnSg=D8P+VI2t$Nq?~U z1cgGdhIy8Bo_s7H!Nrl!^u~MzOF>TwfrmEzWh~d=3FkDo373_?tro`QU6QWAme)V8 zSE&w8(FSF#aQL|)QkUq<4WF`8X_~AwCM0c#71$goO|p;;l5Xdoe=@?Or8#pd?9{E% zwP9m$Pm4#zm1l@Lw9y$!irPf(mN+r<`t4h|wtm%R^{O_)EgEfe$a+LaHHya=c`CI_LjTLq&e$of74V)>#kI_$s1 z5)lS&TGnH#JVpGOvSC=;mwBMJz7rJt7(5x@u#x@++5G9E73cs#i01&FMP{Yc0+@{* z-BYBr=rNqLSsI^U`Y7}X!pRpwP0%ZSC8M;yqf&^Jn-`FZ@G<4%m*@ZQ_}3Bp}uQhAUY%X53v z_Vuon>nr~cTVEaz<=eK8$W|d+k)>2ZsE`<2DQhT8_N_ug_Kam1B++8uWoR*pvM*yB zqijhiJ7cfxGh>~>V1C!=dEVdqJm2@9`N*dkbKmE6ZpU$)=UqNW@__!1`=zGe3b+Y8 zj6}cb<4tyx^i9guZ@0_&nzaJ_8 z{c`ja?Ys+qWwi``|4bvBDQn!Q`gpp_?@Mkn`&aE$HFt9#-F^L zJfyO=Tind{*_0 z8BDkAsIT_xPmH4sqocA?ADx^pvGCk*_L}F6=N18`UC~oY}{OD~Ef-Wi)lYo?s{Vos)(lfHjPLk}daiW(=-g76vs**@+4in9eX2-=q zDvSpu?JL{AMd*HN{P#=pv#?%SQcbwc*%ux1oaf5IOdO(Lh2OAylJ3V3oSVksN&e;V z&gIjh;%cuF=q#@_O5azWW5_2$Bj*w>b4ABC_UNMuiIMQAq|s`iWw{(atgYV(ky8yiaq=+sNM11e`Mm;D>eJAVV@DFT#ux0n3tMZMbNkUX z_7lInpqq#Da})>lH>k-~RFjcPQ&%f-1@|G}OYm~jpSlH06~s{d^@Sp4-2>Qk>#r)5 z;XlN0hvRgmJRf;>3#VS3AKux})(1z?n7)`IJW4z_-}6I>0W9GO@~SlmYdUtf@+Fht zL)xi;%t8d2tl0!;QJYN?P^Pcz<@brGe<$R>l?abH?Lhg@#nbtF@6I0%_uKTZ|qQ@YM&y4iU{A$F`H|P{lqkJJ_94T4>j7NNlNWR3=lYEl|zbyH6oNnT|E*tYb zi;)Xr7WOrtKG#xm`~5f9O+X&dk*wPOFiZ|_qtG|3FL(K8}yaa=y<_$&_P{^FAl)ryxevK8n z+%oycy7BRsHMIog4P~8Dg_24Z>v~~%F7WKM?R&&D<(RK7FIqg^b|ZQu<9fT?nDu`N z%j{Lp0ce{IL@eT%@^rT4)oLhDco~luMOBt;(BV^~X(UOz>KMR)7<^{}cGWmdt32-> z-){*2uGEkFu`-d?EoLls zed0Rj7QVru`_ZF4>+X$8`K`#ZY~>_PjgF%D%j&aqw@(^)D`h|gt1Pwkq+>G7VUX(r zfxefeeGW5ZFnG`t_FVK2G(=mRWE3#hd(m)S!F|~i;~XZxcbgnkhX#Fy4sIe5_B}x$ zRo~#%X$1-z87FX$kg6Qlp{--Wq4{Ac~DDN)!D!l1SF5yU6v>Ujz1Mxk-978$xvREW8Um|3l10pgV(nATUXx`AzBbFGW<8jUc< zmsrSZE>7+{N>hliYHa*ddwy!*r2~wd@qeA`hle3f4G6h7pAPoX`?&3o9`XAS)Ks6Rp{u;g+d0J6F8l{{4e#@ zXV9`Squ+w=ibv-)>S_VHSII?B+Q7I|BfsN%{@Ny3TD&BVBAlSAO~$_aO< zrX!WtjrH4vA#n*yZw~WaFr->uY@Hd<1_uws@bF?65h3e~X#yJz?{*<2@I_H#Tm>H$YB~UMpz(h^L*7?r02m@rF|c%~1fI z;z9a9p!!={42IZIptjLs|8B~8?r~Ue+U;sC3&zcKf3CmjL-tbBzIgCl0Z1QjeR@HQ zB!6B%Qq8|B0frF+#J80I()N52=D4d?u^r?OQxB7 zTHBD?Dl@|E3~5{ENF|Yhc{&Dyq{=&FF6a>N)&t~aP94_o70e#Fkx%j6fycC2VLIg% zA*mJj?kt=n9-LmwnD}%zojLghGRlWJhtrlIbJ# zrFEbD!O}nS_huezoMNgcgaC&2cdA1V%afzWeA+u=H)_bj&r3f^G1s(>3x{D$25{l+ zpb9t+fCrghEl#d~v&H{9J^-9S+Y`7m^~dcTT3PN(O+FIK5DXu7Jv&OAb+!9%xK}#nb0?zJKd-Dq`QK#YbUhE64(~bVv>V$ zMl??>DHwmc`*~$#${2kBB`lI$Ymj1U`Dt7q7l&c)RQkMiJxDM^HLFRu=jv6ua0~W; zG0No0g;G1KgK-+4nd-;Yt6i+T_J$sD(u&XaWlYS_dFcZ*d5|(7XPq_w*FF1pD0VtK z$rLix2G-HdKI5^?zQmUUfgBQ?0ml^tY5j-RxqtQ_q8SW)b2f1F{i&`o zoR8cGf|xNc9$^F`)Q&IjWsrLV}zog3QFfcClnJa*4it8^O% z9V^DcyP-2M=%?`oBn zd9{-+-#Pw%v3B`abLinuwJP=ZMl}IN6uiMx40~H)Eazx&*4H}VX!S`wqW?rwZdrkx_VtOZe%;yiSPdJn zXZQ3PQ*!kuhxyjy;7(FLJX+N3-am^g+#tYV749{&T(@%g#obeDuX|W(P4Ong+qb9u z2$db0E9uz{-C&B45-u z;Yo-w%5~@@Q%$io;63-|yN)Jh>pI_3aHUk-q9+VQ6^>dB^;Y6B&h0ngdksB_BQCrO z#^{J#F2$KVPq^p!a`gS;oYoX$Go$=PdSh(24#|qux@y9JppgI}<*ZYjV3 z01J=liLFV@#K0@#E%R<0kKN8M$z`fvyA5=`msL^)BA6aT1>2T!|Ii1I1yZFRBHwZ! z>K%3$zT5^{nx$NrZ&eslGFOHh#ijU%%s6$eR(dP50#E(2@q>5>IvF@sBw>n2vZEB# z0S(bftyp|s&?nJ7HX**Dq_iO#5uDnpkRQb;@7Zni5dcxUMG3VcnK>lA;crDZdJ^?f zt!7hNjtAcnW46RxDNTKF$%m z`qN5WD?+1>X8CH?W0fh-X97kIJw4T}fcC=%q7_MNvN#Pu#r@NUUn^4MJmdKx^fLS2 zRELdt@2_an9}+jAs^PekjJBXoRu{Xe&s2mvU%36OV#S{j{z+8q13~KW=Hos}u4tv) zqD3@pOCiJYGW&Dfvp-u$z8^e}Qa#}?E>2=Bw3V3)im#Y3OYBtF8D9QSv8vrQg{W&N zdnLd3W`}7y;-v&m8Nkrze;j}RNI)W{-mHMYJ5#VTFt*MN-4GgITo}CPl-6e_vYFEN ztfqzs(jH7`PgFfGI)NH21)R$4?CFW%IB6aDtDKZnTRc^R6@}sne^4m!z(V2V3exWoCg52p1VvFSM zu{^fCFGouU1RFd0s718zYt?#P-7}|5ASS!Z*aO#Q_zO&Q7FGv^Uyji za}hP%7KnwFg}i==IlELjWqSTtc1Lx2&P7_+A_hLuF-GKVNoc59X&wi_OhH7%Vae!v;O>A#X=VlPOj9$H8%@Be4hx&vUzKa2 z14&HfZV%5tlNcv2eV*X8sJO!3m%e#gPSGy`jB^U0)PK0g6;D;^a0wrwC$PvK(bS|t zSkWl(ITkX45n8X;8D7Z7FHC%QKf%XBkiWU0Vpt$7+P|LUdQh()gZd_%f59gu@@Y9s zYv=aszA}h!u$f;^vv^ZX9TZc67wI8|qOJvvO-T8vo0k3Xx{Pv+T<>yXIub)l)!IAl zH=S>v;rp06-vo2@t;(C*-XN~~LQsKQRQ6C5Q=s%$q+J!}EC`mAcHI1i^pywnU(Tgj zihvY2S)cN}FXHIZNoGC*K}B+7Wa*O9keZL6_L?Dds}gSb(!Cw`03v@s+zv8dOPKS* z7w?o-IBHe9l@-`#2-v$tD@>zMmR7;p6}uCGCmuw~PeLR~!a>w95;hKfy6K%v;X8-P^SlnGRN z2k|(?@5?kj#!pWOC>D9Q-#$cV14GUgvWi_3U1b0bv2SdLM>^jTyuY)g!~HiM*HSFC zm2-}GyjjsTVp{AsWJ6Gb!Qjz+OKZbKrEy$SL}mY-j|%e6;XfIeEFmx7UlUkXH>Q?j z+ODh}*vsoWQe(OhVq9J0Y+VP9kS8WDeQCGlh}S|P%iJk`gtlwhcWe`sBkN#ZRpsY) z{L0l6Cwg(J+BOM6udj7ZntrI8WzH2QuI2oiaWT&O?gBTi4&@MXvyjE8|62?2h$;)+ z+(<-bFPdjw*7bEaQl#{MpXkP8JSUa5++-)Dhn1zT=$A#^{9QK$r#@+Gs;phr?s=+x zsknXS>WTS6`Jz-l!?n9Z3Otce7*ngt zd%e4=dRHhn1WxjL`BS~4D2gd36(;T1Ffi|Xq0X)v4E5-po8&L~B^=DkA63K^ogOx* z+6!16uF{{EvW(d`LmG19;)tHfK`r|eMqdp9HU#sq*-vNxF3tC=M3(DzVHcc#r$y7B z;yg}|7Hl8A9{c_14TLCgWbE}r`)IPLGl-SCfpk=X`}m zyvg&xw|PuD{?a7DgthLE6kTovZpDRNT^w)2*?`#}2V)JjEiEcBDOB7owZJH0Q_nY# zg=Y3aueaq`&pnzSL|Yt+q3t z`OuK2@X{l)v@Aq@?sNVIz5ROVhsV?ptM*O8k4h$QwhO<$_2I6#0It3DYDd{eRHu>4 zK+(KxOz8OsQW+ZnxwHySSkqK1bFX}RXTd*J-odI$`ieb&WQc3dR#lrdOfSR`NHA4I zTBNSq&JDKj^rD_Rb(PO~Kat<5U2=Iql&bPTIQ`f%p6K0mRichAGJ3;*v~EU~yQKl} zNz|Aj>&xGPkL!0D=i$niEgqv2Y|9dlyrlEj-I_lE0^qq!;5Tn?_tKR$4vuH-H%JnqLGUxQ~!`#jT#egb?UkHHU-8{B67fb`N zmEA>t?X8!$XY;uieWh;?6QFd5l?PTrW$F>{2kZNn{`_@O;=j@G$q3@q1gHOk&^(@YSn zesG92{1m&_HjG9~K(O7Z>k_M?VV5i(y$9BP24E@VS0|UAf7m*n5d_+Gb)Rulq06m* zUij#SImWVx{}iyr3O0`x4Bb2aiI>c6(-AP^nKHepU8%YmbeW79mAdsE4SFbr@@bt$WmfHi?uqRJZ5YZYa@-dRcF_0J|k4&9gW1`t#JaxMn*XAIy#{mA_ZG z6tw79FTXkcoy-b9)&%q#F<5rtOI1Q$uK?H4OfOr}h4#md z9_Un67sZpQUn(p%Ywg5O=8-+}?8%f*eY3w~)l1`xk?DBeDh@%rP>M>sVTYkCjA1<; z{-y5W4yIyyJATki55`u9>Bf!XKQq9l>s6EA1?Kr$Ben8`mlE70Hq?Y3mCDPyBi#*= zZTj~J-~3=b-$e8T5sRaSv)(=Fb-u)=!bre!?;R>-J~}kPHLBJf@$v!b=N^arF<6Qz zc}lcUX|R-h9eyk3UOuh54#C+x1G?#>%iAHe`l-_N(82IT7H}4HhwhC@fI3H3B!ZSK z89?W9D3Di+X^x6e}Vtk^?|Eb3`G1Cl=3&*EV-=z**a$6ctRi+=3 z=7gFL&3FA021I$Tp%E<86W9KZkFCPXp$PS3t;;VMUyRn6c&d%6Br|es%$A3`ee7V& zhra8pK6}4|H@_FQ7`uY)GF+;3K=dA@){>T^S2$b-tatkRbf=0?m;Anf+UyW+vFF<8 ztL!e7KmLvUs67`-9L7T7vdXE#o!cejOBnbMCW0Sc)qgy1bb`1LoA}gL=#%WnO0Ucc zUpwbrnIzOtb@uVt{!tqH`jl?aREJNvs)iJhL(v`aHC#UO5!ZBS>CN$)WMiUPR);L1Q8lWJ6QDGy}Q0 zfAGNbgniZ4f5!VaOrwZl?DSbmq9jd36`z&OXc+bi$#n<3#p2?^zsAv}0Zk|bgaVfA zL##hOh6?QYusaX4R@k0>ioHC?%dRGkw_30PV`@UwQNxm|_{_+Ra+wqWRAn7-cRVmU zBexK1dQ0g6^svvw$>IFo?5hMe=+2>h=gEdtTccC%gIgbR^lUj;lrxt%__EI6t(HQ@ zmKSq{;fU@zXIF7AoF;fg>u>dg>}|MAIMTtc-!^kooLcqP^qpg5#pHK~a0L9rwGNN8 zqzC2crfCxS4wb)}xNSy0en@%fUMC_@)MQ|U5+ zkz@5r^?*kzOeP-oJ?VQLL8pD@M9L+KnDH7c=ch&E-ZuyFgR(W(M@WjZq@@ivH=&9l zicb?Hqp}W2X%4N>C5Vb!TFZ`6rTm`Pt7arVZ?iLwAvfh%g zFV#r-Y<)}Lw2Fa0r#&ez1*keQg?09I8?rq;o>!=&YW%IL@gdm1#8AlY2V8Cuv-5;D zF2#=0G&4!2OCQzNUCT7-o7`wk zjtwZU%N72QUiK2d*Y>nJwoi8LhD#6&BhTbIKJ1q18kkqa>tEj|vVoc!Fxf|1Za9HG z2K*M}u!?QcviMDF2<-VnyeAk_`#s?|HQdt`*@ds-@UkANQK-jAk}s|+SqiAAmJ6YK z-y(yS{65LAuP(LS6&JBSHX;=dJ&F5Ctt;lhCf#?7LK%vI%zL7k8dZTW?jczko1sm^ zYS%U=eJxf(fP}DqkqnXQFXDxu;H00QasCC+UxI7C`QDiOj?@_;0zL+AQ6Q9M9oo_` zg8zM2P#fTM;rzLV$BRDPjQMUz99L4`=#Ev&A2j^MM&$m9oVhMcn|qWQrP zvjsPCH|BGPJ94-3Am&Tn)$%G|C5GTKIsVVrpaj?@(lU|x*h^y$o)Xxop`?d9>Y6}6 zQ6KT8gL_bCX$U*tC3Hz(6{?TYr)KoMYo!tqaepyGe?=u6erv%|Eb^Qi(GX*##0yav zGc@Y18gfYr(W*(2;m-Xj6B32*T`ULd@Y(}7*xj^381G6>k|Bb^xx&4XPD3oeMH}aF zN|R@HjQ{S<0K@-BX-ga~>UUoMr&uSS!2`P-`Y*wXv1K}#3>ga72B_jx;{o9zf&Urz z+Ryo3{L7R}UKA+~5$n9;vxbq}AVAEWb8zfNr8$Q__M0uuH}R4Ffh;bYV+ZhISD``B zSwG{dNVIglTi(l=cNUYq*G%S}RuB*s4YIdBL8Bgl{jNiPIb>B zZkD-?ev9wO9T&g9_o!u~=e@eqK{2U<+>0>fMHXy=-Ye>PX-bhl#v@8c%yZnljr59X zy3muPW z@&PsGLbDrtJ+6I>w?7eSYzLSgswF7xb2m8jK6-3;P7Hj0{L=lT4^_&>RbHQ+s$J&y zNGd`$VS9=7?Z}Xe$S=D={MAF2ak5|dYngI!97;?1!?40%?Zdo1a}bmacSOW@RSDik zLs`Ezou=S%#gU?wpmc3FBpbAuqX|ieB?&`2k$Y>)L7R~i{h%TwS)t=gOiH;I@VVPI z-wIxVSu+UwmPsQW`MHRWpol*~acQ1_w=S(2%CgbFvGB+f$G>&XqnHENYjP=+BPUJ< z3V;qhu}S`_Uu1cdC4bNjFfVUM)~O-d6_`CU!Xk1c=H(oXT3pFVbCv*5KU~I%<$xw_EnnCAZ{er|v{HZ{2h;GCWIeagDMVSJ zy*BcxJ|Ej6;)A^bH3u6K3-E@n4cfw%emtvZ@v`T~TD1HeAgy-CPu_NeHpgWA3Z3u7UNr6I&MgkFh>BJD@1($4k!gJGeV~=a`9s4@kY8(@*jFlJS^ZYmk z^&TrvNQgZ-0jSPYL{sQkmg^9dc#km7Kko5SZWeonT?&#CKC83a^}PsdY+DHw#d`*` z;}oN$g8_^KzCyn7w;C`5ZF|at*(6&}aqsSOX0C^iJbK4XmMWo66m>A(=u3nz59X?g zbWaSu9ZAd>X6<(M7IAcoCcXJS_+lbUz+7MdecqQrlEaH{MLm-*zF^UH$P1&Cdp+^R zl$dL5K3@VTUf9qV`2y@+i7@FScX0D^C%>lL9?p}SAgYYia_Bb|v0dUOD@%o*bKfjn z>vlV+^Gl?&Fa-S(qn1=BbUNnHG96o96~?f+YI|7a&}aR|LUnXhJC9ZpP>{UVVL*_t z2gC;@@ICh6j0Off(2ILoIsIPj)P-wLIh_EaeA`Z_m8JmwZ3Oc7-gzkNKMfuIi7wAe z8wNdgjNuHI7u;y_tdDN$yYtE$l(IyF#9cE!{m#?-hvPYI(TdKGM-dvvjG32FUn)KF zs0Y0PXJ+|TK8^FwG&^d05D6EAhq}Jx^C3y{SrN#?v5w}0+_GeOQ~zZ7rG?nSZKi5k zA8vE);nn)lh4Yx3BMUxkE)>z>eSW{IaLDRrz}sKlRz^;{AN-eli;iu(=MQ>!;V%=D zXF^W-WtLm#e=uO$EP(rcDxKnj{)zk2Q5}gcL^+x3y~^?P=q0_04Ekl7YIDk+@@g%b zT}X#~*vnjxJ7?6NA-KUtQC&>hmo>R+qV3h*Ice+wW-ynr8r>i%jp-YEQ&ukTp*6E8 zYrn{wD}-j2TBlUVFWXT0_T?tkgNyrelRbPz@y}}GO#bQLP&iA=X4{ty$I5qQM$*40 z02a8j6~NN8x|nNVR2xiMPg!!5iae(;lUlVqR`X|{OpGG>?Rw^#sjqCRd`vt$M6n|; zyF+ZJHt3!f2=_Fr&*X_%0Zvp0Fh+!@%=y}``Ns}1FmZ!~{P^eD_l`g#Z0ULy=nq(F z8k*(9jqCpm=t`h+M`l$zzh~KGy@C0q&pKbc$;)pT_=7Zs4%8>N2=2gNWFG)G=v-<_-H}rx^y68>IQqH^_9`W zq4se;LwEZfhS3Av`kitUPW^>F+ssI7cU4P&tpyi5dcuBKCJeQ1N1@E1p)-B@g9iYU zszfHjAG_a>ygs-Q_I=TO;$&f+Y}OsO=v6Lq$bCl_c2!7qSI(w^K6LR)bg#LnGu#DKVwo1~t z1Ch(4W!#^6#|S!miE=DIPWO;?XJ7@_T!3s9#_&H z3bS#hgecQ$Jks~Akpq{{s`Gc9#8jzzeHN~tG%bMZb0hd0LMRU>Dox9{>)oQR*kdxEvSp08`{s_FMIcl#@!!ekvf0({d8n5A>(zd|wXpGQwMN{q5vK6w8u zEH13K^S;M=sk;x&h95--{}$yxXktYOP2eZ#@cAsek7wX*SLpX5DF|y7mH$g*)`Xh& z$R2TNyPT6$ZLd$l8n)m~hpKA%T+6+ztykE+jCM@+4F5b-ACJsQgLV05YXZt_v?)I) zkf|E^IC0I0td1ESDO`{+*wJ*R+KES?`-Ov__096kmdawaM%GQ2D+@m)wyK`)KeXP^ ziq9RU6)RYlaeE$DnYwEuOGk^A;05wuf1PBkiJg#bbpLFPGvAWQ{=BCL{idHUj8C?K zNDmessxvNtC{Mh5VFxF19)ur@us*(WAfMuh5+Y~)${n+oYrC~C^E}_w5MdjTk&y4hWQ$DK#hs3CfEXg&i>6b}M6{TI?8{b->Epy={t_^(q(ya^O zUvlflPYKeL`L$U)NQ08lz2nAB5CR)b`5wwf6%~**m#up^6OR}05dxwY+s4IlmOL;1 z5T!ZJ@V9J-YVLO*$nOdIQ)%Dl6dmFMpiAtNn=~c??1Tt|aB`>ocRB^ysZ~y|+GpPn z=dM~^raFtvBThY_n8eR!|3;kJnw&SzNg{8Q&lp1Ss`T25!^W2X;eY!>`Jh3?jfBxMe`47~USecw@>a(VY>U>88tNOi3fVmJZB$-!$( zxftQ|KLlO}D=Ua$ndz3us-T1_Lxuzwt-+<^_GB~=U?bbp5IPtzhUYE2SJA-sA8ZjRn*T(P4KX~>{ZDaeq&@xj#bvB~wTzR&tMx*fQ zNmEJJ-1cr}PaOXGRw-EzspbE$ZSm3Cb64mp`)7*nCEF2FrvI+vM9M-e>$r1#CZIHz z=#>mnf6+T=Bv@I*X+mE-G36zq_Ws4U60T*ga(} z{t(Ea?G`T108sgFuxyE!X3qJ#hn$Sx>>EAyfa2sRrq;nXs(p1^mRc#>3KZk~ni*>Q zCn1q`EObA1+HgfjhegINF@ATa#`hgX+(NeLzVk>~dSZyaL^RF!cB-zDzHoVuE5A05 z?1!N5N93d)%&serF=niu0Fz%WVoxrEj+LyNf3XYfCm377PLPivzAB=|MGSTqtFXHI zGORAH;^sKkfjg;Gb!bx4vr)YDC!tifAMg~WoD`Te+vOwd(^jI2I^L?4n6dfJt0T{@ z2^IH#sJoe{HDOtOdmE$n)b>n&$QA4^?%5JwfS(jeg9%>pI0ZEkU$xhQAuJ1?hSZ=( zJ8JFqKYIG-{Mo1X((X8tes+ZeOa^t)KQOKn|CuMnHdnX%NzUL~SzU;8B-Vq-o6_?{ zJa@tF^GNhJHQV4frqtTC^tzj=TGaDH!C9{T*fY{<6Mur>bkS=PNwHv2`)${e^er)V zL+h~Gkm2ouigjbMH#z6*;q)2xVyB6erQNQNed}qh6qSl*Y+kIw($oo3{1Nn}1z7%K zUrC`cXngLTp5$-%rDn4~unqK5i`nOj$#h|+^P-dFjS^{D znCH!*Um0N%(xqfKf@!ItR=p+@3KNmH#(FqCvc|@xV*sPK#0FjS9Ur{Y2Nd8*OFWN? zr~U2*P!`Wet52anyPJ=Sx(9!$nZ50=V@u{xBpVNq*fxhFIL@k?RsY;#s>a{9fSkN+ zk8;eopC=9Qt-=ZpF!TB*9{b9ELWS3DQ+I(vls2h9{&@l-{NU6%8Bgtha{Og zG|EuHUkb*6R<&p;u0Wg({aSOUv#~G2a)h5!*H&*7P+^r2w5;9DQ9H=Gn&E8DQFC)4 zGu$Y*wd z22M0zKSQ$wiDrp^#p3@;qb#zMuzSxq{R8xiG(;B|o}k|~T>EoT4J#`?e4aL27kD!j z48qVH{r?Z6aW3;cu#qa~Ncah44DanL^CLRQTh;QLZl4#e(`%p@ z-`(}FPm!mk&(Gia3klYH7H7I4WD0jt{?_xp8_MDEIe=o~T)n=Ac~{~URNlQK zX?)d_{mM2kdpBBsyRRU*b=TQmXRO$OQUiMDX1wWs^&+Yx_47lyZ+69xUyPRQo0dO{ zIh`ojTr>`;ku$FChgJQ&t@6kt?+Z@1)@}+vvJ0jrmll`sYq@ypBjV^d^TX~m_{9U| z%aXiiJs)yXWt#DPUyiJDGO(|MB`J2|I-0U;_Kt9`7MwjG0XMu05)NjIf#DNu=j$8= z(64~DkuSC?{!x|F``x{wmaPSE4!z2z9cxlh_q>XhfUl9W;8itk+S;sL!#2(>LTAtP zi!<=J4Jnd;^1uZ@VUZtYAiqSkJsZcUOCH{OKk;=FObCFnf{Bo$gr>CiYjQEoYyp_o ze_;y-pAK_(gVxla1fzl3EX`n5q<_|(7p4CPWEsfg#m4A$Pa>{SJ`(JUsQ1|9dLPrs zkOH>5G^F1*aq3R_`B;vZ*5HOers*%QToeM1eJlL^;U3UZTide=|E}WJXxp1Qy4*_tEr=g7a{>EBY)CLAq;@_I%!iH5-gk6lKkQAc7Wx>NZl=}*CB2(S|QOBx?0JQ42d z6PuL2CXZ1DA|FrACUbrP{0Cot6di*T2)L9J*G8SdT-eD3;CkFOOE)s)0p)n1mp03P z%LnAyGxiJz_ucP5Uy{Kvffn6KLeD2=%&B-!`r=n1|5_BpOx6*6 z?DJNy-F^`t=Nxo3><@vECLvX?4}2@L8$4i~w{uVeGNbciudUA6$s9VJQ{R-}AzjU6 zg|Ao`NHV==>MSB<`C~YrXFK^0oU^I54O99!`|)T)N@1~{V6peqg@0=SoOL#e^`+}} z`R`%fP}w3&7M}YMD`5}@_g|3TJe_=oUmv$8{P4Z)S$zsf*@uqYQ5UJKzc!SVow#(` z16{U$3A)%J{O&{YK_oz(9ql0-p1QBHZ-%4}Q{3Y3y05E-*c-s(TlsRlYWitpbd7nz z!qlj~DQTWW)$t5%v3)CDFWmeszblLTG^Bqde|_tT{N`O^Dt@F!#Ft})V=wbQ$GVOz zfbVKZvod+1E|473;Ge~-$E7=a0B&^{8rrQZaxbpQgv0tZaJXqkupOj{=yen=lgN5Hl>~7nj=p)cJuEZXA?m~X0(>fBN#2XSk*suPjmLnU zhY)045UN>ba@${qjZ5ug@>Bc8;Dy!1`JSr5owhsC0+%OB$;=$q)?xnhsBy95FqEnP zBiK9JO(p0o!>n7Iy3OdaD@<<4zyGbQKEzoW-2=^)CpFl>y|ClgQhb+8?Q*>Cn;`*K z(nprhhgn#`JB^!spp;LhA7B)0M3ieGAW9!0sFur?&@>Fp<~frazK?L42TpZ8TzZ)~ z%RRC$@Xg$p?tS&q*LEuYD! zy2w$M+3nG`j`Zt4sZzs>7ctkJatPy+ol0F}-kW5UFp+&`$<&QDLaR5s*sHFMbd}DU z*`wozsKR^J2syH}-ec96bdOwH_Esx%Cz#}J{QypR)iYpj%;5?hpPEQjecdAbKoj5^ z*V#BXZ~iWmo4>>}S!wh1g+QqU*JSL_;|sL4Xs@;((Zr=xH0xk1%iMEp^S{npi!>%tDvVjr=dRC`d$ zS%@b;|JH}_gt7%WZ9g?aetP)E3lM*=hAM~N;n@I>{#isa{?9ov-{I{jlRS#ENkOf? zZJwTAo1SXi&>AAeKvQNgfa#6&ruvdAu?r0B@75%A+}dCK>g(|abv2XBN?s16d^~@@ zeu8{Sq=*X@qP)gJ_Nr55`63Y}+&2Kw%(}iJFe@oNOfd^8B}aj2>MCjnMjZYl4EY#; z=RA-5)cU6hHEUZDG@rvv@nlWKkJ!IyO5HcUI$)SjSyd1r!T z>9M;mgGJTt4K{jzNA&5!MH#NDS>r~g*dwl9&yUbxVB!k_=@%C=&39`NNN6Ts)J$3Y z_+|*6g9)3iL=M&L2ta#H)I!YkxT*1h-~7_KtaLm{7vY8;e$_t=S1NmhaonUbiVRV^nhalJYUL|cjYNw8?`<>WJv`sFW(c$Ycx~N-sL)iLRx2_yzWrDZWvgEg zpCDT0&oZ-L{PLg*N=)m(^6xP&tPflK7%-%(B2N`D>#2_C;GQ+=%O+LTF7+;`fd;Bx z72iifcej@ZqU4u+@(IU?Fn4M*KEuX2*A>eH2xBY5A}l^m3pROI~}6FbzcoQ~nFn;!QpVP=~e zH5dZG?K5}*4SZAg2>xqJJoU(Z!)#CH!=T|7D|v*G$v zoq>zj0z|pNQ`FCv@de#&D78EwzuZ|w_YYmvwpx>e;wI{gkMHA8&_5ig!`O`?YqBGB z$n105r;n!X&rS&44V%rQKp?3-X0?Rvcr`gePUbtroIr=m&L^@<%QO@4HM!=#P4J+w$ z3e9hG%oi={efG_8_u>AWGKEEJ{FEPL2>!&;=rP%~u*_}Y4KGrQ*`&0)B{GL&g)@Q; zI_GbkBY1lxuP86vT+hUg&3WpM+n>1H!*Ra{p7!?pX#)!3&;Vh7B$nx;U~Vctki9BO zVt<;Hd*->SpH%K`^n2lS4VvGOST=WKqu>D#dBNh8f3H)A32_z=&V4egdHICSzvk2F zHJTmkf%dsv^B;+r_8Dx|V;WT&VPh|qAHM9KXR!cn*=?qshIPk?DJ`GdX{Sq(OtejR z+;ytyS7@jS7;B(;YUUa6e2fceqVbI34;g%5t7W_Q9PQXw?`$MWy5*+Mm=1LsqCG5R z6pGjo0)KvZf_g3OvdY=Q`k%JrhTypy@ODGfu1fh&I>@pG)9tMDhqJS{xYgsLPnH|q@T0drz_=y&_9AsZOTHjZwlvjuPbj* zkwhyOSXd-k41FeJAAOm!`ke&lNDO`w#Cof>xbjDO)3p= zxrIWHpZALR@b^>mnFYe{c;Y0s`dN*9bxNvaUdYwq9`p(I)iwx6mIbA1F0w~vr!~4`g7@NkzgkJ1 zoCd7@hCQz4Jux{*A|`Z3TdYfE)I)84X^k4(tUrMaMQVG#*!)fY zBZ3Y1J&5pcX0Jtb>^P&Yyr2tTVf{q0jhn!~EVh@-Olw4(v?6zWcT;`nTSY0vjDh_y z_g;-%sak zi4wn`d}pkdg1*1R3f|?s&sB$DhWW#N^9FsIp?0Klu5L;%#B6kn zEGAqR5_ltxYSA1ST3P2x40O597K-1Kzgq7khTJZUrjiL|XuIN%S1>DUf!bm+TM#br z8K@kd`n|bDGKWKsuWoY+wuHN3VmEMy5N(9;Y|hY5eTld*y{ve{hNQ{Gd?fV_dS^4r zy|=5XFI>*HdSCQ z%`Q}@=6g9y=RO2pn68nHtDdtK>xnq3r)I>gg$?vOSKX?X_B|APuUAuEjZBE<8RV%5usn6O7ti1L2AQ4^V z?y$`v7Dno$668HYMS7JD+@{|Z<0!kg1WfImu>MAt5-m>(fVZjF}?FY;Rdn`vgL>j+s7d$DE5y9a;S;19xe@ zAu*}CW)#G|;u3mlTAy0@?#C`$_wM*~@V3z@4{B9^_{c{!9&gfAy)#O&IFz8;y@Rpy zA5iL!t6neUn!kF&HrO~|rdc3L$$ck3k-iCE!3rA8ii(Lb*ykBa%3l=Gr` zw^Qg-c?6g2_M7OYwh>eAgfDCJ^imQ5IRo}VI$xD!U+Q=UtCL%}ICja=QB;SPW6WZ9 zYY-=@+So=F_=P{B)5V@AVGmh5S-CZPC6O+Cn0<*1&*3>{kh2(Pdl4>0$GFY;mDL01 zNz@!y@MmZ7vgQVsPmcVF9J?PQ#lrFrIfn!IjxjodD?;7FBx<)gvy0G_Wh3*C` zHqQx)99LXOu=t~t@d2Fqqy$rmNp#4{+D<5^cRM+`bGaQZ*6_&W`u0SgA@K=c4uYLv z!sUV#@gA6pUe_98r|PMP&#lk=TtC>;BDxh`?mzCOdVLaovl#cTcRO&CL#)Tz+2j~z zruzW(%rrYOPKTZ%g>V&*zVg(F?UDK>9Q^IOE}bX^5$IUXvn!kR(d8UBvixp0Glfc= zVsfq9IS(&iQ<9Ai_!DCrYfP=o-ucpnn==Zzaw@WxvKc%^EW$2?C=zz+Dkw>hKbaS@ zOc&SP4Z6^zNtK&UP#hA6*yfWgF%BO+{M$Jwo52qHPTZJ;nBawG zGK_LVtDv-m(0GT@r@@*(^vL=_LSaRVDB}XJj%UN(cMffpfVI>aFVX3;P(JUUf@t~i zOh@5u5iX9Mpk{6|fssyjFLugpt6;t`eZ%{43|B4s{&8o2w{@T}>llzy0zj)^A;j5p>bc@;7Uq z@|1x0uI#;gNRH--RiB-XzEX`CRr=Ihk*Y>|`h~giMD!J6KUbO^|4O}j9cFxNPf^U6 zm>SY^;F6vQ4=wPht&DXuZu{GlYl>e6OyQ~^ZFs1_GrDkkMxRjTWDo!+-wjV+P!HGMGit^t zURQy&b>ZGoHnYf?(H)_uPchMj(Ef-+9fL&kHfrKo!0&HY4lLeA;^t^fMJ|Mg$*x~%;QQpisA z5^ep|Cob!znNM)c7HkOZ70_aaFT=XzTM)fEEPaQWA{Oz254C@8F05Yuj}J}LeYz`b zoB3EEZ+Kqvv~UD7GbV#iYCO8Ub%8>kLli*zPq1z$%uv>sW> zg|8|2!@r}bi~UVeYs15&C$jHutv@32BHf~=)xB?7OALMxPOKuF=j5;}OKv^|d%Wih zr+b&EbK4dhm+JJrjht8fmldz5Yro%|Ehj^YzTBUl!rAUF$6<$0S;Hmc76e%DHA7IDPhI z^i|h0cgas(HVr$^mBT3(%cKOfZVwa<27!0Fbg@fQwQC)(Wwk)f`dE^J38$))MC)aJ z%_z5VV^8(JoSxe1P3JrBQ60M3QA_?jwM`>=4DcCufN-sONy@F(QuQf?=L2VthT|Qn zYw~R`WMp5gT}P#lvxKQ{*Ef`9tc5Ov43-J*t#_tYm$kDmU#qb~gRCs*lm8 z5|ws$5e_^34BDyPh>WLDNr`jY(JB`i!`DT=iyfDbNgDJY8xsr{h@dX>tWFCgxg-E~ zMa|>dop+5Jdvq2z;g3_-l=2r+cU7zSVeY~Qpb5@u@Wp%I>UEVT~ zvxE)F+6kP14Zs3_ON>FAE<(&VA_%bM^{Z~aN8aOq>inCR+xIklPHmHdy9a*{@if0N zx;DH1;={jsFNWUAP|n?J?@GFC@?J$a;8 zLSjSu=@2*NA_c<0OY^Dt&b(0D6SCZg!;e|q>3?8rlDxCI?BR8reP`@-N3z0HS=|Mj zHtz|?sH|0B9*`|CgU?E0SQ2F+tE^uI+VW705cxqkGscZYePZI0LhwevtlpixmU!nu ztRFa%bQ?nIXeZH(IK(l$Hr*xoRq4wLo{>!fR?EAO!VWse0|KxafFi}p#%(9LMA<6P zL09U^tUPAMFE^q1+FR`qh`8;YPpDNUU5{)h`72A>#@hod5p&^mWuC+^LHg-TcY2I70O`TtT1PtzE^mLz9wk!5Ob z+>ndaE8gerHtDfR457bD8j;vrJ@0I)dUDk4D1;>}>f;`~!PLxupg3G&#mW|ytf{nr$+da1<;?@qMDd5vf)tH1!KZ%x4=N^?Lpi8Z z;{+ti%R(w)KeAJC0is`VZ^n1&@$vV@xMy3cZP#atb(*wP9*+vS$wd1Y=EKPc>|wbH zQG+#|pD~Rxanahd++xizmcoH!-F@P%_j8;?wn(>Di(KAfm!7I}A2EKGn04iUm+Bmu z;P4OsyIQBOe`3t(L-$mni9pOT)!Ln}XxE3kSY87*-#io21<9MRvXHF$qsR+H%Exzj zba*goNySOQt14ZJ?jF|ddJh%jZ|V&c+BaNqR3?U3TU3%$a=%#d7jxd^w5zQ*b3*8* zEJMp#Gl=wOizDW+zF6$jxR=O~PprB6@VM97)&va&ts`u+?!;gNe0Om}rwgK@^>As~ zd=LnngJ@(d`8Z0V{?xHU4l>N2WnWhAC|x9bY$sp$UQx*t=h~b!Up9Y@S=|EF8a%0j z$nVrUOyhN{JkB|$m#*E?h7Z`{(fnD%qbDzL%h$3;um%0wL9IAeiiP1sREO74;_Txb1=;o_`JBqWu0+oCu*v zo!7pLwlQz?`4{nH9(Q8x%|gH5^~Ul5yw&2!$lQCRz%jl^l)C)m?);0Y-$VHR;A=^} zAAbgsAmKI|?N_ss{072?=IFBS3Po-3lFKJr7>`p$^Ci;9HKyxdiq2|h^FN;dl&vXOFgb?#`Eka8bH|$&;Nl4IPnDYz*R+kl{qgBi`q%FX4dXj9clk; z1qrBlY1Umod$VglIKXNEqf;a@;u{MkfYHpLdq}=LXN(^cAC#D3^x^&2D5Kj^Mlq}; zcAL=Wa~bueOo?3CGJgGy=2j^7(#8sG4jx}TJ7hFO7w0`aJ7A7gPoZ+V*ANe1OrFQ! zcRk5_5>QJVo4mIJd1O^E>dsP_Xf6|pa8}=smt2>V+VGmHp0%I-%_nLu`RBza5zJ5{ zL?8K|V6)q_Wq1*<$k#Cbc%cB754g4*_nq>#K&r%2W$Mbc6zFBkL#0%qGjaC)8!Q;4a2#4RK zSDprJ=H;fsPss_7IDpLrzTP?Z@%=IM`B1KB<2l5_66Yur(;Oz}gc2hqgs8<3%+xrt zx6c9Ysz{0%KG0>1$M~R=XltMCYSV(=S>c7OgI%-*?V|ARK{)%+%!7NOPy9yUVrdxr zVO|9)R#&jF%D(%*&GCB%QlF}>^AdAp#Y#773{g~g)5f(v>|O)hd=nQ=2wrOIzPJI< zTTTRCn_tWU){n=~3*32|8{hW=NXWv(Lu`@4$T-$!pp3qDwpp=Zan>N$?fGUJ>Es6) zOLsr(tt~qA)b)oIahw3iSZ-3e1awqGcg47Q2NFlVQ#J2mZbcpu17N#NlHuFBnhTK}0 zD5)WL>&P%w&8sC^v&2y9SV~n_4^`kz%&J*ycO4z#vq&~kn;q#fOqCHK1V1?^^a50q z@weybd>(s*<8a?^M0qPh$Uc3T)>TbWd=61c%Sio#Vo-@dUNUW)|0U1)qW{*}B$e_B z)b^5wWkm?}1wxf>1kOSAJpLp$f~r1r-dc^!MIVf7yrf%)+`^2V#)J199b00NeE5Hi ztFOl5dec=@KAxyGL%lqjLR(1BcSl1y_)Q}?7f{g$w>S*YYP}QWBKW#+p5*3ej&CKD zhjxy-fym2jyx{l#Zi<`L21kHi%f9@gJjK@GDf9auvH8gI#F(FWZl_#OstN0?l@x=^ zEDd!Kf>C;5$kc4F2k=$z?-b3uxCmI~p<>_=QgEI5pl-<;gUVUU#l}Tbdf3HERae;$ z_=~+TB{m%23SS_c(JO8-=**(~!fM3S^1#%-7Vcrh+I57rvu?U@MlP@h@miRvDP#_7 zbFqulfpJYQD8bHP7B+ZKn8fVMi2kQQe=T+U2%5Cjv1r>iy7q_qwJxd^UemCmYNm-m z$1ol)lX}mt6;7J2(`lWsF#d!XIsr*2rL7_y7+@TX1q`o-cLbpo+mBhNYztwBYJ<-G zT_&@_r8Kf=k1}5iWTG`5x-`CARGuGzaR2N(RXXIL(#HbVi=GRjp;toMz@aUSiFP8K zaCug*bA?OU#R|hOCos`=30yeMD=L2;hJQt@V_`r&HV=lMnjoVF`c^-#y-k1p82GjmcAiz{zdO{M7au(SSIH^ zfB=sb7p2(9%thF|=Tktfrb8-lOe(I%Ogp0;#8rMWMYuSR204NLoj+VWzKACIT6O%sW_C>+4NB1Bxt%0By?6K;+(7 zX^{CLcfA=6x*_0S-jeCffEFmZ)=e^ZpU!0Ob z@&kHWG6=`MQpetcW5LX(jAUTU)y&McRs{OW2fiTNXu4AN3LOj=jC~bo3MUdSV?fJ2 zowySsq9t%%R_gQv#P$NcnTSv6+5|Gf6R3=B3%X9Aq^4_rgECyfT=15W3)+_GU zd+RQuRV;j!M+S)p^q9r|?-6`Yps6IDW8QY@{C|eh3Bmwd}2S#qXoPksp zf*p^5{Sti{dSlfv36o5i#*yJe2{7W}6=0;o44z$Rq*TO6c^Mh0D~mRb8BDY%MH`fv z4mGZg8a?(<4E^j}auDYt#>lyhaNHPJ1OctmG!$G5Za=P^i73b8f0YrukYw76rv!Cp zh3gDlk&r=}NLtMRTj_dnXGO4VWv(zkHX1C{X;inGfxvNy1;?NE*?~w6Cx%HtqMiQT zp+p8*S;jJ{CU%JN1&`(TVpt@Q%Kv%dt>yIXVp$!0B!{N-G!X5~Ixdq!#NxIz`GpYb z5f-qE+c2X}!=7j7p=MVGznA6I=uZD(Z)_2ezUCqs$$vQB9*Y}BLfw10-43*7nt zHwZ63@15PwogeO3FsF8yr=(s1NHg>NPNA4@gZS&L9}1nLWUmKva>;p7w-2$R6Q3SR zf?ODP%4YoV9&x+(4fuNEO2OBY%T;EEaW_KsYU|H1#Sk|j^WgGwVyF*p0ImK=Kw88A zjtc_PJ2W7PF+hPq@C;(qa*Wm^h=I~4-qR5N- ztkfjp21pYrcOdeQ{Kt_W@hZ0d3?n~+KJd=^cq3?g6Lv$!()S-{ED$&$V}S$f=(>t~ znur(o&i)tJ-{kYZB-sB8?EiCMAAkmIZRf6E8YE(kX$KK&RDxJwGaR*G>HWJ~*`-H$ z%lBMn1&S{NiI6W-^+)%6XTPnfplgtjA8A2GvUVg8+M0xZNMrG3^;(P`y@pX?hB|3) zCiO0o2*hb3q@QwO9uOA%@Q%StaTYLs&~`JVdm@hX!%th>KvO3=&!6Ef=8S;y^qsB~ z+7CYr{c!V06X=J(Q*@%Ac`y;CAcWDB9pHpOmPAoV-F#$kF^ZL|3`8Yy5)?Ju#l>hB z7q+^%=PB_)wf(r@%1kNxGgX9}koc568A{akrhJ6-%CgphkN%U)m?o}v7ASzT6`d6k z@kivseh{m8AZcm@=lk=(GagcY4M?YF_}xB`XSyDk(ZqK}E@0P$6CK@@cN<#npOQj! zn-0X;5h7-~3kzO72pwrNo4yrD!(0p|5|d?T*xAiyb-+M@U9$uwOp32~+|wpiieh%E z?=SkkTvYxpB*GyRg&+>fD2Rq=Y4bayNB0tqJ6>!SOqhWyv%85{Ql3~^kquf z2f(zfcd3-X;?BWL8CF^7y7Ii_bA~j6?Tql7Ze^p5;eF zf7bQ11jzHSBouNU^tpuTQdm&crd*0fdpQa#5ZxXI;z-ziGp(ZquRZ`vP0e)Tg5D+e zDyV@s{33&jg^AFFdIj4)UzD>Ou=bj#WW1t9Hp6K%gEU~(0ca*`d02qpaBd))h72V} z)mB6lcA)Sv6Rq6@zXL^122fK*k)2&|o0poiuq`icvc$j6XjV6fUyO9;!eD$E=IGf~ zmifxueiX5aFs9P1qp#5sGLTmE1;q+0Dc7uvx2ip5c|9ZS!tCKu1 zx(8ZyYPSwa&9o60NR*>W5}he4m--+Ak(4ju3~QRZ={kmPLtGyAONsLh_85HD7lNKP z^~6U#w$y*bYDL`kBD-iF9%&!QP!`B&jw$Fxnoj}XJlxL8>}S|3(iC;Jc<<|=riSMI z905o_U&zCNm9C1&E4FyH#%OJ~TH7OQROFb_xXgJ#-AzGZvcIk8kv4$V0M^mW?sOcc zk4;f*%MnbL;2WhIGD7GR2C&F%*4aVdlLVNF`VfO-Np2rcK1fTzmQr0{E<^27Mbof; zbn9pLc1Ri756)Q$oS*4B@)kE8&b+M}&VM`x#R$^>GrpEeSBC_9jvN0_orU<5|KuY? zsAoW+O%X^Bn4p-x|cV@5#Ncd{klQT0^=K19HEt$p<5EwGxt997YRuYIl|@ zI-%$(1bbG?jk!lZL%+zi`F2N}wxirHAR1z#2tVcU-Hxhrd(5~s6S;27t z_v4r#1m1I@iU~v9LK^n^RZrM8JS*%Ejnzx@6d=3e@%CAhA4_~b5(jm9nGjSVhI+$h zKOA>T2d2Kf>8Di#Cd=bHZN=o06%c%VE;mzBM;@+{Fj(HA*PPLFUHFXSn`oXBx+Bm< ztQ8LTRIi)Lnkc4z4O5J`NY^vV8xoZBXf;V*&V33zghmfUMSEmu^XiXWz1+ zg;c>tChZG5as({AQ@E_jA0o3KxqtSx%G@e)5p9VP>RHdgArQ{xZb#(ZhgPlqRBxO( zLC>)j^M7I*IeOnR$aK`M;&Za~*MPp@oc5mWj$Z6B*=5wX`JR)bZfB4kerMOBEe1b- zT|8UgpRK73dQ4?2rJ6+Bm1PLJUY4=hw$@VmRyh%9tR0p!(DBYr$@gTo`!JaKqLkip z5sm}oP__0<^7*$@80s70Kge#mljdy(gS!&?UV6F>%4|ZE> z9rsW;IJA|ZtL*Mrqw88fHd2&Yj_4TyJ{Bj-Jb<~T=BK``(PQk0S8nqX7k~C<_oDlE z{;&lfbf>K$;Hhog=AyF-cVaV?Z2kD?%rh(zk`Y|r&?5WHh|t)qDL1W7JmFD?Z$7ha z32cIZO#^I8?2_a{I(2$`#iul)?;5HIAUk#zC=%B5w3Rp{l}6s{CzbdPdiF{5FLH0G zkIztMn|WLt(5+L@>E<38+tHbPir(7J1b-b~LTD-9=RI*{v0}3rnXQd!K;maUMGTl2|g> zNXs4q*Yrd>Xhvz|T^v7GX;x0&miIkl}guZD$U_XyiUj zajvyBbLO!g;?Zh4{;d{lHw&$;R42rEgGitPX^5;erZZ#&;+se9BtR(9id4d>WO zm(47WyCv;Lu}`jUNtE;%T&kGHt&_%ub+h=;DeY!P*%$Gbqp>BL>rR3rcq(68JI7w~KTSf(jdnn5G)T>!{=xp5wEv z>)FuT1M6n!bH`+9I3_UBS|$6dx$J_|Vm*x7EmLz(m?69RfA-P@BaF?9Hb15HPV#!h0Z2X ztR*GLu;;{k)qjGBZ{N%mxN*{iH&oBcdL2$XjmV@dPT8s2sK^m=Z)Ilc_0HPH7Glb# zK)QJWGv*EJD0ir`rHmVs>z9yW2(X?L1viE3PlqA`Gr?SBBRNxx-M75Eu9HYQy)`A_ zU5T~z@6K)A=$6}`v{)z_@}@*6GnLCaOak-3*?ybphncG)jwz;{ z#Z$=oRK=yC=`w1WSyydoYFz&KqrXi~agR2c$}K|eb)i;g{!*SziDMpU!uNhE45Ln} zjWH=*W5>f;LO-hzVw7yTDla+9PWMB2=_U!!?aTw0mFtF)2XRj@ku0Rj6Dyk=uTFn$ zTHAB-cCXX+-jjIt-(*ZQePnV~P8xN)H4yL`*PW`4hBNAjBe*G#r{I_eP6}RuJ(TBM ze-B^TBD`1p2Ayz-6D1Td?b~qNsP5z@mfQgnA#qc~aeb;?iFKE4D~dpz7D0DWp6T@PAa-leY)&$A` z*2S^k>2^fZhfh(C*;zl ze6uaGyl>z!Y%8E+xHBN=lu93AqIoL2oad%J3BOIu*q5DmicT7adw@y4&|2c|NWDYa zuqF5H#x7S6)1(lCFMIPE1XlE#Hjbc^P|gsXCc7v5pF7eG0kcL==;scQr8F(OyQDi6 zZy=qel4>JOPR+4VvrzRK>dZ?H7V;$L4>vcmY$>$8&uCvO$`)ZQYfvmn68=vfS;v9r zdC!ZKY76|->|bzCDSs_$G(2PS@#IRW?_ z1|`HCf&Q8+Tn<)_d0_JiE;b|)Bz2Us$E1q-*uxLZv@|yGuM+F-x9Ja+U3v1?@c5;? zL0zXdmYc$FdlNHaCdhHl7xvzsjtVB?yQEZRS_;1=@fU-`#z*J?^%V#79$7i+37e~) z-S^mIMLGBjJB|L|&8GF1k(mnVO=KaMp|C00z;rzJu tuatg>;ujbHO6eEE`El$2rj({N+~r&zO20oM%>@7U?$+Ctx&wFNzW~BkCvyM* literal 0 HcmV?d00001 diff --git a/docs/source/user_guide/vectorized_matrix_solver.rst b/docs/source/user_guide/vectorized_matrix_solver.rst index 590b43e27..931c8f866 100644 --- a/docs/source/user_guide/vectorized_matrix_solver.rst +++ b/docs/source/user_guide/vectorized_matrix_solver.rst @@ -73,6 +73,8 @@ matrices are :math:`\mathrm{number\ of\ grid\ cells} \times \mathrm{number\ of\ Regular, dense matrix ordering ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. image:: images/dense.png + Up until now, we've mostly created our :cpp:class:`micm::RosenbrockSolver` solvers like this: .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp @@ -137,6 +139,15 @@ and then we pass these templates as the template arguments to the vectorized Ros And that's all you have to do to orgnaize the data by species first. By specifying the template parameter on the solver, each operation will use the same ordering for all of the data structures needed to solve the chemical system. + +Sparse matrix, standard ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. image:: images/sparse.png + +Sparse matrix, vector ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + You can use the vectorized just as you would the regular solver, and in fact the same output is produced. .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp @@ -161,4 +172,5 @@ You can use the vectorized just as you would the regular solver, and in fact the Species Regular Vectorized A 3.0096 3.0096 B 1.82514e-06 1.82514e-06 - C 6.5904 6.5904 \ No newline at end of file + C 6.5904 6.5904 + From cc531f533ff627b88a6433dfaef8f6d349ad44aa Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 15 Nov 2023 11:35:17 -0800 Subject: [PATCH 268/318] update vector matrix tutorial to include sparse matrix description --- docs/source/_static/custom.css | 4 + docs/source/user_guide/images/dense.png | Bin 377070 -> 382227 bytes docs/source/user_guide/images/sparse.png | Bin 372764 -> 364152 bytes .../user_guide/vectorized_matrix_solver.rst | 137 ++++++++++++++---- .../test_vectorized_matrix_solver.cpp | 42 ++++++ 5 files changed, 151 insertions(+), 32 deletions(-) diff --git a/docs/source/_static/custom.css b/docs/source/_static/custom.css index 655ca3fb4..c51b21d1f 100644 --- a/docs/source/_static/custom.css +++ b/docs/source/_static/custom.css @@ -27,6 +27,10 @@ transition: background-color 0.3s; } +.transparent-image > img { + background-color: unset !important; +} + .download-button:hover { background-color: #2980b9; } diff --git a/docs/source/user_guide/images/dense.png b/docs/source/user_guide/images/dense.png index 0f69c89c858ce3fae2d91e26ef9540d2842e810b..e5885118bd61ec63bfc5a4c7cdd25d0b728c0ac6 100644 GIT binary patch literal 382227 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οT*ETAG#d1llblhsjtPApUwQ$)`O;>X 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
    S*%Bg3# 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°XBnpM&Jc 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 M6*)UX@9+|xNR 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

    1*z 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%0AjRwVb6ikU`feJcGZ?0 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

    Y zlauyI@Y@WHUXovx;3~IE54nq#{P<+LA$%wvrxx6#M4-Q>8J^ZM`Bt$; zceMZ6T4}l8w7%KFebrw~XL}Z9WPE2QT8`w|T1#H7B;78;X~8~q6POYN!hLwAslZiw zOQj~rnP$PY2hn;(K=_;-(VBK(zg{f=N#Q>~n0>vnrKq0jNa|e|=Q%Qp7lFIMK@&2O z3*W&jR*f$FF06tFApSCp0%o8%~MoSP=Hqhm;~V^RK1?=8YWrA$ncqx&ogF z%HZvw8k~hsY<|aMr|16q_7_eUa*;kCy<8#<5LE0ynTpa9`9(@8wX=lth2ZGbFg; zM)&nbZ2rcI=u5WMPBhSZ8QJdULIgAvdlEo@ zT#G720Uo7{RJ}pfuIrm(5EBU=_*~EVbsR z-;~yTMx1Cu7_gYN8#^jAM2eQE@HzDNIUqA=&3D(<2Bg`DaGfIWY&Du*Z7 zmYQ6fX#V!?n^RQOPUQeJBlg~r0PPq@9Lv2U)TE9NP8#d@)OI7QO!LKcqBQcxuLmN( zDP8d%s9O4Z#HM6Q;S4LHbXn=en4TpE)^{nU^> zz^B06xV)EyZn1kcbD{&^+<)3$sm(N&>@2Q|!l@lTU8n~ep-iLyx|ZaZerjb8TNJ4a z@GcNtNT0dUQQK)3Nfe(7R{AP3TZz<`6Aif*RN8VjlxdzvRXDrSg}AQiV*yaTx#Re% z)n7k@EfY%}%N(qLn|7GTRV7zOZI(KCu`ms0O>b3K*n0P(2I1-Ru)h7h4^Azvgd(#^ z7)i}wR#3BId#q9j<33}mX-Ru$=R&xMzfzNeKh{{!zG;Kf-0~@`m1Qw%@pDM4VCURD53Ei>q;D~Yb-_F(6*G5)QsAJt|2Z+?oocdK#bbH+O7 zzWo!KHi1Z=6NgbCX8~TdF0{WRa?VqVg!gEiG1ie&VX+{)H1c3=9Z!6?V6iZg`!pTEjI0=wRQ?ko88K$x&Ly*L}Qr zRs5QXb57O5CJzLy30r49wR5s(;{!d-`r?Ol;9*DkS~`%YMSlM#Q4?&bB3^S$?~#zo z(MJI!_3)h)=w^hCUP(G#C#J(gqOi1(s!gB$;A8Y7RQ!6CFLL!IGi!lqE=m3guU|961h#lKcubg4~+^qeG z>sxC_PT$0G*q2T5JhqDxaH?N^>GRz#_QisYSN_@_D4{glf&_QA*gs8^L?3EwzycO) zo%LWtaQ8su`iu2@H&%vBI=XtkaU8Yv|72l#r`U2cOg`oU``0?lDZ{U)9+q&U3O?Ls zyZbM1K&ykxqi@$E_0)j3E6wTnogzeed3xv23;NUY;m0?FwL9hYK)SLa>7N^vQhw_7 zw@PflHaa{=_-(!ENCUg_F5@6&+P$v#vPqWi+8*{m-LE-2qtrLX*be@Jc?UyHk5}}*y+iOVgCci3$$&UTbDB0RJu(LX_olPx$iIqW>zCvBfnwipc6<7pl&vq^g07m| zAQtGdviA8mE|Dn2KM(((1^@3j^AC%DfA}8>|KYd&Cr$moROz4eAEHIA?UttRZv%hS MRdg_U%65VO3$T4DwEzGB diff --git a/docs/source/user_guide/images/sparse.png b/docs/source/user_guide/images/sparse.png index ce5c9919ac6c78a5bdbaf4444f52c43cc81c2613..0cf1b98854dc7d94791b69b53ca63e98d0aaed52 100644 GIT binary patch literal 364152 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%0Amr} zySPI>S*xlz?N_j-r08*3l}b}~dotvegI4FozFjX)zrHf{>`Pwt$t@hBfbC%_YGehcfI8>N8_+GG_9 z>`6e?!%w7+8yz_Lke{h?u<;9dXFQ9%wN+zi$eX66B223P`ZoU}F_A!EKNcw0Pr6Tg zEj=zP^c56XV21u5SOqFQgO91j(F`qWFvWEJ#&t94C1)z_-=(7d2r89`H%%&qcm{u8gtrKg z)Dz``zXn_=Zq88_94abt#hrbaM+7}K5eCGyZuCkvpb_VLv_9kC7lGA|CNJMEagB^r zTlj-14uJ3HhrE-o_j3J}qd1S+aV)yojTEdEvKF2F)Zk*|NvAq(LIIy#Gq z`+c~7|4IQKgrHiVJ<(%O4J!HjB19-yM4mh#hPwtqD++`W5q(27Zo6K)Gk-4m_x_^c z50>ss+j(uV>up|^Y0t1C)|#lNxju{)YT504LacqrL+5QWtW3=e!9REv3rY$D5C>=dA&{V20XYeUMRi_TuZG7R4)W_L z6Xza{frb}BJ!2Aw!|nC-C!C-Vi6XgDgS{VKJ5!H7xw+E)`0{J(b)jbPAGn4tzsnF} z?hA*>YR0=$93l2@*kY#y>nXD)hX)ix(=cL0JDez0>Iwmd8w=u!tA

    Cv@3Fp(!3!+wNy-ue5{z{>eDR%mq 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°{lcht7`Ib(e_N4yL}5 z6wvHzf643Jx!f4z9IG`{TYLQ|8C#H!G9bD{7K@}KOqJhK{$CwlT1TtXiA!g2iKs(- zEPW-f(QN;a-v^kpS!t|_Mhsc0x7Lj9$<+MGt%_A0=VK{K(ayPIeU8kX% z*t-(@G80nUd#j{DUUrIQ+vm-U2z+GsGmVVCFCHS5 zoU)i!d84t!<$cwD*Lf~>)-~l3+rtx*1*uB;v{}OioX1YD?{LYO`@H?DknjR*&Awkr zY+MhEJ)!qX-S=;Bk#N~5=aRzv9C@gc-u_%06iH`SM====*Teh|S#(25i)%l=l$rO5~RQnyi|oDt9|2Q#h&=CilZgJQdnqF41%I@!|Is*V}ECJRo9H zvF6tK)}lzh&SNBNPI>9X^k-%;Ato?gv|C3bd zZKXaDRRQ*^EhJ3Bwr=eA>)>h8*oAf0@~2idEhe0U3Dusadld=j95_yo2XZE=8G}&5 z^;kvy40>Deg{M+*0-dsZiLx6Fw+fZlXC`LDtUKE!a$eq~tj!$#Mi;TgWIv26>ruQ& zo;_CSlp`~sZkhWw?kX!H#QV$W7dkiy70fK6!CAN0!WuO~%{)kTT-Z2t4pw*JTi9

    82}tc^5Zc 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 GockY%Fw(jJ literal 372764 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

    J;^M}49 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 diff --git a/docs/source/user_guide/vectorized_matrix_solver.rst b/docs/source/user_guide/vectorized_matrix_solver.rst index 931c8f866..c15de062d 100644 --- a/docs/source/user_guide/vectorized_matrix_solver.rst +++ b/docs/source/user_guide/vectorized_matrix_solver.rst @@ -5,7 +5,7 @@ Vectorized matrix solver Many of the tutorials have defined a chemical system without talking about their location. Each of these is essentially defining a `box model `_. We often want to model chemistry -in more than one location; multiple boxes. In weather and climate models, the domain is broken up into grids 2d or 3d grids. +in more than one location; multiple boxes. In weather and climate models, the domain is broken up into 2d or 3d grids. Each location may be referred to as a grid cell or column. With ``micm``, we can solve multiple grid cells simultaneously in several ways. @@ -50,30 +50,30 @@ This entire example is contained below. Copy and paste to follow along. Matrix types in micm -------------------- -``micm`` has its own matrix types, each providing a separate data layout. The data layouts are meant to provide efficient access -such that cache misses are minimized. This is done by placing all of the data into a contiguous vector, and optionally imposing -some sort of ordering on the data, like grouping species across gridcells together in memory. +``micm`` has its own matrix types. These provide dense matrices (for species concentrations, reaction rates, etc.), +and sparse matrices (for the Jacobian matrix). +The elements of these matrices are placed in a contiguous vector, in an effort to minimize cache misses. +Both dense and sparse matrices also have two strategies for element ordering available: standard and vector. +The vector-ordering strategy is intented to encourage vectorization of the solver code. +Dense and sparse matrix types are described in more detail below. -The dimensions of all ``micm`` -matrices are :math:`\mathrm{number\ of\ grid\ cells} \times \mathrm{number\ of\ species}` +Dense matrix +^^^^^^^^^^^^ +.. figure:: images/dense.png + :alt: dense matrix ordering + :figclass: transparent-image -* :cpp:class:`micm::Matrix`, a dense matrix, what you probably think of when you think of a matrix, with the caveat that all rows and columns live in one long array - -* :cpp:class:`micm::VectorMatrix`, the matrix type of interest here, where the data is ordered such that species across grid cells lie beside each other in memory + Element ordering for standard (left) and vector (right) ordered dense matrices. -* :cpp:class:`micm::SparseMatrix`, a `sparse matrix `_ data structure - * The sparse matrix also allows you to specify wether the data is ordered as a regular dense matrix, with zeros removed, or with a vectorized ordering, the topic of this tutorial. - - * :cpp:class:`micm::SparseMatrixStandardOrdering` +* :cpp:class:`micm::Matrix`, a dense matrix, what you probably think of when you think of a matrix, with the caveat that all rows and columns live in one long array - * :cpp:class:`micm::SparseMatrixVectorOrdering` +* :cpp:class:`micm::VectorMatrix`, the same as :cpp:class:`micm::Matrix`, except that the data is ordered such that columns, rather than rows, are contiguous in memory. -Regular, dense matrix ordering -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. image:: images/dense.png +Dense matrix: standard ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Up until now, we've mostly created our :cpp:class:`micm::RosenbrockSolver` solvers like this: @@ -87,7 +87,8 @@ and :cpp:class:`micm::SparseMatrix`. The most visible data affected by these par :cpp:member:`micm::State::variables_` parameter, representing the concnetration of each of the species. After we created that first ``solver``, if we get a state and set the concentration of the species and then print them, -they are printed in order of grid cell first and then species. Here, when setting the species, read the double as ``grid cell.species``. +they are printed in order of grid cell (columns) first and then species (rows). +Here, when setting the species, read the double as ``grid cell.species``. .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp :language: cpp @@ -105,12 +106,12 @@ they are printed in order of grid cell first and then species. Here, when settin 3.2 3.3 -Vectorized matrix ordering -^^^^^^^^^^^^^^^^^^^^^^^^^^ +Dense matrix: vector ordering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ But, if we create a vectorized solver, set the same concentrations and print out the state variables, we see that it is organized -first by species and then by grid cell. Note that we needed to set some partial template speciailizations at the top of the file -to create these. +first by species (rows) and then by grid cell (columns). +Note that we needed to set some partial template specializations at the top of the file to create these. At the top of the file: @@ -139,16 +140,10 @@ and then we pass these templates as the template arguments to the vectorized Ros And that's all you have to do to orgnaize the data by species first. By specifying the template parameter on the solver, each operation will use the same ordering for all of the data structures needed to solve the chemical system. - -Sparse matrix, standard ordering -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. image:: images/sparse.png - -Sparse matrix, vector ordering -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -You can use the vectorized just as you would the regular solver, and in fact the same output is produced. +It's important to note that regardless of the ordering policy, matrix elements can always be accessed using +standard square-bracket notation: ``matrix[column][row]``. +It's only when accessing the underlying data vector directly that you need to pay attention to the ordering strategy. +In fact, you can use the vectorized solver just as you would the regular solver, and the same output is produced. .. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp :language: cpp @@ -174,3 +169,81 @@ You can use the vectorized just as you would the regular solver, and in fact the B 1.82514e-06 1.82514e-06 C 6.5904 6.5904 + +Sparse matrix +^^^^^^^^^^^^^ + +The sparse matrix type in ``micm`` is more-specifically a block-diagonal sparse matrix, where each block along the +diagonal has the same sparsity structure. + +.. figure:: images/sparse.png + :alt: sparse matrix ordering + :figclass: transparent-image + + Element ordering for standard (left) and vector (right) ordered block-diagonal sparse matrices. + + +* :cpp:class:`micm::SparseMatrix`, a `sparse matrix `_ data structure + + * The sparse matrix allows you to select the standard or vector ordering by applying a ordering policy to the :cpp:class:`micm::SparseMatrix` type. + + * :cpp:class:`micm::SparseMatrixStandardOrdering` + + * :cpp:class:`micm::SparseMatrixVectorOrdering` + + +To demonstrate the effects of the ordering policy on sparse matrix objects, we create the same sparse matrix +using each ordering policy: + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 136-152 + +Standard ordering is the default, and thus does not have to be specified as a template parameter. +When creating vector-ordered matrices, specify the second template argument using +``micm::SparseMatrixVectorOrdering`` where ``L`` is the number of blocks (i.e. grid cells) in the +block diagonal matrix. + +Just like the dense matrix, the square bracket notation is always used as ``matrix[block][column][row]``. +We use this syntax to set the same values for each element of our two sparse matrices. +We created matrices of strings so the values can be set as ``block.column.row``: + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 154-165 + +We can again see the effects of the ordering policy by accessing the underlying data vector directly: + +.. literalinclude:: ../../../test/tutorial/test_vectorized_matrix_solver.cpp + :language: cpp + :lines: 167-176 + +.. code-block:: bash + + Sparse matrix standard ordering elements + 0.0.1 + 0.2.1 + 0.2.3 + 0.3.2 + 1.0.1 + 1.2.1 + 1.2.3 + 1.3.2 + 2.0.1 + 2.2.1 + 2.2.3 + 2.3.2 + + Sparse matrix vector ordering elements + 0.0.1 + 1.0.1 + 2.0.1 + 0.2.1 + 1.2.1 + 2.2.1 + 0.2.3 + 1.2.3 + 2.2.3 + 0.3.2 + 1.3.2 + 2.3.2 \ No newline at end of file diff --git a/test/tutorial/test_vectorized_matrix_solver.cpp b/test/tutorial/test_vectorized_matrix_solver.cpp index 6f6c21143..292e35ae2 100644 --- a/test/tutorial/test_vectorized_matrix_solver.cpp +++ b/test/tutorial/test_vectorized_matrix_solver.cpp @@ -132,4 +132,46 @@ int main() std::cout << std::endl; } + + micm::SparseMatrix sparse_matrix{ + micm::SparseMatrix::create(4) + .with_element(0, 1) + .with_element(2, 1) + .with_element(2, 3) + .with_element(3, 2) + .number_of_blocks(3) + }; + + micm::SparseMatrix> sparse_vector_matrix{ + micm::SparseMatrix>::create(4) + .with_element(0, 1) + .with_element(2, 1) + .with_element(2, 3) + .with_element(3, 2) + .number_of_blocks(3) + }; + + sparse_matrix[0][0][1] = sparse_vector_matrix[0][0][1] = "0.0.1"; + sparse_matrix[0][2][1] = sparse_vector_matrix[0][2][1] = "0.2.1"; + sparse_matrix[0][2][3] = sparse_vector_matrix[0][2][3] = "0.2.3"; + sparse_matrix[0][3][2] = sparse_vector_matrix[0][3][2] = "0.3.2"; + sparse_matrix[1][0][1] = sparse_vector_matrix[1][0][1] = "1.0.1"; + sparse_matrix[1][2][1] = sparse_vector_matrix[1][2][1] = "1.2.1"; + sparse_matrix[1][2][3] = sparse_vector_matrix[1][2][3] = "1.2.3"; + sparse_matrix[1][3][2] = sparse_vector_matrix[1][3][2] = "1.3.2"; + sparse_matrix[2][0][1] = sparse_vector_matrix[2][0][1] = "2.0.1"; + sparse_matrix[2][2][1] = sparse_vector_matrix[2][2][1] = "2.2.1"; + sparse_matrix[2][2][3] = sparse_vector_matrix[2][2][3] = "2.2.3"; + sparse_matrix[2][3][2] = sparse_vector_matrix[2][3][2] = "2.3.2"; + + std::cout << std::endl << "Sparse matrix standard ordering elements" << std::endl; + for (auto& elem : sparse_matrix.AsVector()) + { + std::cout << elem << std::endl; + } + std::cout << std::endl << "Sparse matrix vector ordering elements" << std::endl; + for (auto& elem : sparse_vector_matrix.AsVector()) + { + std::cout << elem << std::endl; + } } \ No newline at end of file From aabe856dc0040664ead7867a4d858ff281eb55c8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 13:12:34 -0800 Subject: [PATCH 269/318] Auto-format code changes (#364) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- .../test_vectorized_matrix_solver.cpp | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/test/tutorial/test_vectorized_matrix_solver.cpp b/test/tutorial/test_vectorized_matrix_solver.cpp index 292e35ae2..0bfeead52 100644 --- a/test/tutorial/test_vectorized_matrix_solver.cpp +++ b/test/tutorial/test_vectorized_matrix_solver.cpp @@ -133,23 +133,21 @@ int main() std::cout << std::endl; } - micm::SparseMatrix sparse_matrix{ - micm::SparseMatrix::create(4) - .with_element(0, 1) - .with_element(2, 1) - .with_element(2, 3) - .with_element(3, 2) - .number_of_blocks(3) - }; + micm::SparseMatrix sparse_matrix{ micm::SparseMatrix::create(4) + .with_element(0, 1) + .with_element(2, 1) + .with_element(2, 3) + .with_element(3, 2) + .number_of_blocks(3) }; micm::SparseMatrix> sparse_vector_matrix{ micm::SparseMatrix>::create(4) - .with_element(0, 1) - .with_element(2, 1) - .with_element(2, 3) - .with_element(3, 2) - .number_of_blocks(3) - }; + .with_element(0, 1) + .with_element(2, 1) + .with_element(2, 3) + .with_element(3, 2) + .number_of_blocks(3) + }; sparse_matrix[0][0][1] = sparse_vector_matrix[0][0][1] = "0.0.1"; sparse_matrix[0][2][1] = sparse_vector_matrix[0][2][1] = "0.2.1"; From c4f19831dfb142d826a2046fcd3a5f3196871968 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 17 Nov 2023 14:09:23 -0700 Subject: [PATCH 270/318] Added model and analytic concentration arrays. --- test/integration/analytic_surface_rxn.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/test/integration/analytic_surface_rxn.cpp b/test/integration/analytic_surface_rxn.cpp index 2ade667a7..cacc8dece 100644 --- a/test/integration/analytic_surface_rxn.cpp +++ b/test/integration/analytic_surface_rxn.cpp @@ -1,8 +1,9 @@ #include -#include #include -#include +#include #include +#include +#include int main(const int argc, const char* argv[]) { @@ -46,7 +47,7 @@ int main(const int argc, const char* argv[]) micm::Phase gas_phase{ std::vector{ foo, bar, baz } }; // System - auto chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + micm::System chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); // Rate micm::SurfaceRateConstant surface{ @@ -77,10 +78,22 @@ int main(const int argc, const char* argv[]) double time_step = 3600; // s int nstep = 24; + std::vector> model_conc(nstep, std::vector(3)); + std::vector> analytic_conc(nstep, std::vector(3)); + + model_conc[0] = {conc_foo, 0, 0}; + analytic_conc[0] = {conc_foo, 0, 0}; + + size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; + + std::vector times; + times.push_back(0); + for (int i = 0; i < nstep; ++i) { - double elapsed_solve_time = 0; + times.push_back(time_step); + double elapsed_solve_time = 0; while (elapsed_solve_time < time_step) { auto result = solver.Solve(time_step - elapsed_solve_time, state); From e0769f45c95c61b41d46fe4314311297c13bc790 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 17 Nov 2023 14:16:03 -0700 Subject: [PATCH 271/318] Include gtest.h. --- test/integration/analytic_surface_rxn.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/analytic_surface_rxn.cpp b/test/integration/analytic_surface_rxn.cpp index cacc8dece..54dff9c40 100644 --- a/test/integration/analytic_surface_rxn.cpp +++ b/test/integration/analytic_surface_rxn.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -99,6 +100,7 @@ int main(const int argc, const char* argv[]) auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; state.variables_ = result.result_; + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); } } From 95249ed4fccac8f3a6916cd8f6e3255861f34f80 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Fri, 17 Nov 2023 14:29:57 -0700 Subject: [PATCH 272/318] Save model conentrations in solve loop. --- test/integration/analytic_surface_rxn.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/integration/analytic_surface_rxn.cpp b/test/integration/analytic_surface_rxn.cpp index 54dff9c40..be5f2a5e4 100644 --- a/test/integration/analytic_surface_rxn.cpp +++ b/test/integration/analytic_surface_rxn.cpp @@ -95,13 +95,19 @@ int main(const int argc, const char* argv[]) times.push_back(time_step); double elapsed_solve_time = 0; + + // first iteration + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + // further iterations while (elapsed_solve_time < time_step) { - auto result = solver.Solve(time_step - elapsed_solve_time, state); + result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; state.variables_ = result.result_; - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); } + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + model_conc[i] = result.result_.AsVector(); } return 0; From 5710f878ecc2c621b31aeffae9805e3184f9586d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 08:12:57 -0700 Subject: [PATCH 273/318] Check surface rate constant calculation. --- test/integration/analytic_surface_rxn.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/integration/analytic_surface_rxn.cpp b/test/integration/analytic_surface_rxn.cpp index be5f2a5e4..b0b43b948 100644 --- a/test/integration/analytic_surface_rxn.cpp +++ b/test/integration/analytic_surface_rxn.cpp @@ -76,6 +76,13 @@ int main(const int argc, const char* argv[]) state.SetCustomRateParameter("foo.particle number concentration [# m-3]", number_conc); state.SetConcentration(foo, conc_foo); + + // Check surface reaction rate calculation + double mean_free_speed = std::sqrt(8.0 * GAS_CONSTANT / (M_PI * MW_foo) * temperature); + double k_surface_rxn = 4.0 * number_conc * M_PI * radius * radius / + (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); + + double time_step = 3600; // s int nstep = 24; @@ -107,6 +114,9 @@ int main(const int argc, const char* argv[]) state.variables_ = result.result_; } EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + std::cout << i << " " << k_surface_rxn << " " + << state.rate_constants_.AsVector()[0] << std::endl; + EXPECT_NEAR(k_surface_rxn, state.rate_constants_.AsVector()[0], 1e-8); model_conc[i] = result.result_.AsVector(); } From 93e5e1582ba48f6bc8e80e357ce5dc50a8ff604b Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 08:30:36 -0700 Subject: [PATCH 274/318] Adjust time step to 0.1 / k1. --- test/integration/analytic_surface_rxn.cpp | 31 ++++++++++++++--------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/test/integration/analytic_surface_rxn.cpp b/test/integration/analytic_surface_rxn.cpp index b0b43b948..5adc78f35 100644 --- a/test/integration/analytic_surface_rxn.cpp +++ b/test/integration/analytic_surface_rxn.cpp @@ -77,14 +77,14 @@ int main(const int argc, const char* argv[]) state.SetConcentration(foo, conc_foo); - // Check surface reaction rate calculation + // Surface reaction rate calculation double mean_free_speed = std::sqrt(8.0 * GAS_CONSTANT / (M_PI * MW_foo) * temperature); - double k_surface_rxn = 4.0 * number_conc * M_PI * radius * radius / + double k1 = 4.0 * number_conc * M_PI * radius * radius / (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); - double time_step = 3600; // s - int nstep = 24; + double time_step = 0.1 / k1; // s + int nstep = 10; std::vector> model_conc(nstep, std::vector(3)); std::vector> analytic_conc(nstep, std::vector(3)); @@ -94,18 +94,14 @@ int main(const int argc, const char* argv[]) size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; - std::vector times; - times.push_back(0); - for (int i = 0; i < nstep; ++i) { - times.push_back(time_step); - double elapsed_solve_time = 0; // first iteration auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; + // further iterations while (elapsed_solve_time < time_step) { @@ -114,10 +110,21 @@ int main(const int argc, const char* argv[]) state.variables_ = result.result_; } EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - std::cout << i << " " << k_surface_rxn << " " - << state.rate_constants_.AsVector()[0] << std::endl; - EXPECT_NEAR(k_surface_rxn, state.rate_constants_.AsVector()[0], 1e-8); + + // Check surface reaction rate calculation + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + model_conc[i] = result.result_.AsVector(); + + double time = i * time_step; + analytic_conc[i][idx_foo] = conc_foo * std::exp(-(k1)*time); + + std::cout << i << " " + << time << " " + << k1 << " " + << state.rate_constants_.AsVector()[0] << " " + << analytic_conc[i][idx_foo] << " " + << std::endl; } return 0; From 8f32e71eac7e1db1c568c1e4872eece817e5a278 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 08:31:58 -0700 Subject: [PATCH 275/318] Renamed file to analytical_surface_rxn.cpp. --- test/integration/CMakeLists.txt | 2 +- .../{analytic_surface_rxn.cpp => analytical_surface_rxn.cpp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename test/integration/{analytic_surface_rxn.cpp => analytical_surface_rxn.cpp} (100%) diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 163eb902d..0949d3255 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -9,7 +9,7 @@ include(test_util) create_standard_test(NAME chapman_integration SOURCES chapman.cpp) create_standard_test(NAME analytical_rosenbrock_integration SOURCES analytical_rosenbrock.cpp) create_standard_test(NAME terminator_integration SOURCES terminator.cpp) -create_standard_test(NAME analytical_surface_rxn SOURCES analytic_surface_rxn.cpp) +create_standard_test(NAME analytical_surface_rxn SOURCES analytical_surface_rxn.cpp) if(ENABLE_LLVM) create_standard_test(NAME analytical_jit_rosenbrock_integration SOURCES analytical_jit_rosenbrock.cpp) diff --git a/test/integration/analytic_surface_rxn.cpp b/test/integration/analytical_surface_rxn.cpp similarity index 100% rename from test/integration/analytic_surface_rxn.cpp rename to test/integration/analytical_surface_rxn.cpp From 07dac5f477f2f98cc8f52ddaccfd7a3473101ead Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 09:09:03 -0700 Subject: [PATCH 276/318] Use conc comparison arrays of length nstep + 1. --- test/integration/analytical_surface_rxn.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/test/integration/analytical_surface_rxn.cpp b/test/integration/analytical_surface_rxn.cpp index 5adc78f35..7e42e9c28 100644 --- a/test/integration/analytical_surface_rxn.cpp +++ b/test/integration/analytical_surface_rxn.cpp @@ -76,39 +76,40 @@ int main(const int argc, const char* argv[]) state.SetCustomRateParameter("foo.particle number concentration [# m-3]", number_conc); state.SetConcentration(foo, conc_foo); - // Surface reaction rate calculation double mean_free_speed = std::sqrt(8.0 * GAS_CONSTANT / (M_PI * MW_foo) * temperature); double k1 = 4.0 * number_conc * M_PI * radius * radius / (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); - double time_step = 0.1 / k1; // s int nstep = 10; - std::vector> model_conc(nstep, std::vector(3)); - std::vector> analytic_conc(nstep, std::vector(3)); + std::vector> model_conc(nstep + 1, std::vector(3)); + std::vector> analytic_conc(nstep + 1, std::vector(3)); model_conc[0] = {conc_foo, 0, 0}; analytic_conc[0] = {conc_foo, 0, 0}; size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; - for (int i = 0; i < nstep; ++i) + for (int i = 1; i <= nstep; ++i) { double elapsed_solve_time = 0; // first iteration auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; // further iterations + /* while (elapsed_solve_time < time_step) { result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; state.variables_ = result.result_; } + */ EXPECT_EQ(result.state_, (micm::SolverState::Converged)); // Check surface reaction rate calculation @@ -124,6 +125,7 @@ int main(const int argc, const char* argv[]) << k1 << " " << state.rate_constants_.AsVector()[0] << " " << analytic_conc[i][idx_foo] << " " + << model_conc[i][idx_foo] << " " << std::endl; } From 0b0840fa1ae1fb912b47f6f35f3f34e5e745daff Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 09:28:45 -0700 Subject: [PATCH 277/318] Format stdout. --- test/integration/analytical_surface_rxn.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/integration/analytical_surface_rxn.cpp b/test/integration/analytical_surface_rxn.cpp index 7e42e9c28..c202c56e6 100644 --- a/test/integration/analytical_surface_rxn.cpp +++ b/test/integration/analytical_surface_rxn.cpp @@ -120,12 +120,13 @@ int main(const int argc, const char* argv[]) double time = i * time_step; analytic_conc[i][idx_foo] = conc_foo * std::exp(-(k1)*time); - std::cout << i << " " - << time << " " - << k1 << " " - << state.rate_constants_.AsVector()[0] << " " - << analytic_conc[i][idx_foo] << " " - << model_conc[i][idx_foo] << " " + std::cout + << std::setw(3) << i << " " + << std::fixed << std::setprecision(2) + << std::setw(5) << time << " " + << std::fixed << std::setprecision(7) + << analytic_conc[i][idx_foo] << " " + << model_conc[i][idx_foo] << " " << std::endl; } From 89b89ceeaa9b503ac0c3906a005f088bd4a443b9 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 09:39:59 -0700 Subject: [PATCH 278/318] Format test output. --- test/integration/analytical_surface_rxn.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/integration/analytical_surface_rxn.cpp b/test/integration/analytical_surface_rxn.cpp index c202c56e6..2d1c78eae 100644 --- a/test/integration/analytical_surface_rxn.cpp +++ b/test/integration/analytical_surface_rxn.cpp @@ -92,6 +92,16 @@ int main(const int argc, const char* argv[]) size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; + std::cout << std::setw(3) << "i" + << std::setw(7) << "time" + << std::setw(11) << "anal foo" + << std::setw(11) << "model foo" + << std::setw(11) << "anal bar" + << std::setw(11) << "model bar" + << std::setw(11) << "anal baz" + << std::setw(11) << "model baz" + << std::endl; + for (int i = 1; i <= nstep; ++i) { double elapsed_solve_time = 0; @@ -118,7 +128,9 @@ int main(const int argc, const char* argv[]) model_conc[i] = result.result_.AsVector(); double time = i * time_step; - analytic_conc[i][idx_foo] = conc_foo * std::exp(-(k1)*time); + analytic_conc[i][idx_foo] = conc_foo * std::exp(- k1 * time); + analytic_conc[i][idx_bar] = bar_yield * (1.0 - analytic_conc[i][idx_foo]); + analytic_conc[i][idx_baz] = baz_yield * (1.0 - analytic_conc[i][idx_foo]); std::cout << std::setw(3) << i << " " @@ -127,6 +139,10 @@ int main(const int argc, const char* argv[]) << std::fixed << std::setprecision(7) << analytic_conc[i][idx_foo] << " " << model_conc[i][idx_foo] << " " + << analytic_conc[i][idx_bar] << " " + << model_conc[i][idx_bar] << " " + << analytic_conc[i][idx_baz] << " " + << model_conc[i][idx_baz] << " " << std::endl; } From 7fec54de0a3c8fa541ae1bf87c7025b3b2dea6f6 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 09:44:00 -0700 Subject: [PATCH 279/318] Check EXPECT_NEAR analytic and model concentrations. --- test/integration/analytical_surface_rxn.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/integration/analytical_surface_rxn.cpp b/test/integration/analytical_surface_rxn.cpp index 2d1c78eae..4fc7434fa 100644 --- a/test/integration/analytical_surface_rxn.cpp +++ b/test/integration/analytical_surface_rxn.cpp @@ -132,6 +132,11 @@ int main(const int argc, const char* argv[]) analytic_conc[i][idx_bar] = bar_yield * (1.0 - analytic_conc[i][idx_foo]); analytic_conc[i][idx_baz] = baz_yield * (1.0 - analytic_conc[i][idx_foo]); + // Check concentrations + EXPECT_NEAR(analytic_conc[i][idx_foo], model_conc[i][idx_foo], 1e-5); + EXPECT_NEAR(analytic_conc[i][idx_bar], model_conc[i][idx_bar], 1e-5); + EXPECT_NEAR(analytic_conc[i][idx_baz], model_conc[i][idx_baz], 1e-5); + std::cout << std::setw(3) << i << " " << std::fixed << std::setprecision(2) From c51ddfba093f2cc33fa13b750f94aa6c8d074e7c Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 09:53:15 -0700 Subject: [PATCH 280/318] Save. --- test/integration/analytical_policy.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/analytical_policy.hpp b/test/integration/analytical_policy.hpp index 5ce4b25ff..4ab97da9f 100644 --- a/test/integration/analytical_policy.hpp +++ b/test/integration/analytical_policy.hpp @@ -1687,4 +1687,4 @@ void test_analytical_robertson( EXPECT_NEAR(model_concentrations[i][_c], analytical_concentrations[i][2], tol) << "Arrays differ at index (" << i << ", " << 2 << ")"; } -} \ No newline at end of file +} From 968b23a31b6b131581e6624d353129803aa47967 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 12:04:09 -0700 Subject: [PATCH 281/318] Added file analytical_surface_rxn_policy.hpp. --- .../integration/analytical_jit_rosenbrock.cpp | 3 +- test/integration/analytical_rosenbrock.cpp | 1 + .../analytical_surface_rxn_policy.hpp | 156 ++++++++++++++++++ 3 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 test/integration/analytical_surface_rxn_policy.hpp diff --git a/test/integration/analytical_jit_rosenbrock.cpp b/test/integration/analytical_jit_rosenbrock.cpp index af14b137e..f062d058c 100644 --- a/test/integration/analytical_jit_rosenbrock.cpp +++ b/test/integration/analytical_jit_rosenbrock.cpp @@ -3,6 +3,7 @@ #include #include "analytical_policy.hpp" +#include "analytical_surface_rxn_policy.hpp" template using DefaultVectorMatrix = micm::VectorMatrix; @@ -235,4 +236,4 @@ TEST(AnalyticalExamplesJitRosenbrock, Robertson) jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; }); -} \ No newline at end of file +} diff --git a/test/integration/analytical_rosenbrock.cpp b/test/integration/analytical_rosenbrock.cpp index 6d72a32ce..222cf4b2a 100644 --- a/test/integration/analytical_rosenbrock.cpp +++ b/test/integration/analytical_rosenbrock.cpp @@ -4,6 +4,7 @@ #include #include "analytical_policy.hpp" +#include "analytical_surface_rxn_policy.hpp" #include "e5.hpp" #include "hires.hpp" #include "oregonator.hpp" diff --git a/test/integration/analytical_surface_rxn_policy.hpp b/test/integration/analytical_surface_rxn_policy.hpp new file mode 100644 index 000000000..35197bbc8 --- /dev/null +++ b/test/integration/analytical_surface_rxn_policy.hpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include + + +template +void test_analytical_surface_rxn( + const std::function&)> create_solver) +{ + // parameters, from CAMP/test/unit_rxn_data/test_rxn_surface.F90 + const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] + const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] + const double DENSITY_stuff = 1000.0; // [kg m-3] + const double DENSITY_more_stuff = 1000.0; // [kg m-3] + const double MW_stuff = 0.5; // [kg mol-1] + const double MW_more_stuff = 0.2; // [kg mol-1] + const double MW_foo = 0.04607; // [kg mol-1] + const double Dg_foo = 0.95e-5; // diffusion coefficient [m2 s-1] + const double rxn_gamma = 2.0e-2; // [unitless] + const double bar_yield = 1.0; // [unitless] + const double baz_yield = 0.4; // [unitless] + + // environment + const double temperature = 272.5; // temperature (K) + const double pressure = 101253.3; // pressure (Pa) + + // initial conditions + const double conc_foo = 1.0; + const double conc_stuff = 2.0e-3; + const double conc_more_stuff = 3.0e-3; + + // effective radius + double radius = mode_GMD / 2.0 * exp(5.0 * log(mode_GSD) * log(mode_GSD) / 2.0); + + // particle number concentration [# m-3] + double number_conc = 6.0 / (M_PI * std::pow(mode_GMD, 3.0) + * std::exp(9.0/2.0 * std::log(mode_GSD) * std::log(mode_GSD) )) + * (conc_stuff / DENSITY_stuff + conc_more_stuff / DENSITY_more_stuff); + + micm::Species foo("foo", + { { "molecular weight [kg mol-1]", MW_foo }, + { "diffusion coefficient [m2 s-1]", Dg_foo } }); + micm::Species bar("bar"); + micm::Species baz("baz"); + + // Phase + micm::Phase gas_phase{ std::vector{ foo, bar, baz } }; + + // System + micm::System chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); + + // Rate + micm::SurfaceRateConstant surface{ + { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + + // Process + micm::Process surface_process = micm::Process::create() + .reactants({ foo }) + .products({ micm::yields(bar, bar_yield), micm::yields(baz, baz_yield) }) + .rate_constant(surface) + .phase(gas_phase); + + auto reactions = std::vector{ surface_process }; + + // Solver + micm::RosenbrockSolver<> solver{ + chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + + // State + micm::State state = solver.GetState(); + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.SetCustomRateParameter("foo.effective radius [m]", radius); + state.SetCustomRateParameter("foo.particle number concentration [# m-3]", number_conc); + state.SetConcentration(foo, conc_foo); + + // Surface reaction rate calculation + double mean_free_speed = std::sqrt(8.0 * GAS_CONSTANT / (M_PI * MW_foo) * temperature); + double k1 = 4.0 * number_conc * M_PI * radius * radius / + (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); + + double time_step = 0.1 / k1; // s + int nstep = 10; + + std::vector> model_conc(nstep + 1, std::vector(3)); + std::vector> analytic_conc(nstep + 1, std::vector(3)); + + model_conc[0] = {conc_foo, 0, 0}; + analytic_conc[0] = {conc_foo, 0, 0}; + + size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; + + std::cout << std::setw(3) << "i" + << std::setw(7) << "time" + << std::setw(11) << "anal foo" + << std::setw(11) << "model foo" + << std::setw(11) << "anal bar" + << std::setw(11) << "model bar" + << std::setw(11) << "anal baz" + << std::setw(11) << "model baz" + << std::endl; + + for (int i = 1; i <= nstep; ++i) + { + double elapsed_solve_time = 0; + + // first iteration + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + + // further iterations + /* + while (elapsed_solve_time < time_step) + { + result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + */ + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); + + // Check surface reaction rate calculation + EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); + + model_conc[i] = result.result_.AsVector(); + + double time = i * time_step; + analytic_conc[i][idx_foo] = conc_foo * std::exp(- k1 * time); + analytic_conc[i][idx_bar] = bar_yield * (1.0 - analytic_conc[i][idx_foo]); + analytic_conc[i][idx_baz] = baz_yield * (1.0 - analytic_conc[i][idx_foo]); + + // Check concentrations + EXPECT_NEAR(analytic_conc[i][idx_foo], model_conc[i][idx_foo], 1e-5); + EXPECT_NEAR(analytic_conc[i][idx_bar], model_conc[i][idx_bar], 1e-5); + EXPECT_NEAR(analytic_conc[i][idx_baz], model_conc[i][idx_baz], 1e-5); + + std::cout + << std::setw(3) << i << " " + << std::fixed << std::setprecision(2) + << std::setw(5) << time << " " + << std::fixed << std::setprecision(7) + << analytic_conc[i][idx_foo] << " " + << model_conc[i][idx_foo] << " " + << analytic_conc[i][idx_bar] << " " + << model_conc[i][idx_bar] << " " + << analytic_conc[i][idx_baz] << " " + << model_conc[i][idx_baz] << " " + << std::endl; + } +} From 12286d67979202018d081fc6e1e2ea2bc102fcee Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 12:16:14 -0700 Subject: [PATCH 282/318] Use OdeSolverPolicy solver in analytical_surface_rxn_policy.hpp. --- test/integration/analytical_rosenbrock.cpp | 12 ++++++++++++ test/integration/analytical_surface_rxn_policy.hpp | 9 ++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/test/integration/analytical_rosenbrock.cpp b/test/integration/analytical_rosenbrock.cpp index 222cf4b2a..55549c7a2 100644 --- a/test/integration/analytical_rosenbrock.cpp +++ b/test/integration/analytical_rosenbrock.cpp @@ -168,6 +168,18 @@ TEST(AnalyticalExamples, Robertson) }); } +TEST(AnalyticalExamples, SurfaceRxn) +{ + test_analytical_surface_rxn>( + [](const micm::System& s, + const std::vector& p) -> micm::RosenbrockSolver + { + return micm::RosenbrockSolver{ + s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} + TEST(AnalyticalExamples, Oregonator) { /* diff --git a/test/integration/analytical_surface_rxn_policy.hpp b/test/integration/analytical_surface_rxn_policy.hpp index 35197bbc8..ba9242542 100644 --- a/test/integration/analytical_surface_rxn_policy.hpp +++ b/test/integration/analytical_surface_rxn_policy.hpp @@ -67,9 +67,12 @@ void test_analytical_surface_rxn( auto reactions = std::vector{ surface_process }; // Solver - micm::RosenbrockSolver<> solver{ - chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; + // micm::RosenbrockSolver<> solver{ + // chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + // }; + + // Solver + OdeSolverPolicy solver = create_solver(chemical_system, reactions); // State micm::State state = solver.GetState(); From 8277e5c20cd3876dd0af5bafdc93da4a15ef55a1 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 12:21:09 -0700 Subject: [PATCH 283/318] Added TEST(AnalyticalExamplesJitRosenbrock, SurfaceRxn). --- test/integration/analytical_jit_rosenbrock.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/integration/analytical_jit_rosenbrock.cpp b/test/integration/analytical_jit_rosenbrock.cpp index f062d058c..0c6672e25 100644 --- a/test/integration/analytical_jit_rosenbrock.cpp +++ b/test/integration/analytical_jit_rosenbrock.cpp @@ -237,3 +237,20 @@ TEST(AnalyticalExamplesJitRosenbrock, Robertson) }; }); } + +TEST(AnalyticalExamplesJitRosenbrock, SurfaceRxn) +{ + auto jit{ micm::JitCompiler::create() }; + if (auto err = jit.takeError()) + { + llvm::logAllUnhandledErrors(std::move(err), llvm::errs(), "[JIT Error]"); + EXPECT_TRUE(false); + } + test_analytical_surface_rxn( + [&](const micm::System& s, const std::vector& p) -> DefaultJitRosenbrockSolver + { + return DefaultJitRosenbrockSolver{ + jit.get(), s, p, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() + }; + }); +} From 414362e4df05796d31d96237856d6864f615c775 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 12:31:30 -0700 Subject: [PATCH 284/318] Removed cpp version of analytical_surface_rxn. --- test/integration/CMakeLists.txt | 1 - test/integration/analytical_surface_rxn.cpp | 155 -------------------- 2 files changed, 156 deletions(-) delete mode 100644 test/integration/analytical_surface_rxn.cpp diff --git a/test/integration/CMakeLists.txt b/test/integration/CMakeLists.txt index 0949d3255..c9e9ab9de 100644 --- a/test/integration/CMakeLists.txt +++ b/test/integration/CMakeLists.txt @@ -9,7 +9,6 @@ include(test_util) create_standard_test(NAME chapman_integration SOURCES chapman.cpp) create_standard_test(NAME analytical_rosenbrock_integration SOURCES analytical_rosenbrock.cpp) create_standard_test(NAME terminator_integration SOURCES terminator.cpp) -create_standard_test(NAME analytical_surface_rxn SOURCES analytical_surface_rxn.cpp) if(ENABLE_LLVM) create_standard_test(NAME analytical_jit_rosenbrock_integration SOURCES analytical_jit_rosenbrock.cpp) diff --git a/test/integration/analytical_surface_rxn.cpp b/test/integration/analytical_surface_rxn.cpp deleted file mode 100644 index 4fc7434fa..000000000 --- a/test/integration/analytical_surface_rxn.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -int main(const int argc, const char* argv[]) -{ - // parameters, from CAMP/test/unit_rxn_data/test_rxn_surface.F90 - const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] - const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] - const double DENSITY_stuff = 1000.0; // [kg m-3] - const double DENSITY_more_stuff = 1000.0; // [kg m-3] - const double MW_stuff = 0.5; // [kg mol-1] - const double MW_more_stuff = 0.2; // [kg mol-1] - const double MW_foo = 0.04607; // [kg mol-1] - const double Dg_foo = 0.95e-5; // diffusion coefficient [m2 s-1] - const double rxn_gamma = 2.0e-2; // [unitless] - const double bar_yield = 1.0; // [unitless] - const double baz_yield = 0.4; // [unitless] - - // environment - const double temperature = 272.5; // temperature (K) - const double pressure = 101253.3; // pressure (Pa) - - // initial conditions - const double conc_foo = 1.0; - const double conc_stuff = 2.0e-3; - const double conc_more_stuff = 3.0e-3; - - // effective radius - double radius = mode_GMD / 2.0 * exp(5.0 * log(mode_GSD) * log(mode_GSD) / 2.0); - - // particle number concentration [# m-3] - double number_conc = 6.0 / (M_PI * std::pow(mode_GMD, 3.0) - * std::exp(9.0/2.0 * std::log(mode_GSD) * std::log(mode_GSD) )) - * (conc_stuff / DENSITY_stuff + conc_more_stuff / DENSITY_more_stuff); - - micm::Species foo("foo", - { { "molecular weight [kg mol-1]", MW_foo }, - { "diffusion coefficient [m2 s-1]", Dg_foo } }); - micm::Species bar("bar"); - micm::Species baz("baz"); - - // Phase - micm::Phase gas_phase{ std::vector{ foo, bar, baz } }; - - // System - micm::System chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); - - // Rate - micm::SurfaceRateConstant surface{ - { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; - - // Process - micm::Process surface_process = micm::Process::create() - .reactants({ foo }) - .products({ micm::yields(bar, bar_yield), micm::yields(baz, baz_yield) }) - .rate_constant(surface) - .phase(gas_phase); - - auto reactions = std::vector{ surface_process }; - - // Solver - micm::RosenbrockSolver<> solver{ - chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - // State - micm::State state = solver.GetState(); - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.SetCustomRateParameter("foo.effective radius [m]", radius); - state.SetCustomRateParameter("foo.particle number concentration [# m-3]", number_conc); - state.SetConcentration(foo, conc_foo); - - // Surface reaction rate calculation - double mean_free_speed = std::sqrt(8.0 * GAS_CONSTANT / (M_PI * MW_foo) * temperature); - double k1 = 4.0 * number_conc * M_PI * radius * radius / - (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); - - double time_step = 0.1 / k1; // s - int nstep = 10; - - std::vector> model_conc(nstep + 1, std::vector(3)); - std::vector> analytic_conc(nstep + 1, std::vector(3)); - - model_conc[0] = {conc_foo, 0, 0}; - analytic_conc[0] = {conc_foo, 0, 0}; - - size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; - - std::cout << std::setw(3) << "i" - << std::setw(7) << "time" - << std::setw(11) << "anal foo" - << std::setw(11) << "model foo" - << std::setw(11) << "anal bar" - << std::setw(11) << "model bar" - << std::setw(11) << "anal baz" - << std::setw(11) << "model baz" - << std::endl; - - for (int i = 1; i <= nstep; ++i) - { - double elapsed_solve_time = 0; - - // first iteration - auto result = solver.Solve(time_step - elapsed_solve_time, state); - elapsed_solve_time = result.final_time_; - state.variables_ = result.result_; - - // further iterations - /* - while (elapsed_solve_time < time_step) - { - result = solver.Solve(time_step - elapsed_solve_time, state); - elapsed_solve_time = result.final_time_; - state.variables_ = result.result_; - } - */ - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); - - // Check surface reaction rate calculation - EXPECT_NEAR(k1, state.rate_constants_.AsVector()[0], 1e-8); - - model_conc[i] = result.result_.AsVector(); - - double time = i * time_step; - analytic_conc[i][idx_foo] = conc_foo * std::exp(- k1 * time); - analytic_conc[i][idx_bar] = bar_yield * (1.0 - analytic_conc[i][idx_foo]); - analytic_conc[i][idx_baz] = baz_yield * (1.0 - analytic_conc[i][idx_foo]); - - // Check concentrations - EXPECT_NEAR(analytic_conc[i][idx_foo], model_conc[i][idx_foo], 1e-5); - EXPECT_NEAR(analytic_conc[i][idx_bar], model_conc[i][idx_bar], 1e-5); - EXPECT_NEAR(analytic_conc[i][idx_baz], model_conc[i][idx_baz], 1e-5); - - std::cout - << std::setw(3) << i << " " - << std::fixed << std::setprecision(2) - << std::setw(5) << time << " " - << std::fixed << std::setprecision(7) - << analytic_conc[i][idx_foo] << " " - << model_conc[i][idx_foo] << " " - << analytic_conc[i][idx_bar] << " " - << model_conc[i][idx_bar] << " " - << analytic_conc[i][idx_baz] << " " - << model_conc[i][idx_baz] << " " - << std::endl; - } - - return 0; -} From 9bf48a69050004fed1225501b595e19874fc832d Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 17:34:14 -0700 Subject: [PATCH 285/318] Capture last JSON object on parse failure. --- include/micm/configure/solver_config.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index a23945926..b02fc772b 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -111,6 +112,9 @@ namespace micm static const inline std::string CAMP_DATA = "camp-data"; static const inline std::string TYPE = "type"; + // Error string + std::stringstream last_json_object_; + // Functions /// @brief Parse configures @@ -270,7 +274,10 @@ namespace micm } if (status != ConfigParseStatus::Success) + { + last_json_object_ << object.dump(4); break; + } } return status; @@ -338,7 +345,10 @@ namespace micm } if (status != ConfigParseStatus::Success) + { + last_json_object_ << object.dump(4); break; + } } return status; From 2d270fd6189ba395fb45fb5b7e9c307cb1ea2c79 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 17:52:20 -0700 Subject: [PATCH 286/318] Add last JSON object dump to error message. --- include/micm/configure/solver_config.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index b02fc772b..483139c0f 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -1109,7 +1109,8 @@ namespace micm if (last_parse_status_ != ConfigParseStatus::Success) { std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_); + configParseStatusToString(last_parse_status_) + + this->last_json_object_.str(); throw std::runtime_error(msg); } From 198ffebfcce3d4cef42403e4832a17e03adab68e Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 18:22:56 -0700 Subject: [PATCH 287/318] Added test for unknown key. --- test/unit/configure/test_solver_config.cpp | 7 + .../CAMP/camp_unknown_key/camp_mechanism.json | 144 ++++++++++++++++++ .../CAMP/camp_unknown_key/camp_species.json | 53 +++++++ .../CAMP/camp_unknown_key/config.json | 1 + 4 files changed, 205 insertions(+) create mode 100644 test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json create mode 100644 test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json create mode 100644 test/unit/unit_configs/CAMP/camp_unknown_key/config.json diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 1b406cf8d..837844734 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -16,6 +16,13 @@ TEST(SolverConfig, NoConfigFilesFound) EXPECT_EQ(micm::ConfigParseStatus::NoConfigFilesFound, status); } +TEST(SolverConfig, UnknownKey) +{ + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_unknown_key/config.json"); + EXPECT_EQ(micm::ConfigParseStatus::UnknownKey, status); +} + TEST(SolverConfig, ReadAndParseCAMPFiles) { micm::SolverConfig solverConfig{}; diff --git a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json new file mode 100644 index 000000000..18c99c037 --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json @@ -0,0 +1,144 @@ +{ + "camp-data" : [ + { + "name" : "Chapman", + "type" : "MECHANISM", + "reactions" : [ + { + "type" : "UNKNOWN", + "reactants" : { + "O2" : { } + }, + "products" : { + "O" : { "yield" : 2.0 } + }, + "MUSICA name" : "UNKNOWN" + }, + { + "type" : "PHOTOLYSIS", + "reactants" : { + "O2" : { } + }, + "products" : { + "O" : { "yield" : 2.0 } + }, + "MUSICA name" : "O2_1" + }, + { + "type" : "PHOTOLYSIS", + "reactants" : { + "O3" : { } + }, + "products" : { + "O1D" : { }, + "O2" : { } + }, + "MUSICA name" : "O3_1" + }, + { + "type" : "PHOTOLYSIS", + "reactants" : { + "O3" : { } + }, + "products" : { + "O" : { }, + "O2" : { } + }, + "MUSICA name" : "O3_2" + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O1D" : { }, + "N2" : { } + }, + "products" : { + "O" : { }, + "N2" : { } + }, + "A" : 2.15e-11, + "C" : 110.0 + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O1D" : { }, + "O2" : { } + }, + "products" : { + "O" : { }, + "O2" : { } + }, + "A" : 3.3e-11, + "C" : 55.0 + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O" : { }, + "O3" : { } + }, + "products" : { + "O2" : { "yield" : 2.0 } + }, + "A" : 8.0e-12, + "C" : -2060.00 + }, + { + "type" : "ARRHENIUS", + "reactants" : { + "O" : { }, + "O2" : { }, + "M" : { } + }, + "products" : { + "O3" : { }, + "M" : { } + }, + "A" : 6.0e-34, + "B" : 2.4 + }, + { + "type" : "EMISSION", + "species" : "O1D", + "MUSICA name" : "O1D" + }, + { + "type" : "EMISSION", + "species" : "O", + "MUSICA name" : "O" + }, + { + "type" : "EMISSION", + "species" : "O3", + "MUSICA name" : "O3" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "N2", + "MUSICA name" : "N2" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "O2", + "MUSICA name" : "O2" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "CO2", + "MUSICA name" : "CO2" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "Ar", + "MUSICA name" : "Ar" + }, + { + "type" : "FIRST_ORDER_LOSS", + "species" : "H2O", + "MUSICA name" : "H2O" + } + ] + } + ] +} diff --git a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json new file mode 100644 index 000000000..75fc517ac --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json @@ -0,0 +1,53 @@ +{ + "camp-data" : [ + { + "type" : "RELATIVE_TOLERANCE", + "value" : 1.0e-4 + }, + { + "name" : "M", + "type" : "CHEM_SPEC", + "tracer type" : "CONSTANT" + }, + { + "name" : "Ar", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "CO2", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "H2O", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "N2", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O1D", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O2", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "O3", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + } + ] +} \ No newline at end of file diff --git a/test/unit/unit_configs/CAMP/camp_unknown_key/config.json b/test/unit/unit_configs/CAMP/camp_unknown_key/config.json new file mode 100644 index 000000000..95836f060 --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_unknown_key/config.json @@ -0,0 +1 @@ +{"camp-files": ["camp_species.json", "camp_mechanism.json"]} From 69e2951fb70dd91798ee1815605f93464470cd21 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 18:35:56 -0700 Subject: [PATCH 288/318] Save. --- include/micm/configure/solver_config.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 483139c0f..5285ed2af 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -1111,6 +1111,7 @@ namespace micm std::string msg = "Parsing configuration files failed. The parsing failed with error: " + configParseStatusToString(last_parse_status_) + this->last_json_object_.str(); + std::cerr << msg << std::endl; throw std::runtime_error(msg); } From 9c537dde619c05e82fb7e5b053a5f378e008d18c Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 18:54:20 -0700 Subject: [PATCH 289/318] Simplified unknown type JSON. --- test/unit/configure/test_solver_config.cpp | 7 + .../CAMP/camp_unknown_key/camp_mechanism.json | 128 +----------------- .../CAMP/camp_unknown_key/camp_species.json | 40 +----- 3 files changed, 12 insertions(+), 163 deletions(-) diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 837844734..f62e69914 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -21,6 +21,13 @@ TEST(SolverConfig, UnknownKey) micm::SolverConfig solverConfig{}; auto status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_unknown_key/config.json"); EXPECT_EQ(micm::ConfigParseStatus::UnknownKey, status); + try + { + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + } + catch (...) + { + } } TEST(SolverConfig, ReadAndParseCAMPFiles) diff --git a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json index 18c99c037..85df7aea4 100644 --- a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json +++ b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json @@ -7,136 +7,12 @@ { "type" : "UNKNOWN", "reactants" : { - "O2" : { } + "A" : { } }, "products" : { - "O" : { "yield" : 2.0 } + "B" : { "yield" : 2.0 } }, "MUSICA name" : "UNKNOWN" - }, - { - "type" : "PHOTOLYSIS", - "reactants" : { - "O2" : { } - }, - "products" : { - "O" : { "yield" : 2.0 } - }, - "MUSICA name" : "O2_1" - }, - { - "type" : "PHOTOLYSIS", - "reactants" : { - "O3" : { } - }, - "products" : { - "O1D" : { }, - "O2" : { } - }, - "MUSICA name" : "O3_1" - }, - { - "type" : "PHOTOLYSIS", - "reactants" : { - "O3" : { } - }, - "products" : { - "O" : { }, - "O2" : { } - }, - "MUSICA name" : "O3_2" - }, - { - "type" : "ARRHENIUS", - "reactants" : { - "O1D" : { }, - "N2" : { } - }, - "products" : { - "O" : { }, - "N2" : { } - }, - "A" : 2.15e-11, - "C" : 110.0 - }, - { - "type" : "ARRHENIUS", - "reactants" : { - "O1D" : { }, - "O2" : { } - }, - "products" : { - "O" : { }, - "O2" : { } - }, - "A" : 3.3e-11, - "C" : 55.0 - }, - { - "type" : "ARRHENIUS", - "reactants" : { - "O" : { }, - "O3" : { } - }, - "products" : { - "O2" : { "yield" : 2.0 } - }, - "A" : 8.0e-12, - "C" : -2060.00 - }, - { - "type" : "ARRHENIUS", - "reactants" : { - "O" : { }, - "O2" : { }, - "M" : { } - }, - "products" : { - "O3" : { }, - "M" : { } - }, - "A" : 6.0e-34, - "B" : 2.4 - }, - { - "type" : "EMISSION", - "species" : "O1D", - "MUSICA name" : "O1D" - }, - { - "type" : "EMISSION", - "species" : "O", - "MUSICA name" : "O" - }, - { - "type" : "EMISSION", - "species" : "O3", - "MUSICA name" : "O3" - }, - { - "type" : "FIRST_ORDER_LOSS", - "species" : "N2", - "MUSICA name" : "N2" - }, - { - "type" : "FIRST_ORDER_LOSS", - "species" : "O2", - "MUSICA name" : "O2" - }, - { - "type" : "FIRST_ORDER_LOSS", - "species" : "CO2", - "MUSICA name" : "CO2" - }, - { - "type" : "FIRST_ORDER_LOSS", - "species" : "Ar", - "MUSICA name" : "Ar" - }, - { - "type" : "FIRST_ORDER_LOSS", - "species" : "H2O", - "MUSICA name" : "H2O" } ] } diff --git a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json index 75fc517ac..ac75910b9 100644 --- a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json +++ b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_species.json @@ -1,53 +1,19 @@ { "camp-data" : [ - { - "type" : "RELATIVE_TOLERANCE", - "value" : 1.0e-4 - }, { "name" : "M", "type" : "CHEM_SPEC", "tracer type" : "CONSTANT" }, { - "name" : "Ar", - "type" : "CHEM_SPEC", - "absolute tolerance" : 1.0e-12 - }, - { - "name" : "CO2", - "type" : "CHEM_SPEC", - "absolute tolerance" : 1.0e-12 - }, - { - "name" : "H2O", - "type" : "CHEM_SPEC", - "absolute tolerance" : 1.0e-12 - }, - { - "name" : "N2", - "type" : "CHEM_SPEC", - "absolute tolerance" : 1.0e-12 - }, - { - "name" : "O1D", - "type" : "CHEM_SPEC", - "absolute tolerance" : 1.0e-12 - }, - { - "name" : "O", - "type" : "CHEM_SPEC", - "absolute tolerance" : 1.0e-12 - }, - { - "name" : "O2", + "name" : "A", "type" : "CHEM_SPEC", "absolute tolerance" : 1.0e-12 }, { - "name" : "O3", + "name" : "B", "type" : "CHEM_SPEC", "absolute tolerance" : 1.0e-12 } ] -} \ No newline at end of file +} From 8b5df488faa34a063621d6c59b3a403839a4689a Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 19:00:43 -0700 Subject: [PATCH 290/318] Format last JSON. --- include/micm/configure/solver_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 5285ed2af..661b0424b 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -1109,7 +1109,7 @@ namespace micm if (last_parse_status_ != ConfigParseStatus::Success) { std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_) + + configParseStatusToString(last_parse_status_) + "\n" + this->last_json_object_.str(); std::cerr << msg << std::endl; throw std::runtime_error(msg); From 408821b08e7dfd97456451ebecce4225465f5cf4 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 19:38:32 -0700 Subject: [PATCH 291/318] Added valid reaction. --- include/micm/configure/solver_config.hpp | 4 ++-- .../CAMP/camp_unknown_key/camp_mechanism.json | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 661b0424b..6222db42c 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -275,7 +275,7 @@ namespace micm if (status != ConfigParseStatus::Success) { - last_json_object_ << object.dump(4); + last_json_object_ << object.dump(4) << std::endl; break; } } @@ -346,7 +346,7 @@ namespace micm if (status != ConfigParseStatus::Success) { - last_json_object_ << object.dump(4); + last_json_object_ << object.dump(4) << std::endl; break; } } diff --git a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json index 85df7aea4..9d7d2f408 100644 --- a/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json +++ b/test/unit/unit_configs/CAMP/camp_unknown_key/camp_mechanism.json @@ -4,6 +4,16 @@ "name" : "Chapman", "type" : "MECHANISM", "reactions" : [ + { + "type" : "PHOTOLYSIS", + "reactants" : { + "A" : { } + }, + "products" : { + "B" : { "yield" : 2.0 } + }, + "MUSICA name" : "R_AB" + }, { "type" : "UNKNOWN", "reactants" : { From a378efaac0bbd879b5c00012079ab4e6f7e8b5c7 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 19:58:02 -0700 Subject: [PATCH 292/318] Save. --- include/micm/configure/solver_config.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 6222db42c..350f8a8d1 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -346,6 +346,7 @@ namespace micm if (status != ConfigParseStatus::Success) { + std::cout << "TEST" << std::endl; last_json_object_ << object.dump(4) << std::endl; break; } From dc662bf37e9eec957f2c84ab804481c9b934a1a7 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Sun, 19 Nov 2023 20:13:52 -0700 Subject: [PATCH 293/318] Save. --- include/micm/configure/solver_config.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 350f8a8d1..4ebcf22d1 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -346,7 +346,7 @@ namespace micm if (status != ConfigParseStatus::Success) { - std::cout << "TEST" << std::endl; + std::cerr << type << std::endl; last_json_object_ << object.dump(4) << std::endl; break; } From 1ae1ec08f56feac1a1664a7db4e4e00adc5223bb Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 20 Nov 2023 08:02:26 -0700 Subject: [PATCH 294/318] Omit long MECHANISM lists in last JSON error messages. --- include/micm/configure/solver_config.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 4ebcf22d1..0741ebad4 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -346,8 +346,10 @@ namespace micm if (status != ConfigParseStatus::Success) { - std::cerr << type << std::endl; - last_json_object_ << object.dump(4) << std::endl; + if (type != "MECHANISM") + { + last_json_object_ << object.dump(4) << std::endl; + } break; } } From 1ba7cf535b0160c80a7d5d04e0d3adb61eeacddc Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 20 Nov 2023 14:01:07 -0700 Subject: [PATCH 295/318] In TEST(SolverConfig, UnknownKey), catch std::runtime_error. --- test/unit/configure/test_solver_config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index f62e69914..59043266d 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -25,7 +25,7 @@ TEST(SolverConfig, UnknownKey) { micm::SolverParameters solver_params = solverConfig.GetSolverParams(); } - catch (...) + catch (std::runtime_error) { } } From e1f22598054db3475f89662d83c4ee07b79081c8 Mon Sep 17 00:00:00 2001 From: David Fillmore Date: Mon, 20 Nov 2023 14:14:09 -0700 Subject: [PATCH 296/318] Added additional unit test for bad JSON output. --- test/unit/configure/test_solver_config.cpp | 14 +++++++++++++ .../CAMP/camp_bad_type/camp_mechanism.json | 20 +++++++++++++++++++ .../CAMP/camp_bad_type/camp_species.json | 19 ++++++++++++++++++ .../CAMP/camp_bad_type/config.json | 1 + 4 files changed, 54 insertions(+) create mode 100644 test/unit/unit_configs/CAMP/camp_bad_type/camp_mechanism.json create mode 100644 test/unit/unit_configs/CAMP/camp_bad_type/camp_species.json create mode 100644 test/unit/unit_configs/CAMP/camp_bad_type/config.json diff --git a/test/unit/configure/test_solver_config.cpp b/test/unit/configure/test_solver_config.cpp index 59043266d..16777202f 100644 --- a/test/unit/configure/test_solver_config.cpp +++ b/test/unit/configure/test_solver_config.cpp @@ -30,6 +30,20 @@ TEST(SolverConfig, UnknownKey) } } +TEST(SolverConfig, BadType) +{ + micm::SolverConfig solverConfig{}; + auto status = solverConfig.ReadAndParse("./unit_configs/CAMP/camp_bad_type/config.json"); + EXPECT_EQ(micm::ConfigParseStatus::UnknownKey, status); + try + { + micm::SolverParameters solver_params = solverConfig.GetSolverParams(); + } + catch (std::runtime_error) + { + } +} + TEST(SolverConfig, ReadAndParseCAMPFiles) { micm::SolverConfig solverConfig{}; diff --git a/test/unit/unit_configs/CAMP/camp_bad_type/camp_mechanism.json b/test/unit/unit_configs/CAMP/camp_bad_type/camp_mechanism.json new file mode 100644 index 000000000..b92e49014 --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_bad_type/camp_mechanism.json @@ -0,0 +1,20 @@ +{ + "camp-data" : [ + { + "name" : "Chapman", + "type" : "MECHANISM", + "reactions" : [ + { + "type" : "PHOTOLYSIS", + "reactants" : { + "A" : { } + }, + "products" : { + "B" : { "yield" : 2.0 } + }, + "MUSICA name" : "R_AB" + } + ] + } + ] +} diff --git a/test/unit/unit_configs/CAMP/camp_bad_type/camp_species.json b/test/unit/unit_configs/CAMP/camp_bad_type/camp_species.json new file mode 100644 index 000000000..d3ac03536 --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_bad_type/camp_species.json @@ -0,0 +1,19 @@ +{ + "camp-data" : [ + { + "name" : "A", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "B", + "type" : "CHEM_SPEC", + "absolute tolerance" : 1.0e-12 + }, + { + "name" : "M", + "type" : "MOLECULE", + "tracer type" : "CONSTANT" + } + ] +} diff --git a/test/unit/unit_configs/CAMP/camp_bad_type/config.json b/test/unit/unit_configs/CAMP/camp_bad_type/config.json new file mode 100644 index 000000000..95836f060 --- /dev/null +++ b/test/unit/unit_configs/CAMP/camp_bad_type/config.json @@ -0,0 +1 @@ +{"camp-files": ["camp_species.json", "camp_mechanism.json"]} From 3e253e654b69879011c2ecce1d0c2c561aa7a433 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Tue, 21 Nov 2023 17:24:25 +0000 Subject: [PATCH 297/318] Auto-format code using Clang-Format --- include/micm/configure/solver_config.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 0741ebad4..3d5621278 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -22,6 +21,7 @@ #include #include #include +#include namespace micm { @@ -1112,8 +1112,7 @@ namespace micm if (last_parse_status_ != ConfigParseStatus::Success) { std::string msg = "Parsing configuration files failed. The parsing failed with error: " + - configParseStatusToString(last_parse_status_) + "\n" + - this->last_json_object_.str(); + configParseStatusToString(last_parse_status_) + "\n" + this->last_json_object_.str(); std::cerr << msg << std::endl; throw std::runtime_error(msg); } From 5212eafcca488eb95bf831c55c25dc31b95af6c8 Mon Sep 17 00:00:00 2001 From: Qina Tan <72781532+qinatan@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:32:56 -0700 Subject: [PATCH 298/318] Cuda solver (#358) * cuda solve done - no testing yet * argument changed * test added for solve() * test * fixing bug * fixing bug * fixing bug * modified variable name * fixing bug * fixing debug * fixing bug * added function call * added kernel call * fixing bug * fixing bug * fixing bug * fixing bugs * fixing test template * added constructor * modified constructor * modified constructor * modified constructor * modified test * modified test * modified test * modified constructor * test * modified constructor * modified constructor * test * test * test * modified constructor * added CudaLuDecomposition Constructor * test * test * added header * test * test * test * modified * test * test * ready to test * bug * segment fault fixed? * segfault fixed? * memory issue * memory issue fixed? * test segfault * test memory * test memory * print * test on gpu node * assignment after memory allocation * print * print * no array access * no memory copy * uncomment * test * run on cpu only * debug cpu code * empty function * add constructor * changed to LuDecomposition * test with LuDecomposition class * without kernel * finding segment fault * minor change * minor change * finding segment fault * finding segment fault * finding segment fault * finding segment fault * finding segment fault * finding segment fault * no cudafree * find memory fault * find memory fault * find memory fault * find memory fault * find memory fault * find memory fault * find memory fault * runnning all tests * clean up print statements * added time measurement * added bit by bit GPU accuracy testing.. not yet merge main * added licent * added endif * fixing bug * removed used_cuda * no gpu validation test * no gpu validation * modified solve function * added gpuValidation * added licent * uncommented cudafreee * used #pramga once * used #pramga once --- include/micm/solver/cuda_linear_solver.cuh | 17 + include/micm/solver/cuda_linear_solver.hpp | 56 +++ include/micm/solver/cuda_lu_decomposition.hpp | 90 ++--- include/micm/solver/linear_solver.hpp | 2 +- include/micm/solver/linear_solver.inl | 1 - include/micm/util/cuda_param.hpp | 141 ++++--- src/solver/CMakeLists.txt | 1 + src/solver/linear_solver.cu | 157 ++++++++ src/solver/lu_decomposition.cu | 357 +++++++++--------- test/unit/solver/CMakeLists.txt | 1 + test/unit/solver/test_cuda_linear_solver.cpp | 164 ++++++++ .../solver/test_cuda_lu_decomposition.cpp | 2 +- 12 files changed, 694 insertions(+), 295 deletions(-) create mode 100644 include/micm/solver/cuda_linear_solver.cuh create mode 100644 include/micm/solver/cuda_linear_solver.hpp create mode 100644 src/solver/linear_solver.cu create mode 100644 test/unit/solver/test_cuda_linear_solver.cpp diff --git a/include/micm/solver/cuda_linear_solver.cuh b/include/micm/solver/cuda_linear_solver.cuh new file mode 100644 index 000000000..f2014dbb0 --- /dev/null +++ b/include/micm/solver/cuda_linear_solver.cuh @@ -0,0 +1,17 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include +#include +namespace micm +{ + namespace cuda + { + std::chrono::nanoseconds SolveKernelDriver( + CudaLinearSolverParam& linearSolver, + CudaSparseMatrixParam& sparseMatrix, + CudaMatrixParam& denseMatrix); + } // namespace cuda +} // namespace micm diff --git a/include/micm/solver/cuda_linear_solver.hpp b/include/micm/solver/cuda_linear_solver.hpp new file mode 100644 index 000000000..6b7190d61 --- /dev/null +++ b/include/micm/solver/cuda_linear_solver.hpp @@ -0,0 +1,56 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace micm{ + template class SparseMatrixPolicy, class LuDecompositionPolicy = CudaLuDecomposition> + class CudaLinearSolver : public LinearSolver { + public: + //constructor + CudaLinearSolver(){}; + + CudaLinearSolver(const SparseMatrixPolicy& matrix, T initial_value): LinearSolver (matrix, initial_value){}; + + CudaLinearSolver(const SparseMatrixPolicy& matrix, T initial_value, const std::function&)> create_lu_decomp) + : linearSolver(matrix, initial_value, create_lu_decomp); + + template class MatrixPolicy > + requires(VectorizableDense> || VectorizableSparse>) + std::chrono::nanoseconds Solve(const MatrixPolicy&b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, + SparseMatrixPolicy& upper_matrix) + { + CudaLinearSolverParam linearSolver; + CudaSparseMatrixParam sparseMatrix; + CudaMatrixParam denseMatrix; + linearSolver.nLij_Lii_ = this->nLij_Lii_.data(); + linearSolver.nLij_Lii_size_ = this->nLij_Lii_.size(); + linearSolver.Lij_yj_= this->Lij_yj_.data(); + linearSolver.Lij_yj_size_ = this->Lij_yj_.size(); + linearSolver.nUij_Uii_ = this->nUij_Uii_.data(); + linearSolver.nUij_Uii_size_ = this->nUij_Uii_.size(); + linearSolver.Uij_xj_ = this->Uij_xj_.data(); + linearSolver.Uij_xj_size_ = this->Uij_xj_.size(); + sparseMatrix.lower_matrix_ = lower_matrix.AsVector().data(); + sparseMatrix.lower_matrix_size_ = lower_matrix.AsVector().size(); + sparseMatrix.upper_matrix_ = upper_matrix.AsVector().data(); + sparseMatrix.upper_matrix_size_ = upper_matrix.AsVector().size(); + denseMatrix.b_ = b.AsVector().data(); + denseMatrix.x_ = x.AsVector().data(); + denseMatrix.b_size_= b.AsVector().size(); + denseMatrix.x_size_= x.AsVector().size(); + denseMatrix.n_grids_ = b.size(); //number of grids + denseMatrix.b_column_counts_ = b[0].size(); + denseMatrix.x_column_counts_= x[0].size(); + //calling kernel driver + return micm::cuda::SolveKernelDriver(linearSolver, sparseMatrix, denseMatrix); + }; + }; +}//end micm diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index 118a64fe7..710b63604 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -9,12 +9,14 @@ #include #include -namespace micm -{ - class CudaLuDecomposition : public LuDecomposition - { - public: - CudaLuDecomposition(){}; + +namespace micm{ + class CudaLuDecomposition: public LuDecomposition{ + public: + CudaLuDecomposition(){}; + + template + CudaLuDecomposition(const SparseMatrix& matrix): LuDecomposition(matrix){}; template typename SparseMatrixPolicy> requires VectorizableSparse> std::chrono::nanoseconds @@ -22,44 +24,46 @@ namespace micm const; }; - template class SparseMatrixPolicy> - requires(VectorizableSparse>) std::chrono::nanoseconds - CudaLuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) - const - { - CudaSparseMatrixParam sparseMatrix; - sparseMatrix.A_ = A.AsVector().data(); - sparseMatrix.A_size_ = A.AsVector().size(); - sparseMatrix.L_ = L.AsVector().data(); - sparseMatrix.L_size_ = L.AsVector().size(); - sparseMatrix.U_ = U.AsVector().data(); - sparseMatrix.U_size_ = U.AsVector().size(); - sparseMatrix.n_grids_ = A.size(); - - CudaSolverParam solver; - solver.do_aik_ = do_aik_.data(); - solver.do_aik_size_ = do_aik_.size(); - solver.aik_ = aik_.data(); - solver.aik_size_ = aik_.size(); - solver.do_aki_ = do_aki_.data(); - solver.do_aki_size_ = do_aki_.size(); - solver.aki_ = aki_.data(); - solver.aki_size_ = aki_.size(); - solver.uii_ = uii_.data(); - solver.uii_size_ = uii_.size(); - - solver.niLU_ = niLU_.data(); - solver.niLU_size_ = niLU_.size(); - solver.uik_nkj_ = uik_nkj_.data(); - solver.uik_nkj_size_ = uik_nkj_.size(); - solver.lij_ujk_ = lij_ujk_.data(); - solver.lij_ujk_size_ = lij_ujk_.size(); - solver.lki_nkj_ = lki_nkj_.data(); - solver.lki_nkj_size_ = lki_nkj_.size(); - solver.lkj_uji_ = lkj_uji_.data(); - solver.lkj_uji_size_ = lkj_uji_.size(); + template class SparseMatrixPolicy> + requires(VectorizableSparse>) + std::chrono::nanoseconds CudaLuDecomposition::Decompose( + const SparseMatrixPolicy& A, + SparseMatrixPolicy& L, + SparseMatrixPolicy& U) const + { + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.A_ = A.AsVector().data(); + sparseMatrix.A_size_ = A.AsVector().size(); + sparseMatrix.L_ = L.AsVector().data(); + sparseMatrix.L_size_ = L.AsVector().size(); + sparseMatrix.U_ = U.AsVector().data(); + sparseMatrix.U_size_ = U.AsVector().size(); + sparseMatrix.n_grids_ = A.size(); + + CudaSolverParam solver; + solver.do_aik_ = this->do_aik_.data(); + solver.do_aik_size_ = this->do_aik_.size(); + solver.aik_ = this->aik_.data(); + solver.aik_size_ = this->aik_.size(); + solver.do_aki_ = this->do_aki_.data(); + solver.do_aki_size_ = this->do_aki_.size(); + solver.aki_ = this->aki_.data(); + solver.aki_size_ = this->aki_.size(); + solver.uii_ = this->uii_.data(); + solver.uii_size_ = this->uii_.size(); + + solver.niLU_ = this->niLU_.data(); + solver.niLU_size_ = this->niLU_.size(); + solver.uik_nkj_ = this->uik_nkj_.data(); + solver.uik_nkj_size_ = this->uik_nkj_.size(); + solver.lij_ujk_ = this->lij_ujk_.data(); + solver.lij_ujk_size_ = this->lij_ujk_.size(); + solver.lki_nkj_ = this->lki_nkj_.data(); + solver.lki_nkj_size_ = this->lki_nkj_.size(); + solver.lkj_uji_ = this->lkj_uji_.data(); + solver.lkj_uji_size_ = this->lkj_uji_.size(); // calling kernelSetup function return micm::cuda::DecomposeKernelDriver(sparseMatrix, solver); } -} // namespace micm \ No newline at end of file +} // namespace micm diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index 9af334502..150cf0276 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -50,7 +50,7 @@ namespace micm LuDecompositionPolicy lu_decomp_; - public: + public: /// @brief default constructor LinearSolver(){}; diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index 741958ff6..74d2724c1 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -3,7 +3,6 @@ namespace micm { - template class MatrixPolicy> inline std::vector DiagonalMarkowitzReorder(const MatrixPolicy& matrix) { diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index 759e2bf4b..411920294 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -1,69 +1,88 @@ // Copyright (C) 2023 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 + #pragma once +#include -#include + const size_t BLOCK_SIZE = 320; + //member data of class CudaProcessSet grouped in struct passing to kernel driver function + struct CudaProcessSetParam{ + const size_t* number_of_reactants_; + const size_t* reactant_ids_; + size_t reactant_ids_size_; + const size_t* number_of_products_; + const size_t* product_ids_; + size_t product_ids_size_; + const double* yields_; + size_t yields_size_; + const size_t* jacobian_flat_ids_; + size_t jacobian_flat_ids_size_; +}; -// member data of class CudaProcessSet grouped in struct passing to kernel driver function -const size_t BLOCK_SIZE = 320; -struct CudaProcessSetParam -{ - const size_t* number_of_reactants_; - const size_t* reactant_ids_; - size_t reactant_ids_size_; - const size_t* number_of_products_; - const size_t* product_ids_; - size_t product_ids_size_; - const double* yields_; - size_t yields_size_; - const size_t* jacobian_flat_ids_; - size_t jacobian_flat_ids_size_; -}; + struct CudaSolverParam{ + const std::pair* niLU_; + size_t niLU_size_; + const char* do_aik_; + size_t do_aik_size_; + const size_t* aik_; + size_t aik_size_; + const std::pair* uik_nkj_; + size_t uik_nkj_size_; + const std::pair* lij_ujk_; + size_t lij_ujk_size_; + const char* do_aki_; + size_t do_aki_size_; + const size_t* aki_; + size_t aki_size_; + const std::pair* lki_nkj_; + size_t lki_nkj_size_; + const std::pair* lkj_uji_; + size_t lkj_uji_size_; + const size_t* uii_; + size_t uii_size_; + }; + + struct CudaLinearSolverParam{ + const std::pair* nLij_Lii_; + size_t nLij_Lii_size_; + const std::pair* Lij_yj_; + size_t Lij_yj_size_; + const std::pair* nUij_Uii_; + size_t nUij_Uii_size_; + const std::pair< size_t, size_t>* Uij_xj_; + size_t Uij_xj_size_; + }; -struct CudaSolverParam -{ - const std::pair* niLU_; - size_t niLU_size_; - const char* do_aik_; - size_t do_aik_size_; - const size_t* aik_; - size_t aik_size_; - const std::pair* uik_nkj_; - size_t uik_nkj_size_; - const std::pair* lij_ujk_; - size_t lij_ujk_size_; - const char* do_aki_; - size_t do_aki_size_; - const size_t* aki_; - size_t aki_size_; - const std::pair* lki_nkj_; - size_t lki_nkj_size_; - const std::pair* lkj_uji_; - size_t lkj_uji_size_; - const size_t* uii_; - size_t uii_size_; -}; -// different matrix data grouped in struct passing to kernel driver function -struct CudaMatrixParam -{ - const double* rate_constants_; - const double* state_variables_; - double* forcing_; - size_t n_grids_; - size_t n_reactions_; - size_t n_species_; + //different matrix data grouped in struct passing to kernel driver function + struct CudaMatrixParam{ + const double* rate_constants_; + const double* state_variables_; + double* forcing_; + const double* b_; + double* x_; + size_t x_size_; + size_t b_size_; + size_t n_grids_; + size_t n_reactions_; + size_t n_species_; + size_t b_column_counts_; + size_t x_column_counts_; +}; + +//sparseMatrix data grouped in struct passing to kernel driver function +struct CudaSparseMatrixParam{ + double* jacobian_; + size_t jacobian_size_; + const double* A_; + size_t A_size_; + double* L_; + size_t L_size_; + double* U_; + size_t U_size_; + size_t n_grids_; + const double* lower_matrix_; + size_t lower_matrix_size_; + const double* upper_matrix_; + size_t upper_matrix_size_; }; -// sparseMatrix data grouped in struct passing to kernel driver function -struct CudaSparseMatrixParam -{ - double* jacobian_; - size_t jacobian_size_; - const double* A_; - size_t A_size_; - double* L_; - size_t L_size_; - double* U_; - size_t U_size_; - size_t n_grids_; -}; \ No newline at end of file diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index caf73c9da..2c5ca20ee 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -2,5 +2,6 @@ if(ENABLE_CUDA) target_sources(micm_cuda PRIVATE lu_decomposition.cu + linear_solver.cu ) endif() \ No newline at end of file diff --git a/src/solver/linear_solver.cu b/src/solver/linear_solver.cu new file mode 100644 index 000000000..ae8d6f32e --- /dev/null +++ b/src/solver/linear_solver.cu @@ -0,0 +1,157 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include +struct SolveDevice{ + std::pair* nLij_Lii_; + std::pair* Lij_yj_; + std::pair* nUij_Uii_; + std::pair* Uij_xj_; + double* lower_matrix_; + double* upper_matrix_; + double* b_; + double* x_; +}; +namespace micm{ + namespace cuda{ +__global__ void SolveKernel(SolveDevice* device, + size_t n_grids, + size_t b_column_counts, + size_t x_column_counts, + size_t nLij_Lii_size, + size_t nUij_Uii_size) +{ + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + + double* b = device->b_; + double* x = device->x_; + double* y = device->x_; //Alias x for consistency with equation, but to reuse memory + double* lower_matrix = device->lower_matrix_; + double* upper_matrix = device->upper_matrix_; + std::pair* nLij_Lii = device->nLij_Lii_; + std::pair* Lij_yj = device->Lij_yj_; + std::pair* nUij_Uii = device->nUij_Uii_; + std::pair* Uij_xj = device->Uij_xj_; + + if (tid < n_grids) + { + size_t b_column_index = 0; + size_t x_column_index = 0; + size_t y_column_index = 0; + size_t b_column_backward_index = b_column_counts -1; + size_t x_column_backward_index = x_column_counts -1; + size_t Lij_yj_index = 0; + size_t Uij_xj_index = 0; + + for (size_t j = 0; j < nLij_Lii_size; ++j) + { + auto& nLij_Lii_element = nLij_Lii[j]; + y[y_column_index * n_grids + tid] = b[b_column_index++ * n_grids + tid]; + for (size_t i = 0; i < nLij_Lii_element.first; ++i) + { + size_t lower_matrix_index = Lij_yj[Lij_yj_index].first + tid; + size_t y_index = Lij_yj[Lij_yj_index].second * n_grids + tid; + y[y_column_index * n_grids + tid] -= lower_matrix[lower_matrix_index] * y[y_index]; + ++Lij_yj_index; + } + y[y_column_index++ * n_grids + tid] /= lower_matrix[nLij_Lii_element.second + tid]; + } + + for (size_t k = 0; k < nUij_Uii_size; ++k) + { + auto& nUij_Uii_element = nUij_Uii[k]; + + for (size_t i = 0; i < nUij_Uii_element.first; ++i) + { + size_t upper_matrix_index = Uij_xj[Uij_xj_index].first + tid; + size_t x_index = Uij_xj[Uij_xj_index].second * n_grids + tid; + x[x_column_backward_index * n_grids + tid] -= upper_matrix[upper_matrix_index] * x[x_index]; + ++Uij_xj_index; + } + x[x_column_backward_index * n_grids + tid] /= upper_matrix[nUij_Uii_element.second + tid]; + + if (x_column_backward_index != 0) + { + --x_column_backward_index; + } + } + } +} + std::chrono::nanoseconds SolveKernelDriver(CudaLinearSolverParam& linearSolver,CudaSparseMatrixParam& sparseMatrix, CudaMatrixParam& denseMatrix) + { + //create device pointer + std::pair* d_nLij_Lii; + std::pair* d_Lij_yj; + std::pair* d_nUij_Uii; + std::pair* d_Uij_xj; + double* d_lower_matrix; + double* d_upper_matrix; + double* d_b; + double* d_x; + SolveDevice* device; + + //allocate device memory + cudaMalloc(&d_nLij_Lii, sizeof(std::pair)* linearSolver.nLij_Lii_size_); + cudaMalloc(&d_Lij_yj, sizeof(std::pair)* linearSolver.Lij_yj_size_); + cudaMalloc(&d_nUij_Uii, sizeof(std::pair)* linearSolver.nUij_Uii_size_); + cudaMalloc(&d_Uij_xj, sizeof(std::pair)* linearSolver.Uij_xj_size_); + + cudaMalloc(&d_lower_matrix, sizeof(double)* sparseMatrix.lower_matrix_size_); + cudaMalloc(&d_upper_matrix, sizeof(double)* sparseMatrix.upper_matrix_size_); + cudaMalloc(&d_b, sizeof(double)* denseMatrix.b_size_); + cudaMalloc(&d_x, sizeof(double)* denseMatrix.x_size_); + cudaMalloc(&device, sizeof(SolveDevice)); + + //transfer memory from host to device + cudaMemcpy(d_nLij_Lii, linearSolver.nLij_Lii_, sizeof(std::pair)* linearSolver.nLij_Lii_size_,cudaMemcpyHostToDevice); + cudaMemcpy(d_Lij_yj, linearSolver.Lij_yj_, sizeof(std::pair)* linearSolver.Lij_yj_size_,cudaMemcpyHostToDevice); + cudaMemcpy(d_nUij_Uii, linearSolver.nUij_Uii_, sizeof(std::pair)* linearSolver.nUij_Uii_size_,cudaMemcpyHostToDevice); + cudaMemcpy(d_Uij_xj, linearSolver.Uij_xj_, sizeof(std::pair)* linearSolver.Uij_xj_size_, cudaMemcpyHostToDevice); + + cudaMemcpy(d_lower_matrix, sparseMatrix.lower_matrix_, sizeof(double)*sparseMatrix.lower_matrix_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_upper_matrix, sparseMatrix.upper_matrix_, sizeof(double)*sparseMatrix.upper_matrix_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_b, denseMatrix.b_, sizeof(double)* denseMatrix.b_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_x, denseMatrix.x_, sizeof(double)* denseMatrix.x_size_, cudaMemcpyHostToDevice); + + cudaMemcpy(&(device->nLij_Lii_), &d_nLij_Lii, sizeof(std::pair*),cudaMemcpyHostToDevice); + cudaMemcpy(&(device->Lij_yj_), &d_Lij_yj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->nUij_Uii_), &d_nUij_Uii, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->Uij_xj_), &d_Uij_xj, sizeof(std::pair*), cudaMemcpyHostToDevice); + + cudaMemcpy(&(device->lower_matrix_), &d_lower_matrix, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->upper_matrix_), &d_upper_matrix, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->b_), &d_b, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->x_),&d_x, sizeof(double*), cudaMemcpyHostToDevice); + + //kernel call + size_t num_block = (denseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + auto startTime = std::chrono::high_resolution_clock::now(); + SolveKernel<<>>(device, + denseMatrix.n_grids_, + denseMatrix.b_column_counts_, + denseMatrix.x_column_counts_, + linearSolver.nLij_Lii_size_, + linearSolver.nUij_Uii_size_); + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(denseMatrix.x_, d_x, sizeof(double)* denseMatrix.x_size_, cudaMemcpyDeviceToHost); + + //clean up + cudaFree(d_nLij_Lii); + cudaFree(d_Lij_yj); + cudaFree(d_nUij_Uii); + cudaFree(d_Uij_xj); + cudaFree(d_lower_matrix); + cudaFree(d_upper_matrix); + cudaFree(d_b); + cudaFree(d_x); + cudaFree(device); + return kernel_duration; + } + }//end cuda +}// end micm \ No newline at end of file diff --git a/src/solver/lu_decomposition.cu b/src/solver/lu_decomposition.cu index 1381e71d7..c1d93f23d 100644 --- a/src/solver/lu_decomposition.cu +++ b/src/solver/lu_decomposition.cu @@ -6,193 +6,174 @@ #include #include -// grouped parameters passing to DecomposeKernel() -struct DecomposeDevice -{ - double* A_; - double* L_; - double* U_; - char* do_aik_; - size_t* aik_; - char* do_aki_; - size_t* aki_; - size_t* uii_; - std::pair* niLU_; - std::pair* uik_nkj_; - std::pair* lij_ujk_; - std::pair* lki_nkj_; - std::pair* lkj_uji_; - size_t n_grids_; - size_t niLU_size_; -}; -namespace micm -{ - namespace cuda - { - __global__ void DecomposeKernel(DecomposeDevice* device) - { - size_t tid = blockIdx.x * blockDim.x + threadIdx.x; - double* A = device->A_; - double* L = device->L_; - double* U = device->U_; - std::pair* lkj_uji = device->lkj_uji_; - std::pair* uik_nkj = device->uik_nkj_; - std::pair* lij_ujk = device->lij_ujk_; - std::pair* lki_nkj = device->lki_nkj_; - size_t do_aik_offset = 0; // boolean vector - size_t aik_offset = 0; - size_t uik_nkj_offset = 0; - size_t lij_ujk_offset = 0; - size_t do_aki_offset = 0; // boolean vector - size_t aki_offset = 0; - size_t lki_nkj_offset = 0; - size_t lkj_uji_offset = 0; - size_t uii_offset = 0; - - if (tid < device->n_grids_) - { - // loop through every element in niLU - for (size_t i = 0; i < device->niLU_size_; i++) +//grouped parameters passing to DecomposeKernel() +struct DecomposeDevice{ + double* A_; + double* L_; + double* U_; + char* do_aik_; + size_t* aik_; + char* do_aki_; + size_t* aki_; + size_t* uii_; + std::pair* niLU_; + std::pair* uik_nkj_; + std::pair* lij_ujk_; + std::pair* lki_nkj_; + std::pair* lkj_uji_; +}; +namespace micm{ + namespace cuda{ + __global__ void DecomposeKernel(DecomposeDevice* device, size_t n_grids, size_t niLU_size) { - // upper triangular matrix - auto inLU = device->niLU_[i]; - for (size_t iU = 0; iU < inLU.second; ++iU) - { - if (device->do_aik_[do_aik_offset++]) - { - size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; - size_t A_idx = device->aik_[aik_offset++] + tid; - U[U_idx] = A[A_idx]; - } - - for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj) - { - size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; - size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; - size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; - U[U_idx_1] -= L[L_idx] * U[U_idx_2]; - ++lij_ujk_offset; - } - ++uik_nkj_offset; - } - // lower triangular matrix - - L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; - - for (size_t iL = 0; iL < inLU.first; ++iL) - { - if (device->do_aki_[do_aki_offset++]) - { - size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; - size_t A_idx = device->aki_[aki_offset++] + tid; - L[L_idx] = A[A_idx]; - } - for (size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second; ++ikj) - { - size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; - size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; - size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; - L[L_idx_1] -= L[L_idx_2] * U[U_idx]; - ++lkj_uji_offset; + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + double* A = device->A_; + double* L = device->L_; + double* U = device->U_; + std::pair* lkj_uji = device->lkj_uji_; + std::pair* uik_nkj = device->uik_nkj_; + std::pair* lij_ujk = device->lij_ujk_; + std::pair* lki_nkj = device->lki_nkj_; + size_t do_aik_offset = 0; //boolean vector + size_t aik_offset = 0; + size_t uik_nkj_offset = 0; + size_t lij_ujk_offset = 0; + size_t do_aki_offset = 0; //boolean vector + size_t aki_offset = 0; + size_t lki_nkj_offset = 0; + size_t lkj_uji_offset = 0; + size_t uii_offset = 0; + + if (tid < n_grids){ + //loop through every element in niLU + for (size_t i = 0; i < niLU_size; i++){ + //upper triangular matrix + auto inLU = device->niLU_[i]; + for (size_t iU = 0; iU < inLU.second; ++iU){ + if(device->do_aik_[do_aik_offset++]){ + size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; + size_t A_idx = device->aik_[aik_offset++]+ tid; + U[U_idx] = A[A_idx]; + } + + for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj){ + size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; + size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; + size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; + U[U_idx_1] -= L[L_idx] * U[U_idx_2]; + ++lij_ujk_offset; + } + ++uik_nkj_offset; + } + // lower triangular matrix + + L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; + + for (size_t iL = 0; iL do_aki_[do_aki_offset++]){ + size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; + size_t A_idx = device->aki_[aki_offset++] + tid; + L[L_idx] = A[A_idx]; + } + for(size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second;++ikj){ + size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; + size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; + size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; + L[L_idx_1] -= L[L_idx_2] * U[U_idx]; + ++lkj_uji_offset; + } + L[lki_nkj[lki_nkj_offset].first + tid]/=U[device->uii_[uii_offset] + tid]; + ++lki_nkj_offset; + ++uii_offset; + } + } } - L[lki_nkj[lki_nkj_offset].first + tid] /= U[device->uii_[uii_offset] + tid]; - ++lki_nkj_offset; - ++uii_offset; - } - } - } - } // end of kernel - - std::chrono::nanoseconds DecomposeKernelDriver(CudaSparseMatrixParam& sparseMatrix, CudaSolverParam& solver) - { - // create device pointers and allocate device memory - double* d_A; - double* d_L; - double* d_U; - bool* d_do_aik; - size_t* d_aik; - bool* d_do_aki; - size_t* d_aki; - size_t* d_uii; - std::pair* d_niLU; - std::pair* d_uik_nkj; - std::pair* d_lij_ujk; - std::pair* d_lki_nkj; - std::pair* d_lkj_uji; - DecomposeDevice* device; - - cudaMalloc(&d_A, sizeof(double) * sparseMatrix.A_size_); - cudaMalloc(&d_L, sizeof(double) * sparseMatrix.L_size_); - cudaMalloc(&d_U, sizeof(double) * sparseMatrix.U_size_); - cudaMalloc(&d_do_aik, sizeof(char) * solver.do_aik_size_); - cudaMalloc(&d_aik, sizeof(size_t) * solver.aik_size_); - cudaMalloc(&d_do_aki, sizeof(char) * solver.do_aki_size_); - cudaMalloc(&d_aki, sizeof(size_t) * solver.aki_size_); - cudaMalloc(&d_uii, sizeof(size_t) * solver.uii_size_); - cudaMalloc(&d_niLU, sizeof(std::pair) * solver.niLU_size_); - cudaMalloc(&d_uik_nkj, sizeof(std::pair) * solver.uik_nkj_size_); - cudaMalloc(&d_lij_ujk, sizeof(std::pair) * solver.lij_ujk_size_); - cudaMalloc(&d_lki_nkj, sizeof(std::pair) * solver.lki_nkj_size_); - cudaMalloc(&d_lkj_uji, sizeof(std::pair) * solver.lkj_uji_size_); - cudaMalloc(&device, sizeof(DecomposeDevice)); - - // transfer data from host to device - cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double) * sparseMatrix.A_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double) * sparseMatrix.L_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double) * sparseMatrix.U_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char) * solver.do_aik_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_aik, solver.aik_, sizeof(size_t) * solver.aik_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char) * solver.do_aki_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_aki, solver.aki_, sizeof(size_t) * solver.aki_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_uii, solver.uii_, sizeof(size_t) * solver.uii_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair) * solver.niLU_size_, cudaMemcpyHostToDevice); - cudaMemcpy( - d_uik_nkj, solver.uik_nkj_, sizeof(std::pair) * solver.uik_nkj_size_, cudaMemcpyHostToDevice); - cudaMemcpy( - d_lij_ujk, solver.lij_ujk_, sizeof(std::pair) * solver.lij_ujk_size_, cudaMemcpyHostToDevice); - cudaMemcpy( - d_lki_nkj, solver.lki_nkj_, sizeof(std::pair) * solver.lki_nkj_size_, cudaMemcpyHostToDevice); - cudaMemcpy( - d_lkj_uji, solver.lkj_uji_, sizeof(std::pair) * solver.lkj_uji_size_, cudaMemcpyHostToDevice); - cudaMemcpy(&(device->A_), &d_A, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->L_), &d_L, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->U_), &d_U, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->do_aki_), &d_do_aki, sizeof(char*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->aki_), &d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); - - // total number of threads is number of blocks in sparseMatrix A - size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - device->n_grids_ = sparseMatrix.n_grids_; - device->niLU_size_ = solver.niLU_size_; - - // call kernel - auto startTime = std::chrono::high_resolution_clock::now(); - DecomposeKernel<<>>(device); - cudaDeviceSynchronize(); - auto endTime = std::chrono::high_resolution_clock::now(); - auto kernel_duration = std::chrono::duration_cast(endTime - startTime); - cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double) * sparseMatrix.L_size_, cudaMemcpyDeviceToHost); - cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double) * sparseMatrix.U_size_, cudaMemcpyDeviceToHost); - - // clean up - cudaFree(d_A); - cudaFree(d_L); - cudaFree(d_U); - cudaFree(d_do_aik); - cudaFree(d_aik); - cudaFree(d_do_aki); - cudaFree(d_aki); - cudaFree(d_uii); - cudaFree(device); - return kernel_duration; - } // end kernelDriver - } // namespace cuda -} // namespace micm \ No newline at end of file + }// end of kernel + + std::chrono::nanoseconds DecomposeKernelDriver( + CudaSparseMatrixParam& sparseMatrix, + CudaSolverParam& solver){ + //create device pointers and allocate device memory + double* d_A; + double* d_L; + double* d_U; + bool* d_do_aik; + size_t* d_aik; + bool* d_do_aki; + size_t* d_aki; + size_t* d_uii; + std::pair* d_niLU; + std::pair* d_uik_nkj; + std::pair* d_lij_ujk; + std::pair* d_lki_nkj; + std::pair* d_lkj_uji; + DecomposeDevice* device; + + cudaMalloc(&d_A,sizeof(double)* sparseMatrix.A_size_); + cudaMalloc(&d_L,sizeof(double)* sparseMatrix.L_size_); + cudaMalloc(&d_U,sizeof(double)* sparseMatrix.U_size_); + cudaMalloc(&d_do_aik,sizeof(char)* solver.do_aik_size_); + cudaMalloc(&d_aik,sizeof(size_t)* solver.aik_size_); + cudaMalloc(&d_do_aki,sizeof(char)* solver.do_aki_size_); + cudaMalloc(&d_aki,sizeof(size_t)* solver.aki_size_); + cudaMalloc(&d_uii,sizeof(size_t)* solver.uii_size_); + cudaMalloc(&d_niLU,sizeof(std::pair)* solver.niLU_size_); + cudaMalloc(&d_uik_nkj,sizeof(std::pair)* solver.uik_nkj_size_); + cudaMalloc(&d_lij_ujk,sizeof(std::pair)* solver.lij_ujk_size_); + cudaMalloc(&d_lki_nkj,sizeof(std::pair)* solver.lki_nkj_size_); + cudaMalloc(&d_lkj_uji,sizeof(std::pair)* solver.lkj_uji_size_); + cudaMalloc(&device, sizeof(DecomposeDevice)); + + //transfer data from host to device + cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double)* sparseMatrix.A_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char)* solver.do_aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aik, solver.aik_, sizeof(size_t)* solver.aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char)* solver.do_aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aki, solver.aki_, sizeof(size_t)*solver.aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_uii, solver.uii_, sizeof(size_t)* solver.uii_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair)*solver.niLU_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_uik_nkj, solver.uik_nkj_, sizeof(std::pair)*solver.uik_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_lij_ujk, solver.lij_ujk_, sizeof(std::pair)*solver.lij_ujk_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_lki_nkj, solver.lki_nkj_, sizeof(std::pair)*solver.lki_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_lkj_uji, solver.lkj_uji_, sizeof(std::pair)*solver.lkj_uji_size_, cudaMemcpyHostToDevice); + cudaMemcpy(&(device->A_),&d_A, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->L_),&d_L, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->U_),&d_U, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aki_),&d_do_aki,sizeof(char*),cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aki_),&d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); + + //total number of threads is number of blocks in sparseMatrix A + size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + + // call kernel + auto startTime = std::chrono::high_resolution_clock::now(); + DecomposeKernel<<>>(device, sparseMatrix.n_grids_, solver.niLU_size_); + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyDeviceToHost); + cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyDeviceToHost); + //clean up + cudaFree(d_A); + cudaFree(d_L); + cudaFree(d_U); + cudaFree(d_do_aik); + cudaFree(d_aik); + cudaFree(d_do_aki); + cudaFree(d_aki); + cudaFree(d_uii); + cudaFree(device); + return kernel_duration; + }//end kernelDriver + }//end cuda +}//end micm \ No newline at end of file diff --git a/test/unit/solver/CMakeLists.txt b/test/unit/solver/CMakeLists.txt index c45f12645..f2302671e 100644 --- a/test/unit/solver/CMakeLists.txt +++ b/test/unit/solver/CMakeLists.txt @@ -15,6 +15,7 @@ create_standard_test(NAME state SOURCES test_state.cpp) # GPU tests if(ENABLE_CUDA) create_standard_test(NAME cuda_lu_decomposition SOURCES test_cuda_lu_decomposition.cpp LIBRARIES musica::micm_cuda) + create_standard_test(NAME cuda_linear_solver SOURCES test_cuda_linear_solver.cpp LIBRARIES musica::micm_cuda) endif() if(ENABLE_LLVM) diff --git a/test/unit/solver/test_cuda_linear_solver.cpp b/test/unit/solver/test_cuda_linear_solver.cpp new file mode 100644 index 000000000..6a127a628 --- /dev/null +++ b/test/unit/solver/test_cuda_linear_solver.cpp @@ -0,0 +1,164 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "test_linear_solver_policy.hpp" + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; +template +using Group10000VectorMatrix = micm::VectorMatrix; + + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; +template +using Group10000SparseVectorMatrix = micm::SparseMatrix>; + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +std::vector linearSolverGenerator( + const std::function, double)> create_linear_solver, + std::size_t number_of_blocks) +{ + auto gen_bool = std::bind(std::uniform_int_distribution<>(0, 1), std::default_random_engine()); + auto get_double = std::bind(std::lognormal_distribution(-2.0, 2.0), std::default_random_engine()); + + auto builder = SparseMatrixPolicy::create(10).number_of_blocks(number_of_blocks).initial_value(1.0e-30); + for (std::size_t i = 0; i < 10; ++i) + for (std::size_t j = 0; j < 10; ++j) + if (i == j || gen_bool()) + builder = builder.with_element(i, j); + + SparseMatrixPolicy A(builder); + MatrixPolicy b(number_of_blocks, 10, 0.0); + MatrixPolicy x(number_of_blocks, 10, 100.0); + + for (std::size_t i = 0; i < 10; ++i) + for (std::size_t j = 0; j < 10; ++j) + if (!A.IsZero(i, j)) + for (std::size_t i_block = 0; i_block < number_of_blocks; ++i_block) + A[i_block][i][j] = get_double(); + + for (std::size_t i = 0; i < 10; ++i) + for (std::size_t i_block = 0; i_block < number_of_blocks; ++i_block) + b[i_block][i] = get_double(); + + LinearSolverPolicy solver = create_linear_solver(A, 1.0e-30); + auto lu = micm::LuDecomposition::GetLUMatrices(A, 1.0e-30); + auto lower_matrix = std::move(lu.first); + auto upper_matrix = std::move(lu.second); + solver.Factor(A, lower_matrix, upper_matrix); + solver.template Solve(b, x, lower_matrix, upper_matrix); + return x.AsVector(); +} + +//bit to bit variation between CPU and GPU result with randomMatrixVectorOrdering +void gpuValidation(){ + std::vector cpu_x = linearSolverGenerator>( + [](const Group10000SparseVectorMatrix& matrix, + double initial_value) -> micm::LinearSolver { + return micm::LinearSolver{ matrix, initial_value }; + }, + 10000); + + std::vector gpu_x = linearSolverGenerator>( + [](const Group10000SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 10000); + + for (int i = 0; i < cpu_x.size(); i++){ + EXPECT_EQ(cpu_x, gpu_x); + } +} + + +TEST(CudaLinearSolver, DenseMatrixVectorOrdering) +{ + testDenseMatrix>( + [](const Group1SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{matrix, initial_value}; + }); +} + +TEST(CudaLinearSolver, RandomMatrixVectorOrdering) +{ + testRandomMatrix>( + [](const Group1SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 1); + testRandomMatrix>( + [](const Group2SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 2); + testRandomMatrix>( + [](const Group3SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 3); + testRandomMatrix>( + [](const Group4SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 4); + gpuValidation(); + +} + +TEST(CudaLinearSolver, DiagonalMatrixVectorOrdering) +{ + testDiagonalMatrix>( + [](const Group1SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 1); + testDiagonalMatrix>( + [](const Group2SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 2); + testDiagonalMatrix>( + [](const Group3SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 3); + testDiagonalMatrix>( + [](const Group4SparseVectorMatrix& matrix, + double initial_value) -> micm::CudaLinearSolver { + return micm::CudaLinearSolver{ matrix, initial_value }; + }, + 4); +} + + diff --git a/test/unit/solver/test_cuda_lu_decomposition.cpp b/test/unit/solver/test_cuda_lu_decomposition.cpp index 06512d939..a18e25336 100644 --- a/test/unit/solver/test_cuda_lu_decomposition.cpp +++ b/test/unit/solver/test_cuda_lu_decomposition.cpp @@ -92,7 +92,7 @@ void testRandomMatrix(size_t n_grids) for (std::size_t i_block = 0; i_block < n_grids; ++i_block) A[i_block][i][j] = get_double(); - micm::LuDecomposition gpu_lud(A); + micm::CudaLuDecomposition gpu_lud(A); auto gpu_LU = micm::CudaLuDecomposition::GetLUMatrices(A, 1.0e-30); gpu_lud.Decompose(A, gpu_LU.first, gpu_LU.second); check_results( From 56222ef889d0677544b1383e961732d739253334 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:35:38 -0800 Subject: [PATCH 299/318] Auto-format code changes (#371) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/solver/cuda_linear_solver.hpp | 96 ++--- include/micm/solver/cuda_lu_decomposition.hpp | 88 ++--- include/micm/solver/linear_solver.hpp | 2 +- include/micm/util/cuda_param.hpp | 161 ++++---- src/solver/linear_solver.cu | 296 ++++++++------- src/solver/lu_decomposition.cu | 352 +++++++++--------- .../analytical_surface_rxn_policy.hpp | 88 ++--- test/unit/solver/test_cuda_linear_solver.cpp | 44 ++- 8 files changed, 587 insertions(+), 540 deletions(-) diff --git a/include/micm/solver/cuda_linear_solver.hpp b/include/micm/solver/cuda_linear_solver.hpp index 6b7190d61..fe80cd954 100644 --- a/include/micm/solver/cuda_linear_solver.hpp +++ b/include/micm/solver/cuda_linear_solver.hpp @@ -4,53 +4,61 @@ #pragma once #include #include -#include -#include +#include #include +#include +#include #include -#include -namespace micm{ - template class SparseMatrixPolicy, class LuDecompositionPolicy = CudaLuDecomposition> - class CudaLinearSolver : public LinearSolver { - public: - //constructor - CudaLinearSolver(){}; +namespace micm +{ + template class SparseMatrixPolicy, class LuDecompositionPolicy = CudaLuDecomposition> + class CudaLinearSolver : public LinearSolver + { + public: + // constructor + CudaLinearSolver(){}; + + CudaLinearSolver(const SparseMatrixPolicy& matrix, T initial_value) + : LinearSolver(matrix, initial_value){}; + + CudaLinearSolver( + const SparseMatrixPolicy& matrix, + T initial_value, + const std::function&)> create_lu_decomp) + : linearSolver(matrix, initial_value, create_lu_decomp); - CudaLinearSolver(const SparseMatrixPolicy& matrix, T initial_value): LinearSolver (matrix, initial_value){}; - - CudaLinearSolver(const SparseMatrixPolicy& matrix, T initial_value, const std::function&)> create_lu_decomp) - : linearSolver(matrix, initial_value, create_lu_decomp); - - template class MatrixPolicy > - requires(VectorizableDense> || VectorizableSparse>) - std::chrono::nanoseconds Solve(const MatrixPolicy&b, MatrixPolicy& x, SparseMatrixPolicy& lower_matrix, + template class MatrixPolicy> + requires(VectorizableDense> || VectorizableSparse>) std::chrono::nanoseconds Solve( + const MatrixPolicy& b, + MatrixPolicy& x, + SparseMatrixPolicy& lower_matrix, SparseMatrixPolicy& upper_matrix) - { - CudaLinearSolverParam linearSolver; - CudaSparseMatrixParam sparseMatrix; - CudaMatrixParam denseMatrix; - linearSolver.nLij_Lii_ = this->nLij_Lii_.data(); - linearSolver.nLij_Lii_size_ = this->nLij_Lii_.size(); - linearSolver.Lij_yj_= this->Lij_yj_.data(); - linearSolver.Lij_yj_size_ = this->Lij_yj_.size(); - linearSolver.nUij_Uii_ = this->nUij_Uii_.data(); - linearSolver.nUij_Uii_size_ = this->nUij_Uii_.size(); - linearSolver.Uij_xj_ = this->Uij_xj_.data(); - linearSolver.Uij_xj_size_ = this->Uij_xj_.size(); - sparseMatrix.lower_matrix_ = lower_matrix.AsVector().data(); - sparseMatrix.lower_matrix_size_ = lower_matrix.AsVector().size(); - sparseMatrix.upper_matrix_ = upper_matrix.AsVector().data(); - sparseMatrix.upper_matrix_size_ = upper_matrix.AsVector().size(); - denseMatrix.b_ = b.AsVector().data(); - denseMatrix.x_ = x.AsVector().data(); - denseMatrix.b_size_= b.AsVector().size(); - denseMatrix.x_size_= x.AsVector().size(); - denseMatrix.n_grids_ = b.size(); //number of grids - denseMatrix.b_column_counts_ = b[0].size(); - denseMatrix.x_column_counts_= x[0].size(); - //calling kernel driver - return micm::cuda::SolveKernelDriver(linearSolver, sparseMatrix, denseMatrix); - }; + { + CudaLinearSolverParam linearSolver; + CudaSparseMatrixParam sparseMatrix; + CudaMatrixParam denseMatrix; + linearSolver.nLij_Lii_ = this->nLij_Lii_.data(); + linearSolver.nLij_Lii_size_ = this->nLij_Lii_.size(); + linearSolver.Lij_yj_ = this->Lij_yj_.data(); + linearSolver.Lij_yj_size_ = this->Lij_yj_.size(); + linearSolver.nUij_Uii_ = this->nUij_Uii_.data(); + linearSolver.nUij_Uii_size_ = this->nUij_Uii_.size(); + linearSolver.Uij_xj_ = this->Uij_xj_.data(); + linearSolver.Uij_xj_size_ = this->Uij_xj_.size(); + sparseMatrix.lower_matrix_ = lower_matrix.AsVector().data(); + sparseMatrix.lower_matrix_size_ = lower_matrix.AsVector().size(); + sparseMatrix.upper_matrix_ = upper_matrix.AsVector().data(); + sparseMatrix.upper_matrix_size_ = upper_matrix.AsVector().size(); + denseMatrix.b_ = b.AsVector().data(); + denseMatrix.x_ = x.AsVector().data(); + denseMatrix.b_size_ = b.AsVector().size(); + denseMatrix.x_size_ = x.AsVector().size(); + denseMatrix.n_grids_ = b.size(); // number of grids + denseMatrix.b_column_counts_ = b[0].size(); + denseMatrix.x_column_counts_ = x[0].size(); + // calling kernel driver + return micm::cuda::SolveKernelDriver(linearSolver, sparseMatrix, denseMatrix); }; -}//end micm + }; +} // namespace micm diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index 710b63604..6315b3d44 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -9,14 +9,16 @@ #include #include - -namespace micm{ - class CudaLuDecomposition: public LuDecomposition{ - public: - CudaLuDecomposition(){}; +namespace micm +{ + class CudaLuDecomposition : public LuDecomposition + { + public: + CudaLuDecomposition(){}; template - CudaLuDecomposition(const SparseMatrix& matrix): LuDecomposition(matrix){}; + CudaLuDecomposition(const SparseMatrix& matrix) + : LuDecomposition(matrix){}; template typename SparseMatrixPolicy> requires VectorizableSparse> std::chrono::nanoseconds @@ -24,44 +26,42 @@ namespace micm{ const; }; - template class SparseMatrixPolicy> - requires(VectorizableSparse>) - std::chrono::nanoseconds CudaLuDecomposition::Decompose( - const SparseMatrixPolicy& A, - SparseMatrixPolicy& L, - SparseMatrixPolicy& U) const - { - CudaSparseMatrixParam sparseMatrix; - sparseMatrix.A_ = A.AsVector().data(); - sparseMatrix.A_size_ = A.AsVector().size(); - sparseMatrix.L_ = L.AsVector().data(); - sparseMatrix.L_size_ = L.AsVector().size(); - sparseMatrix.U_ = U.AsVector().data(); - sparseMatrix.U_size_ = U.AsVector().size(); - sparseMatrix.n_grids_ = A.size(); - - CudaSolverParam solver; - solver.do_aik_ = this->do_aik_.data(); - solver.do_aik_size_ = this->do_aik_.size(); - solver.aik_ = this->aik_.data(); - solver.aik_size_ = this->aik_.size(); - solver.do_aki_ = this->do_aki_.data(); - solver.do_aki_size_ = this->do_aki_.size(); - solver.aki_ = this->aki_.data(); - solver.aki_size_ = this->aki_.size(); - solver.uii_ = this->uii_.data(); - solver.uii_size_ = this->uii_.size(); - - solver.niLU_ = this->niLU_.data(); - solver.niLU_size_ = this->niLU_.size(); - solver.uik_nkj_ = this->uik_nkj_.data(); - solver.uik_nkj_size_ = this->uik_nkj_.size(); - solver.lij_ujk_ = this->lij_ujk_.data(); - solver.lij_ujk_size_ = this->lij_ujk_.size(); - solver.lki_nkj_ = this->lki_nkj_.data(); - solver.lki_nkj_size_ = this->lki_nkj_.size(); - solver.lkj_uji_ = this->lkj_uji_.data(); - solver.lkj_uji_size_ = this->lkj_uji_.size(); + template class SparseMatrixPolicy> + requires(VectorizableSparse>) std::chrono::nanoseconds + CudaLuDecomposition::Decompose(const SparseMatrixPolicy& A, SparseMatrixPolicy& L, SparseMatrixPolicy& U) + const + { + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.A_ = A.AsVector().data(); + sparseMatrix.A_size_ = A.AsVector().size(); + sparseMatrix.L_ = L.AsVector().data(); + sparseMatrix.L_size_ = L.AsVector().size(); + sparseMatrix.U_ = U.AsVector().data(); + sparseMatrix.U_size_ = U.AsVector().size(); + sparseMatrix.n_grids_ = A.size(); + + CudaSolverParam solver; + solver.do_aik_ = this->do_aik_.data(); + solver.do_aik_size_ = this->do_aik_.size(); + solver.aik_ = this->aik_.data(); + solver.aik_size_ = this->aik_.size(); + solver.do_aki_ = this->do_aki_.data(); + solver.do_aki_size_ = this->do_aki_.size(); + solver.aki_ = this->aki_.data(); + solver.aki_size_ = this->aki_.size(); + solver.uii_ = this->uii_.data(); + solver.uii_size_ = this->uii_.size(); + + solver.niLU_ = this->niLU_.data(); + solver.niLU_size_ = this->niLU_.size(); + solver.uik_nkj_ = this->uik_nkj_.data(); + solver.uik_nkj_size_ = this->uik_nkj_.size(); + solver.lij_ujk_ = this->lij_ujk_.data(); + solver.lij_ujk_size_ = this->lij_ujk_.size(); + solver.lki_nkj_ = this->lki_nkj_.data(); + solver.lki_nkj_size_ = this->lki_nkj_.size(); + solver.lkj_uji_ = this->lkj_uji_.data(); + solver.lkj_uji_size_ = this->lkj_uji_.size(); // calling kernelSetup function return micm::cuda::DecomposeKernelDriver(sparseMatrix, solver); diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index 150cf0276..9af334502 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -50,7 +50,7 @@ namespace micm LuDecompositionPolicy lu_decomp_; - public: + public: /// @brief default constructor LinearSolver(){}; diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index 411920294..2f680e915 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -3,86 +3,91 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -#include +#include - const size_t BLOCK_SIZE = 320; - //member data of class CudaProcessSet grouped in struct passing to kernel driver function - struct CudaProcessSetParam{ - const size_t* number_of_reactants_; - const size_t* reactant_ids_; - size_t reactant_ids_size_; - const size_t* number_of_products_; - const size_t* product_ids_; - size_t product_ids_size_; - const double* yields_; - size_t yields_size_; - const size_t* jacobian_flat_ids_; - size_t jacobian_flat_ids_size_; -}; +const size_t BLOCK_SIZE = 320; +// member data of class CudaProcessSet grouped in struct passing to kernel driver function +struct CudaProcessSetParam +{ + const size_t* number_of_reactants_; + const size_t* reactant_ids_; + size_t reactant_ids_size_; + const size_t* number_of_products_; + const size_t* product_ids_; + size_t product_ids_size_; + const double* yields_; + size_t yields_size_; + const size_t* jacobian_flat_ids_; + size_t jacobian_flat_ids_size_; +}; - struct CudaSolverParam{ - const std::pair* niLU_; - size_t niLU_size_; - const char* do_aik_; - size_t do_aik_size_; - const size_t* aik_; - size_t aik_size_; - const std::pair* uik_nkj_; - size_t uik_nkj_size_; - const std::pair* lij_ujk_; - size_t lij_ujk_size_; - const char* do_aki_; - size_t do_aki_size_; - const size_t* aki_; - size_t aki_size_; - const std::pair* lki_nkj_; - size_t lki_nkj_size_; - const std::pair* lkj_uji_; - size_t lkj_uji_size_; - const size_t* uii_; - size_t uii_size_; - }; - - struct CudaLinearSolverParam{ - const std::pair* nLij_Lii_; - size_t nLij_Lii_size_; - const std::pair* Lij_yj_; - size_t Lij_yj_size_; - const std::pair* nUij_Uii_; - size_t nUij_Uii_size_; - const std::pair< size_t, size_t>* Uij_xj_; - size_t Uij_xj_size_; - }; +struct CudaSolverParam +{ + const std::pair* niLU_; + size_t niLU_size_; + const char* do_aik_; + size_t do_aik_size_; + const size_t* aik_; + size_t aik_size_; + const std::pair* uik_nkj_; + size_t uik_nkj_size_; + const std::pair* lij_ujk_; + size_t lij_ujk_size_; + const char* do_aki_; + size_t do_aki_size_; + const size_t* aki_; + size_t aki_size_; + const std::pair* lki_nkj_; + size_t lki_nkj_size_; + const std::pair* lkj_uji_; + size_t lkj_uji_size_; + const size_t* uii_; + size_t uii_size_; +}; - //different matrix data grouped in struct passing to kernel driver function - struct CudaMatrixParam{ - const double* rate_constants_; - const double* state_variables_; - double* forcing_; - const double* b_; - double* x_; - size_t x_size_; - size_t b_size_; - size_t n_grids_; - size_t n_reactions_; - size_t n_species_; - size_t b_column_counts_; - size_t x_column_counts_; -}; +struct CudaLinearSolverParam +{ + const std::pair* nLij_Lii_; + size_t nLij_Lii_size_; + const std::pair* Lij_yj_; + size_t Lij_yj_size_; + const std::pair* nUij_Uii_; + size_t nUij_Uii_size_; + const std::pair* Uij_xj_; + size_t Uij_xj_size_; +}; + +// different matrix data grouped in struct passing to kernel driver function +struct CudaMatrixParam +{ + const double* rate_constants_; + const double* state_variables_; + double* forcing_; + const double* b_; + double* x_; + size_t x_size_; + size_t b_size_; + size_t n_grids_; + size_t n_reactions_; + size_t n_species_; + size_t b_column_counts_; + size_t x_column_counts_; +}; -//sparseMatrix data grouped in struct passing to kernel driver function -struct CudaSparseMatrixParam{ - double* jacobian_; - size_t jacobian_size_; - const double* A_; - size_t A_size_; - double* L_; - size_t L_size_; - double* U_; - size_t U_size_; - size_t n_grids_; - const double* lower_matrix_; - size_t lower_matrix_size_; - const double* upper_matrix_; - size_t upper_matrix_size_; +// sparseMatrix data grouped in struct passing to kernel driver function +struct CudaSparseMatrixParam +{ + double* jacobian_; + size_t jacobian_size_; + const double* A_; + size_t A_size_; + double* L_; + size_t L_size_; + double* U_; + size_t U_size_; + size_t n_grids_; + const double* lower_matrix_; + size_t lower_matrix_size_; + const double* upper_matrix_; + size_t upper_matrix_size_; }; diff --git a/src/solver/linear_solver.cu b/src/solver/linear_solver.cu index ae8d6f32e..947572d27 100644 --- a/src/solver/linear_solver.cu +++ b/src/solver/linear_solver.cu @@ -2,156 +2,186 @@ // // SPDX-License-Identifier: Apache-2.0 -#include -#include #include -#include -struct SolveDevice{ - std::pair* nLij_Lii_; - std::pair* Lij_yj_; - std::pair* nUij_Uii_; - std::pair* Uij_xj_; - double* lower_matrix_; - double* upper_matrix_; - double* b_; - double* x_; +#include +#include +#include +struct SolveDevice +{ + std::pair* nLij_Lii_; + std::pair* Lij_yj_; + std::pair* nUij_Uii_; + std::pair* Uij_xj_; + double* lower_matrix_; + double* upper_matrix_; + double* b_; + double* x_; }; -namespace micm{ - namespace cuda{ -__global__ void SolveKernel(SolveDevice* device, - size_t n_grids, - size_t b_column_counts, - size_t x_column_counts, - size_t nLij_Lii_size, - size_t nUij_Uii_size) +namespace micm { - size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + namespace cuda + { + __global__ void SolveKernel( + SolveDevice* device, + size_t n_grids, + size_t b_column_counts, + size_t x_column_counts, + size_t nLij_Lii_size, + size_t nUij_Uii_size) + { + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; - double* b = device->b_; - double* x = device->x_; - double* y = device->x_; //Alias x for consistency with equation, but to reuse memory - double* lower_matrix = device->lower_matrix_; - double* upper_matrix = device->upper_matrix_; - std::pair* nLij_Lii = device->nLij_Lii_; - std::pair* Lij_yj = device->Lij_yj_; - std::pair* nUij_Uii = device->nUij_Uii_; - std::pair* Uij_xj = device->Uij_xj_; + double* b = device->b_; + double* x = device->x_; + double* y = device->x_; // Alias x for consistency with equation, but to reuse memory + double* lower_matrix = device->lower_matrix_; + double* upper_matrix = device->upper_matrix_; + std::pair* nLij_Lii = device->nLij_Lii_; + std::pair* Lij_yj = device->Lij_yj_; + std::pair* nUij_Uii = device->nUij_Uii_; + std::pair* Uij_xj = device->Uij_xj_; - if (tid < n_grids) - { + if (tid < n_grids) + { size_t b_column_index = 0; size_t x_column_index = 0; size_t y_column_index = 0; - size_t b_column_backward_index = b_column_counts -1; - size_t x_column_backward_index = x_column_counts -1; - size_t Lij_yj_index = 0; + size_t b_column_backward_index = b_column_counts - 1; + size_t x_column_backward_index = x_column_counts - 1; + size_t Lij_yj_index = 0; size_t Uij_xj_index = 0; - + for (size_t j = 0; j < nLij_Lii_size; ++j) { - auto& nLij_Lii_element = nLij_Lii[j]; - y[y_column_index * n_grids + tid] = b[b_column_index++ * n_grids + tid]; - for (size_t i = 0; i < nLij_Lii_element.first; ++i) - { - size_t lower_matrix_index = Lij_yj[Lij_yj_index].first + tid; - size_t y_index = Lij_yj[Lij_yj_index].second * n_grids + tid; - y[y_column_index * n_grids + tid] -= lower_matrix[lower_matrix_index] * y[y_index]; - ++Lij_yj_index; - } - y[y_column_index++ * n_grids + tid] /= lower_matrix[nLij_Lii_element.second + tid]; + auto& nLij_Lii_element = nLij_Lii[j]; + y[y_column_index * n_grids + tid] = b[b_column_index++ * n_grids + tid]; + for (size_t i = 0; i < nLij_Lii_element.first; ++i) + { + size_t lower_matrix_index = Lij_yj[Lij_yj_index].first + tid; + size_t y_index = Lij_yj[Lij_yj_index].second * n_grids + tid; + y[y_column_index * n_grids + tid] -= lower_matrix[lower_matrix_index] * y[y_index]; + ++Lij_yj_index; + } + y[y_column_index++ * n_grids + tid] /= lower_matrix[nLij_Lii_element.second + tid]; } - + for (size_t k = 0; k < nUij_Uii_size; ++k) - { - auto& nUij_Uii_element = nUij_Uii[k]; - - for (size_t i = 0; i < nUij_Uii_element.first; ++i) - { - size_t upper_matrix_index = Uij_xj[Uij_xj_index].first + tid; - size_t x_index = Uij_xj[Uij_xj_index].second * n_grids + tid; - x[x_column_backward_index * n_grids + tid] -= upper_matrix[upper_matrix_index] * x[x_index]; - ++Uij_xj_index; - } - x[x_column_backward_index * n_grids + tid] /= upper_matrix[nUij_Uii_element.second + tid]; - - if (x_column_backward_index != 0) - { - --x_column_backward_index; - } + { + auto& nUij_Uii_element = nUij_Uii[k]; + + for (size_t i = 0; i < nUij_Uii_element.first; ++i) + { + size_t upper_matrix_index = Uij_xj[Uij_xj_index].first + tid; + size_t x_index = Uij_xj[Uij_xj_index].second * n_grids + tid; + x[x_column_backward_index * n_grids + tid] -= upper_matrix[upper_matrix_index] * x[x_index]; + ++Uij_xj_index; + } + x[x_column_backward_index * n_grids + tid] /= upper_matrix[nUij_Uii_element.second + tid]; + + if (x_column_backward_index != 0) + { + --x_column_backward_index; + } } + } } -} - std::chrono::nanoseconds SolveKernelDriver(CudaLinearSolverParam& linearSolver,CudaSparseMatrixParam& sparseMatrix, CudaMatrixParam& denseMatrix) + std::chrono::nanoseconds + SolveKernelDriver(CudaLinearSolverParam& linearSolver, CudaSparseMatrixParam& sparseMatrix, CudaMatrixParam& denseMatrix) { - //create device pointer - std::pair* d_nLij_Lii; - std::pair* d_Lij_yj; - std::pair* d_nUij_Uii; - std::pair* d_Uij_xj; - double* d_lower_matrix; - double* d_upper_matrix; - double* d_b; - double* d_x; - SolveDevice* device; + // create device pointer + std::pair* d_nLij_Lii; + std::pair* d_Lij_yj; + std::pair* d_nUij_Uii; + std::pair* d_Uij_xj; + double* d_lower_matrix; + double* d_upper_matrix; + double* d_b; + double* d_x; + SolveDevice* device; + + // allocate device memory + cudaMalloc(&d_nLij_Lii, sizeof(std::pair) * linearSolver.nLij_Lii_size_); + cudaMalloc(&d_Lij_yj, sizeof(std::pair) * linearSolver.Lij_yj_size_); + cudaMalloc(&d_nUij_Uii, sizeof(std::pair) * linearSolver.nUij_Uii_size_); + cudaMalloc(&d_Uij_xj, sizeof(std::pair) * linearSolver.Uij_xj_size_); + + cudaMalloc(&d_lower_matrix, sizeof(double) * sparseMatrix.lower_matrix_size_); + cudaMalloc(&d_upper_matrix, sizeof(double) * sparseMatrix.upper_matrix_size_); + cudaMalloc(&d_b, sizeof(double) * denseMatrix.b_size_); + cudaMalloc(&d_x, sizeof(double) * denseMatrix.x_size_); + cudaMalloc(&device, sizeof(SolveDevice)); + + // transfer memory from host to device + cudaMemcpy( + d_nLij_Lii, + linearSolver.nLij_Lii_, + sizeof(std::pair) * linearSolver.nLij_Lii_size_, + cudaMemcpyHostToDevice); + cudaMemcpy( + d_Lij_yj, + linearSolver.Lij_yj_, + sizeof(std::pair) * linearSolver.Lij_yj_size_, + cudaMemcpyHostToDevice); + cudaMemcpy( + d_nUij_Uii, + linearSolver.nUij_Uii_, + sizeof(std::pair) * linearSolver.nUij_Uii_size_, + cudaMemcpyHostToDevice); + cudaMemcpy( + d_Uij_xj, + linearSolver.Uij_xj_, + sizeof(std::pair) * linearSolver.Uij_xj_size_, + cudaMemcpyHostToDevice); + + cudaMemcpy( + d_lower_matrix, + sparseMatrix.lower_matrix_, + sizeof(double) * sparseMatrix.lower_matrix_size_, + cudaMemcpyHostToDevice); + cudaMemcpy( + d_upper_matrix, + sparseMatrix.upper_matrix_, + sizeof(double) * sparseMatrix.upper_matrix_size_, + cudaMemcpyHostToDevice); + cudaMemcpy(d_b, denseMatrix.b_, sizeof(double) * denseMatrix.b_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_x, denseMatrix.x_, sizeof(double) * denseMatrix.x_size_, cudaMemcpyHostToDevice); + + cudaMemcpy(&(device->nLij_Lii_), &d_nLij_Lii, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->Lij_yj_), &d_Lij_yj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->nUij_Uii_), &d_nUij_Uii, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->Uij_xj_), &d_Uij_xj, sizeof(std::pair*), cudaMemcpyHostToDevice); - //allocate device memory - cudaMalloc(&d_nLij_Lii, sizeof(std::pair)* linearSolver.nLij_Lii_size_); - cudaMalloc(&d_Lij_yj, sizeof(std::pair)* linearSolver.Lij_yj_size_); - cudaMalloc(&d_nUij_Uii, sizeof(std::pair)* linearSolver.nUij_Uii_size_); - cudaMalloc(&d_Uij_xj, sizeof(std::pair)* linearSolver.Uij_xj_size_); - - cudaMalloc(&d_lower_matrix, sizeof(double)* sparseMatrix.lower_matrix_size_); - cudaMalloc(&d_upper_matrix, sizeof(double)* sparseMatrix.upper_matrix_size_); - cudaMalloc(&d_b, sizeof(double)* denseMatrix.b_size_); - cudaMalloc(&d_x, sizeof(double)* denseMatrix.x_size_); - cudaMalloc(&device, sizeof(SolveDevice)); + cudaMemcpy(&(device->lower_matrix_), &d_lower_matrix, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->upper_matrix_), &d_upper_matrix, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->b_), &d_b, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->x_), &d_x, sizeof(double*), cudaMemcpyHostToDevice); - //transfer memory from host to device - cudaMemcpy(d_nLij_Lii, linearSolver.nLij_Lii_, sizeof(std::pair)* linearSolver.nLij_Lii_size_,cudaMemcpyHostToDevice); - cudaMemcpy(d_Lij_yj, linearSolver.Lij_yj_, sizeof(std::pair)* linearSolver.Lij_yj_size_,cudaMemcpyHostToDevice); - cudaMemcpy(d_nUij_Uii, linearSolver.nUij_Uii_, sizeof(std::pair)* linearSolver.nUij_Uii_size_,cudaMemcpyHostToDevice); - cudaMemcpy(d_Uij_xj, linearSolver.Uij_xj_, sizeof(std::pair)* linearSolver.Uij_xj_size_, cudaMemcpyHostToDevice); - - cudaMemcpy(d_lower_matrix, sparseMatrix.lower_matrix_, sizeof(double)*sparseMatrix.lower_matrix_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_upper_matrix, sparseMatrix.upper_matrix_, sizeof(double)*sparseMatrix.upper_matrix_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_b, denseMatrix.b_, sizeof(double)* denseMatrix.b_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_x, denseMatrix.x_, sizeof(double)* denseMatrix.x_size_, cudaMemcpyHostToDevice); - - cudaMemcpy(&(device->nLij_Lii_), &d_nLij_Lii, sizeof(std::pair*),cudaMemcpyHostToDevice); - cudaMemcpy(&(device->Lij_yj_), &d_Lij_yj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->nUij_Uii_), &d_nUij_Uii, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->Uij_xj_), &d_Uij_xj, sizeof(std::pair*), cudaMemcpyHostToDevice); - - cudaMemcpy(&(device->lower_matrix_), &d_lower_matrix, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->upper_matrix_), &d_upper_matrix, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->b_), &d_b, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->x_),&d_x, sizeof(double*), cudaMemcpyHostToDevice); - - //kernel call - size_t num_block = (denseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - auto startTime = std::chrono::high_resolution_clock::now(); - SolveKernel<<>>(device, - denseMatrix.n_grids_, - denseMatrix.b_column_counts_, - denseMatrix.x_column_counts_, - linearSolver.nLij_Lii_size_, - linearSolver.nUij_Uii_size_); - cudaDeviceSynchronize(); - auto endTime = std::chrono::high_resolution_clock::now(); - auto kernel_duration = std::chrono::duration_cast(endTime - startTime); - cudaMemcpy(denseMatrix.x_, d_x, sizeof(double)* denseMatrix.x_size_, cudaMemcpyDeviceToHost); + // kernel call + size_t num_block = (denseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + auto startTime = std::chrono::high_resolution_clock::now(); + SolveKernel<<>>( + device, + denseMatrix.n_grids_, + denseMatrix.b_column_counts_, + denseMatrix.x_column_counts_, + linearSolver.nLij_Lii_size_, + linearSolver.nUij_Uii_size_); + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(denseMatrix.x_, d_x, sizeof(double) * denseMatrix.x_size_, cudaMemcpyDeviceToHost); - //clean up - cudaFree(d_nLij_Lii); - cudaFree(d_Lij_yj); - cudaFree(d_nUij_Uii); - cudaFree(d_Uij_xj); - cudaFree(d_lower_matrix); - cudaFree(d_upper_matrix); - cudaFree(d_b); - cudaFree(d_x); - cudaFree(device); - return kernel_duration; + // clean up + cudaFree(d_nLij_Lii); + cudaFree(d_Lij_yj); + cudaFree(d_nUij_Uii); + cudaFree(d_Uij_xj); + cudaFree(d_lower_matrix); + cudaFree(d_upper_matrix); + cudaFree(d_b); + cudaFree(d_x); + cudaFree(device); + return kernel_duration; } - }//end cuda -}// end micm \ No newline at end of file + } // namespace cuda +} // namespace micm \ No newline at end of file diff --git a/src/solver/lu_decomposition.cu b/src/solver/lu_decomposition.cu index c1d93f23d..6c1826e88 100644 --- a/src/solver/lu_decomposition.cu +++ b/src/solver/lu_decomposition.cu @@ -6,174 +6,188 @@ #include #include -//grouped parameters passing to DecomposeKernel() -struct DecomposeDevice{ - double* A_; - double* L_; - double* U_; - char* do_aik_; - size_t* aik_; - char* do_aki_; - size_t* aki_; - size_t* uii_; - std::pair* niLU_; - std::pair* uik_nkj_; - std::pair* lij_ujk_; - std::pair* lki_nkj_; - std::pair* lkj_uji_; -}; -namespace micm{ - namespace cuda{ - __global__ void DecomposeKernel(DecomposeDevice* device, size_t n_grids, size_t niLU_size) +// grouped parameters passing to DecomposeKernel() +struct DecomposeDevice +{ + double* A_; + double* L_; + double* U_; + char* do_aik_; + size_t* aik_; + char* do_aki_; + size_t* aki_; + size_t* uii_; + std::pair* niLU_; + std::pair* uik_nkj_; + std::pair* lij_ujk_; + std::pair* lki_nkj_; + std::pair* lkj_uji_; +}; +namespace micm +{ + namespace cuda + { + __global__ void DecomposeKernel(DecomposeDevice* device, size_t n_grids, size_t niLU_size) + { + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + double* A = device->A_; + double* L = device->L_; + double* U = device->U_; + std::pair* lkj_uji = device->lkj_uji_; + std::pair* uik_nkj = device->uik_nkj_; + std::pair* lij_ujk = device->lij_ujk_; + std::pair* lki_nkj = device->lki_nkj_; + size_t do_aik_offset = 0; // boolean vector + size_t aik_offset = 0; + size_t uik_nkj_offset = 0; + size_t lij_ujk_offset = 0; + size_t do_aki_offset = 0; // boolean vector + size_t aki_offset = 0; + size_t lki_nkj_offset = 0; + size_t lkj_uji_offset = 0; + size_t uii_offset = 0; + + if (tid < n_grids) + { + // loop through every element in niLU + for (size_t i = 0; i < niLU_size; i++) { - size_t tid = blockIdx.x * blockDim.x + threadIdx.x; - double* A = device->A_; - double* L = device->L_; - double* U = device->U_; - std::pair* lkj_uji = device->lkj_uji_; - std::pair* uik_nkj = device->uik_nkj_; - std::pair* lij_ujk = device->lij_ujk_; - std::pair* lki_nkj = device->lki_nkj_; - size_t do_aik_offset = 0; //boolean vector - size_t aik_offset = 0; - size_t uik_nkj_offset = 0; - size_t lij_ujk_offset = 0; - size_t do_aki_offset = 0; //boolean vector - size_t aki_offset = 0; - size_t lki_nkj_offset = 0; - size_t lkj_uji_offset = 0; - size_t uii_offset = 0; - - if (tid < n_grids){ - //loop through every element in niLU - for (size_t i = 0; i < niLU_size; i++){ - //upper triangular matrix - auto inLU = device->niLU_[i]; - for (size_t iU = 0; iU < inLU.second; ++iU){ - if(device->do_aik_[do_aik_offset++]){ - size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; - size_t A_idx = device->aik_[aik_offset++]+ tid; - U[U_idx] = A[A_idx]; - } - - for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj){ - size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; - size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; - size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; - U[U_idx_1] -= L[L_idx] * U[U_idx_2]; - ++lij_ujk_offset; - } - ++uik_nkj_offset; - } - // lower triangular matrix - - L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; - - for (size_t iL = 0; iL do_aki_[do_aki_offset++]){ - size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; - size_t A_idx = device->aki_[aki_offset++] + tid; - L[L_idx] = A[A_idx]; - } - for(size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second;++ikj){ - size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; - size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; - size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; - L[L_idx_1] -= L[L_idx_2] * U[U_idx]; - ++lkj_uji_offset; - } - L[lki_nkj[lki_nkj_offset].first + tid]/=U[device->uii_[uii_offset] + tid]; - ++lki_nkj_offset; - ++uii_offset; - } - } + // upper triangular matrix + auto inLU = device->niLU_[i]; + for (size_t iU = 0; iU < inLU.second; ++iU) + { + if (device->do_aik_[do_aik_offset++]) + { + size_t U_idx = uik_nkj[uik_nkj_offset].first + tid; + size_t A_idx = device->aik_[aik_offset++] + tid; + U[U_idx] = A[A_idx]; + } + + for (size_t ikj = 0; ikj < uik_nkj[uik_nkj_offset].second; ++ikj) + { + size_t U_idx_1 = uik_nkj[uik_nkj_offset].first + tid; + size_t L_idx = lij_ujk[lij_ujk_offset].first + tid; + size_t U_idx_2 = lij_ujk[lij_ujk_offset].second + tid; + U[U_idx_1] -= L[L_idx] * U[U_idx_2]; + ++lij_ujk_offset; + } + ++uik_nkj_offset; + } + // lower triangular matrix + + L[lki_nkj[lki_nkj_offset++].first + tid] = 1.0; + + for (size_t iL = 0; iL < inLU.first; ++iL) + { + if (device->do_aki_[do_aki_offset++]) + { + size_t L_idx = lki_nkj[lki_nkj_offset].first + tid; + size_t A_idx = device->aki_[aki_offset++] + tid; + L[L_idx] = A[A_idx]; } - }// end of kernel - - std::chrono::nanoseconds DecomposeKernelDriver( - CudaSparseMatrixParam& sparseMatrix, - CudaSolverParam& solver){ - //create device pointers and allocate device memory - double* d_A; - double* d_L; - double* d_U; - bool* d_do_aik; - size_t* d_aik; - bool* d_do_aki; - size_t* d_aki; - size_t* d_uii; - std::pair* d_niLU; - std::pair* d_uik_nkj; - std::pair* d_lij_ujk; - std::pair* d_lki_nkj; - std::pair* d_lkj_uji; - DecomposeDevice* device; - - cudaMalloc(&d_A,sizeof(double)* sparseMatrix.A_size_); - cudaMalloc(&d_L,sizeof(double)* sparseMatrix.L_size_); - cudaMalloc(&d_U,sizeof(double)* sparseMatrix.U_size_); - cudaMalloc(&d_do_aik,sizeof(char)* solver.do_aik_size_); - cudaMalloc(&d_aik,sizeof(size_t)* solver.aik_size_); - cudaMalloc(&d_do_aki,sizeof(char)* solver.do_aki_size_); - cudaMalloc(&d_aki,sizeof(size_t)* solver.aki_size_); - cudaMalloc(&d_uii,sizeof(size_t)* solver.uii_size_); - cudaMalloc(&d_niLU,sizeof(std::pair)* solver.niLU_size_); - cudaMalloc(&d_uik_nkj,sizeof(std::pair)* solver.uik_nkj_size_); - cudaMalloc(&d_lij_ujk,sizeof(std::pair)* solver.lij_ujk_size_); - cudaMalloc(&d_lki_nkj,sizeof(std::pair)* solver.lki_nkj_size_); - cudaMalloc(&d_lkj_uji,sizeof(std::pair)* solver.lkj_uji_size_); - cudaMalloc(&device, sizeof(DecomposeDevice)); - - //transfer data from host to device - cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double)* sparseMatrix.A_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char)* solver.do_aik_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_aik, solver.aik_, sizeof(size_t)* solver.aik_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char)* solver.do_aki_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_aki, solver.aki_, sizeof(size_t)*solver.aki_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_uii, solver.uii_, sizeof(size_t)* solver.uii_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair)*solver.niLU_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_uik_nkj, solver.uik_nkj_, sizeof(std::pair)*solver.uik_nkj_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_lij_ujk, solver.lij_ujk_, sizeof(std::pair)*solver.lij_ujk_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_lki_nkj, solver.lki_nkj_, sizeof(std::pair)*solver.lki_nkj_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_lkj_uji, solver.lkj_uji_, sizeof(std::pair)*solver.lkj_uji_size_, cudaMemcpyHostToDevice); - cudaMemcpy(&(device->A_),&d_A, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->L_),&d_L, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->U_),&d_U, sizeof(double*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->do_aki_),&d_do_aki,sizeof(char*),cudaMemcpyHostToDevice); - cudaMemcpy(&(device->aki_),&d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); - cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); - - //total number of threads is number of blocks in sparseMatrix A - size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - - // call kernel - auto startTime = std::chrono::high_resolution_clock::now(); - DecomposeKernel<<>>(device, sparseMatrix.n_grids_, solver.niLU_size_); - cudaDeviceSynchronize(); - auto endTime = std::chrono::high_resolution_clock::now(); - auto kernel_duration = std::chrono::duration_cast(endTime - startTime); - cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double)* sparseMatrix.L_size_, cudaMemcpyDeviceToHost); - cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double)* sparseMatrix.U_size_, cudaMemcpyDeviceToHost); - //clean up - cudaFree(d_A); - cudaFree(d_L); - cudaFree(d_U); - cudaFree(d_do_aik); - cudaFree(d_aik); - cudaFree(d_do_aki); - cudaFree(d_aki); - cudaFree(d_uii); - cudaFree(device); - return kernel_duration; - }//end kernelDriver - }//end cuda -}//end micm \ No newline at end of file + for (size_t ikj = 0; ikj < lki_nkj[lki_nkj_offset].second; ++ikj) + { + size_t L_idx_1 = lki_nkj[lki_nkj_offset].first + tid; + size_t L_idx_2 = lkj_uji[lkj_uji_offset].first + tid; + size_t U_idx = lkj_uji[lkj_uji_offset].second + tid; + L[L_idx_1] -= L[L_idx_2] * U[U_idx]; + ++lkj_uji_offset; + } + L[lki_nkj[lki_nkj_offset].first + tid] /= U[device->uii_[uii_offset] + tid]; + ++lki_nkj_offset; + ++uii_offset; + } + } + } + } // end of kernel + + std::chrono::nanoseconds DecomposeKernelDriver(CudaSparseMatrixParam& sparseMatrix, CudaSolverParam& solver) + { + // create device pointers and allocate device memory + double* d_A; + double* d_L; + double* d_U; + bool* d_do_aik; + size_t* d_aik; + bool* d_do_aki; + size_t* d_aki; + size_t* d_uii; + std::pair* d_niLU; + std::pair* d_uik_nkj; + std::pair* d_lij_ujk; + std::pair* d_lki_nkj; + std::pair* d_lkj_uji; + DecomposeDevice* device; + + cudaMalloc(&d_A, sizeof(double) * sparseMatrix.A_size_); + cudaMalloc(&d_L, sizeof(double) * sparseMatrix.L_size_); + cudaMalloc(&d_U, sizeof(double) * sparseMatrix.U_size_); + cudaMalloc(&d_do_aik, sizeof(char) * solver.do_aik_size_); + cudaMalloc(&d_aik, sizeof(size_t) * solver.aik_size_); + cudaMalloc(&d_do_aki, sizeof(char) * solver.do_aki_size_); + cudaMalloc(&d_aki, sizeof(size_t) * solver.aki_size_); + cudaMalloc(&d_uii, sizeof(size_t) * solver.uii_size_); + cudaMalloc(&d_niLU, sizeof(std::pair) * solver.niLU_size_); + cudaMalloc(&d_uik_nkj, sizeof(std::pair) * solver.uik_nkj_size_); + cudaMalloc(&d_lij_ujk, sizeof(std::pair) * solver.lij_ujk_size_); + cudaMalloc(&d_lki_nkj, sizeof(std::pair) * solver.lki_nkj_size_); + cudaMalloc(&d_lkj_uji, sizeof(std::pair) * solver.lkj_uji_size_); + cudaMalloc(&device, sizeof(DecomposeDevice)); + + // transfer data from host to device + cudaMemcpy(d_A, sparseMatrix.A_, sizeof(double) * sparseMatrix.A_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_L, sparseMatrix.L_, sizeof(double) * sparseMatrix.L_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_U, sparseMatrix.U_, sizeof(double) * sparseMatrix.U_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aik, solver.do_aik_, sizeof(char) * solver.do_aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aik, solver.aik_, sizeof(size_t) * solver.aik_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_do_aki, solver.do_aki_, sizeof(char) * solver.do_aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_aki, solver.aki_, sizeof(size_t) * solver.aki_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_uii, solver.uii_, sizeof(size_t) * solver.uii_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_niLU, solver.niLU_, sizeof(std::pair) * solver.niLU_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_uik_nkj, solver.uik_nkj_, sizeof(std::pair) * solver.uik_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_lij_ujk, solver.lij_ujk_, sizeof(std::pair) * solver.lij_ujk_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_lki_nkj, solver.lki_nkj_, sizeof(std::pair) * solver.lki_nkj_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_lkj_uji, solver.lkj_uji_, sizeof(std::pair) * solver.lkj_uji_size_, cudaMemcpyHostToDevice); + cudaMemcpy(&(device->A_), &d_A, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->L_), &d_L, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->U_), &d_U, sizeof(double*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aik_), &d_do_aik, sizeof(char*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aik_), &d_aik, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->do_aki_), &d_do_aki, sizeof(char*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->aki_), &d_aki, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uii_), &d_uii, sizeof(size_t*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->niLU_), &d_niLU, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->uik_nkj_), &d_uik_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lij_ujk_), &d_lij_ujk, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lki_nkj_), &d_lki_nkj, sizeof(std::pair*), cudaMemcpyHostToDevice); + cudaMemcpy(&(device->lkj_uji_), &d_lkj_uji, sizeof(std::pair*), cudaMemcpyHostToDevice); + + // total number of threads is number of blocks in sparseMatrix A + size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + + // call kernel + auto startTime = std::chrono::high_resolution_clock::now(); + DecomposeKernel<<>>(device, sparseMatrix.n_grids_, solver.niLU_size_); + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(sparseMatrix.L_, d_L, sizeof(double) * sparseMatrix.L_size_, cudaMemcpyDeviceToHost); + cudaMemcpy(sparseMatrix.U_, d_U, sizeof(double) * sparseMatrix.U_size_, cudaMemcpyDeviceToHost); + // clean up + cudaFree(d_A); + cudaFree(d_L); + cudaFree(d_U); + cudaFree(d_do_aik); + cudaFree(d_aik); + cudaFree(d_do_aki); + cudaFree(d_aki); + cudaFree(d_uii); + cudaFree(device); + return kernel_duration; + } // end kernelDriver + } // namespace cuda +} // namespace micm \ No newline at end of file diff --git a/test/integration/analytical_surface_rxn_policy.hpp b/test/integration/analytical_surface_rxn_policy.hpp index ba9242542..21942931e 100644 --- a/test/integration/analytical_surface_rxn_policy.hpp +++ b/test/integration/analytical_surface_rxn_policy.hpp @@ -1,28 +1,28 @@ #include -#include -#include -#include -#include + #include +#include +#include +#include +#include #include - template void test_analytical_surface_rxn( const std::function&)> create_solver) { // parameters, from CAMP/test/unit_rxn_data/test_rxn_surface.F90 - const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] - const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] - const double DENSITY_stuff = 1000.0; // [kg m-3] + const double mode_GMD = 1.0e-6; // mode geometric mean diameter [m] + const double mode_GSD = 0.1; // mode geometric standard deviation [unitless] + const double DENSITY_stuff = 1000.0; // [kg m-3] const double DENSITY_more_stuff = 1000.0; // [kg m-3] - const double MW_stuff = 0.5; // [kg mol-1] - const double MW_more_stuff = 0.2; // [kg mol-1] - const double MW_foo = 0.04607; // [kg mol-1] - const double Dg_foo = 0.95e-5; // diffusion coefficient [m2 s-1] - const double rxn_gamma = 2.0e-2; // [unitless] - const double bar_yield = 1.0; // [unitless] - const double baz_yield = 0.4; // [unitless] + const double MW_stuff = 0.5; // [kg mol-1] + const double MW_more_stuff = 0.2; // [kg mol-1] + const double MW_foo = 0.04607; // [kg mol-1] + const double Dg_foo = 0.95e-5; // diffusion coefficient [m2 s-1] + const double rxn_gamma = 2.0e-2; // [unitless] + const double bar_yield = 1.0; // [unitless] + const double baz_yield = 0.4; // [unitless] // environment const double temperature = 272.5; // temperature (K) @@ -37,13 +37,11 @@ void test_analytical_surface_rxn( double radius = mode_GMD / 2.0 * exp(5.0 * log(mode_GSD) * log(mode_GSD) / 2.0); // particle number concentration [# m-3] - double number_conc = 6.0 / (M_PI * std::pow(mode_GMD, 3.0) - * std::exp(9.0/2.0 * std::log(mode_GSD) * std::log(mode_GSD) )) - * (conc_stuff / DENSITY_stuff + conc_more_stuff / DENSITY_more_stuff); + double number_conc = 6.0 / + (M_PI * std::pow(mode_GMD, 3.0) * std::exp(9.0 / 2.0 * std::log(mode_GSD) * std::log(mode_GSD))) * + (conc_stuff / DENSITY_stuff + conc_more_stuff / DENSITY_more_stuff); - micm::Species foo("foo", - { { "molecular weight [kg mol-1]", MW_foo }, - { "diffusion coefficient [m2 s-1]", Dg_foo } }); + micm::Species foo("foo", { { "molecular weight [kg mol-1]", MW_foo }, { "diffusion coefficient [m2 s-1]", Dg_foo } }); micm::Species bar("bar"); micm::Species baz("baz"); @@ -54,15 +52,14 @@ void test_analytical_surface_rxn( micm::System chemical_system = micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }); // Rate - micm::SurfaceRateConstant surface{ - { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; + micm::SurfaceRateConstant surface{ { .label_ = "foo", .species_ = foo, .reaction_probability_ = rxn_gamma } }; // Process micm::Process surface_process = micm::Process::create() - .reactants({ foo }) - .products({ micm::yields(bar, bar_yield), micm::yields(baz, baz_yield) }) - .rate_constant(surface) - .phase(gas_phase); + .reactants({ foo }) + .products({ micm::yields(bar, bar_yield), micm::yields(baz, baz_yield) }) + .rate_constant(surface) + .phase(gas_phase); auto reactions = std::vector{ surface_process }; @@ -84,8 +81,7 @@ void test_analytical_surface_rxn( // Surface reaction rate calculation double mean_free_speed = std::sqrt(8.0 * GAS_CONSTANT / (M_PI * MW_foo) * temperature); - double k1 = 4.0 * number_conc * M_PI * radius * radius / - (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); + double k1 = 4.0 * number_conc * M_PI * radius * radius / (radius / Dg_foo + 4.0 / (mean_free_speed * rxn_gamma)); double time_step = 0.1 / k1; // s int nstep = 10; @@ -93,20 +89,14 @@ void test_analytical_surface_rxn( std::vector> model_conc(nstep + 1, std::vector(3)); std::vector> analytic_conc(nstep + 1, std::vector(3)); - model_conc[0] = {conc_foo, 0, 0}; - analytic_conc[0] = {conc_foo, 0, 0}; + model_conc[0] = { conc_foo, 0, 0 }; + analytic_conc[0] = { conc_foo, 0, 0 }; size_t idx_foo = 0, idx_bar = 1, idx_baz = 2; - std::cout << std::setw(3) << "i" - << std::setw(7) << "time" - << std::setw(11) << "anal foo" - << std::setw(11) << "model foo" - << std::setw(11) << "anal bar" - << std::setw(11) << "model bar" - << std::setw(11) << "anal baz" - << std::setw(11) << "model baz" - << std::endl; + std::cout << std::setw(3) << "i" << std::setw(7) << "time" << std::setw(11) << "anal foo" << std::setw(11) << "model foo" + << std::setw(11) << "anal bar" << std::setw(11) << "model bar" << std::setw(11) << "anal baz" << std::setw(11) + << "model baz" << std::endl; for (int i = 1; i <= nstep; ++i) { @@ -134,7 +124,7 @@ void test_analytical_surface_rxn( model_conc[i] = result.result_.AsVector(); double time = i * time_step; - analytic_conc[i][idx_foo] = conc_foo * std::exp(- k1 * time); + analytic_conc[i][idx_foo] = conc_foo * std::exp(-k1 * time); analytic_conc[i][idx_bar] = bar_yield * (1.0 - analytic_conc[i][idx_foo]); analytic_conc[i][idx_baz] = baz_yield * (1.0 - analytic_conc[i][idx_foo]); @@ -143,17 +133,9 @@ void test_analytical_surface_rxn( EXPECT_NEAR(analytic_conc[i][idx_bar], model_conc[i][idx_bar], 1e-5); EXPECT_NEAR(analytic_conc[i][idx_baz], model_conc[i][idx_baz], 1e-5); - std::cout - << std::setw(3) << i << " " - << std::fixed << std::setprecision(2) - << std::setw(5) << time << " " - << std::fixed << std::setprecision(7) - << analytic_conc[i][idx_foo] << " " - << model_conc[i][idx_foo] << " " - << analytic_conc[i][idx_bar] << " " - << model_conc[i][idx_bar] << " " - << analytic_conc[i][idx_baz] << " " - << model_conc[i][idx_baz] << " " - << std::endl; + std::cout << std::setw(3) << i << " " << std::fixed << std::setprecision(2) << std::setw(5) << time << " " + << std::fixed << std::setprecision(7) << analytic_conc[i][idx_foo] << " " << model_conc[i][idx_foo] << " " + << analytic_conc[i][idx_bar] << " " << model_conc[i][idx_bar] << " " << analytic_conc[i][idx_baz] << " " + << model_conc[i][idx_baz] << " " << std::endl; } } diff --git a/test/unit/solver/test_cuda_linear_solver.cpp b/test/unit/solver/test_cuda_linear_solver.cpp index 6a127a628..09921a39f 100644 --- a/test/unit/solver/test_cuda_linear_solver.cpp +++ b/test/unit/solver/test_cuda_linear_solver.cpp @@ -1,15 +1,17 @@ #pragma once #include + #include -#include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include +#include + #include "test_linear_solver_policy.hpp" template @@ -23,7 +25,6 @@ using Group4VectorMatrix = micm::VectorMatrix; template using Group10000VectorMatrix = micm::VectorMatrix; - template using Group1SparseVectorMatrix = micm::SparseMatrix>; template @@ -72,34 +73,44 @@ std::vector linearSolverGenerator( return x.AsVector(); } -//bit to bit variation between CPU and GPU result with randomMatrixVectorOrdering -void gpuValidation(){ - std::vector cpu_x = linearSolverGenerator>( +// bit to bit variation between CPU and GPU result with randomMatrixVectorOrdering +void gpuValidation() +{ + std::vector cpu_x = linearSolverGenerator< + Group10000VectorMatrix, + Group10000SparseVectorMatrix, + micm::LinearSolver>( [](const Group10000SparseVectorMatrix& matrix, double initial_value) -> micm::LinearSolver { return micm::LinearSolver{ matrix, initial_value }; }, 10000); - std::vector gpu_x = linearSolverGenerator>( + std::vector gpu_x = linearSolverGenerator< + Group10000VectorMatrix, + Group10000SparseVectorMatrix, + micm::CudaLinearSolver>( [](const Group10000SparseVectorMatrix& matrix, double initial_value) -> micm::CudaLinearSolver { return micm::CudaLinearSolver{ matrix, initial_value }; }, 10000); - for (int i = 0; i < cpu_x.size(); i++){ - EXPECT_EQ(cpu_x, gpu_x); + for (int i = 0; i < cpu_x.size(); i++) + { + EXPECT_EQ(cpu_x, gpu_x); } } - TEST(CudaLinearSolver, DenseMatrixVectorOrdering) { - testDenseMatrix>( + testDenseMatrix< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::CudaLinearSolver>( [](const Group1SparseVectorMatrix& matrix, double initial_value) -> micm::CudaLinearSolver { - return micm::CudaLinearSolver{matrix, initial_value}; + return micm::CudaLinearSolver{ matrix, initial_value }; }); } @@ -130,7 +141,6 @@ TEST(CudaLinearSolver, RandomMatrixVectorOrdering) }, 4); gpuValidation(); - } TEST(CudaLinearSolver, DiagonalMatrixVectorOrdering) @@ -160,5 +170,3 @@ TEST(CudaLinearSolver, DiagonalMatrixVectorOrdering) }, 4); } - - From b9b2f8810022480e1190645563fc497ab46d04df Mon Sep 17 00:00:00 2001 From: Jiwon Gim <55209567+boulderdaze@users.noreply.github.com> Date: Tue, 21 Nov 2023 10:36:17 -0700 Subject: [PATCH 300/318] Command-line driver for MICM (#326) * micm driver that works for chapman config * set the binary name to micm Co-authored-by: Kyle Shores * edit dockerignore file to add examples folder * add a cmake option for examples * add TS1 configuration and more * add carbon bond 5 configuration * move chapman config * remove carbon bond 5 config from tutorial * revert back to kpp chapman configure, add tests * add a missing header * print all --------- Co-authored-by: Kyle Shores --- .dockerignore | 1 + CMakeLists.txt | 16 + examples/CMakeLists.txt | 7 + examples/Chapman/config.json | 1 - examples/{ => configs}/TS1/config.json | 0 examples/configs/TS1/initial_conditions.csv | 163 + examples/{ => configs}/TS1/reactions.json | 0 examples/{ => configs}/TS1/species.json | 0 examples/configs/carbon_bond_5/config.json | 7 + .../carbon_bond_5/initial_conditions.csv | 38 + examples/configs/carbon_bond_5/reactions.json | 4766 +++++++++++++++++ examples/configs/carbon_bond_5/species.json | 1 + examples/configs/chapman/config.json | 6 + .../configs/chapman/initial_conditions.csv | 12 + .../chapman}/reactions.json | 2 +- .../{Chapman => configs/chapman}/species.json | 0 examples/configs/robertson/config.json | 6 + .../configs/robertson/initial_conditions.csv | 10 + examples/configs/robertson/reactions.json | 43 + examples/configs/robertson/species.json | 16 + examples/example.cpp | 261 + include/micm/solver/state.hpp | 8 + include/micm/solver/state.inl | 44 +- test/examples/CMakeLists.txt | 10 +- test/examples/test_TS1.cpp | 2 +- test/examples/test_carbon_bond_5.cpp | 84 + .../{test_Chapman.cpp => test_chapman.cpp} | 2 +- test/examples/test_robertson.cpp | 60 + .../configs/carbon_bond_5/config.json | 1 - .../configs/carbon_bond_5/reactions.json | 1 - .../configs/carbon_bond_5/species.json | 1 - 31 files changed, 5553 insertions(+), 16 deletions(-) create mode 100644 examples/CMakeLists.txt delete mode 100644 examples/Chapman/config.json rename examples/{ => configs}/TS1/config.json (100%) create mode 100644 examples/configs/TS1/initial_conditions.csv rename examples/{ => configs}/TS1/reactions.json (100%) rename examples/{ => configs}/TS1/species.json (100%) create mode 100644 examples/configs/carbon_bond_5/config.json create mode 100644 examples/configs/carbon_bond_5/initial_conditions.csv create mode 100644 examples/configs/carbon_bond_5/reactions.json create mode 100644 examples/configs/carbon_bond_5/species.json create mode 100644 examples/configs/chapman/config.json create mode 100644 examples/configs/chapman/initial_conditions.csv rename examples/{Chapman => configs/chapman}/reactions.json (99%) rename examples/{Chapman => configs/chapman}/species.json (100%) create mode 100644 examples/configs/robertson/config.json create mode 100644 examples/configs/robertson/initial_conditions.csv create mode 100644 examples/configs/robertson/reactions.json create mode 100644 examples/configs/robertson/species.json create mode 100644 examples/example.cpp create mode 100644 test/examples/test_carbon_bond_5.cpp rename test/examples/{test_Chapman.cpp => test_chapman.cpp} (99%) create mode 100644 test/examples/test_robertson.cpp delete mode 100644 test/tutorial/configs/carbon_bond_5/config.json delete mode 100644 test/tutorial/configs/carbon_bond_5/reactions.json delete mode 100644 test/tutorial/configs/carbon_bond_5/species.json diff --git a/.dockerignore b/.dockerignore index 810bb6789..ab47ae477 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,3 +12,4 @@ !cmake/ !packaging !include/ +!examples diff --git a/CMakeLists.txt b/CMakeLists.txt index c8921c7d7..01e7c665e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ option(ENABLE_CUDA "Build with Cuda support" OFF) option(ENABLE_OPENACC "Build with OpenACC Support" OFF) option(ENABLE_LLVM "Build with LLVM support for JIT-compiling" OFF) option(ENABLE_TESTS "Build the tests" ON) +option(ENABLE_EXAMPLES "Build the examples" ON) set(DEFAULT_VECTOR_MATRIX_SIZE "4" CACHE STRING "Default size for vectorizable matrix types") include(CMakeDependentOption) @@ -106,3 +107,18 @@ if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux" AND "${CMAKE_CXX_COMPILER_ID}" STR # If the compiler is Clang, use libc++ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif() + +################################################################################ +# Command-line driver examples + +if(PROJECT_IS_TOP_LEVEL AND ENABLE_EXAMPLES AND ENABLE_JSON) + add_subdirectory(examples) +endif() + +################################################################################ +# Copy configuration data + +if(ENABLE_TESTS OR ENABLE_EXAMPLES) +add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/examples/configs ${CMAKE_BINARY_DIR}/examples/configs) +endif() \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 000000000..73c481d2f --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,7 @@ +# disable clang tidy for test files +set(CMAKE_CXX_CLANG_TIDY "") + +add_executable(micmDriver example.cpp) +target_link_libraries(micmDriver PUBLIC musica::micm ) + +set_target_properties(micmDriver PROPERTIES OUTPUT_NAME "micm") \ No newline at end of file diff --git a/examples/Chapman/config.json b/examples/Chapman/config.json deleted file mode 100644 index 61165b00d..000000000 --- a/examples/Chapman/config.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-files": ["species.json", "reactions.json"]} diff --git a/examples/TS1/config.json b/examples/configs/TS1/config.json similarity index 100% rename from examples/TS1/config.json rename to examples/configs/TS1/config.json diff --git a/examples/configs/TS1/initial_conditions.csv b/examples/configs/TS1/initial_conditions.csv new file mode 100644 index 000000000..4c0b92bff --- /dev/null +++ b/examples/configs/TS1/initial_conditions.csv @@ -0,0 +1,163 @@ +CONC.CH4,7.63078e-05 +CONC.CL2,4.23932e-08 +CONC.CO,4.23932e-08 +CONC.CO2,0.0163214 +CONC.DMS,4.23932e-08 +CONC.H2O,0.0423932 +CONC.H2O2,4.23932e-08 +CONC.HNO3,4.23932e-08 +CONC.ISOP,4.23932e-08 +CONC.M,42.3932 +CONC.N2,33.0667 +CONC.NH3,4.23932e-08 +CONC.NO,4.23932e-08 +CONC.NO2,4.23932e-08 +CONC.O2,8.90257 +CONC.O3,2.11966e-06 +CONC.OH,4.23932e-11 +CONC.SO2,4.23932e-08 +ENV.temperature,287.45 +ENV.pressure,101319.9 +ENV.air_density,42.3932 +ENV.time_step,150.0 +PHOTO.jacet,1e-08 +PHOTO.jalknit->,jch3ooh,1e-08 +PHOTO.jalkooh->,jch3ooh,1e-08 +PHOTO.jbenzooh->,jch3ooh,1e-08 +PHOTO.jbepomuc->,.10*jno2,1e-08 +PHOTO.jbigald->,0.2*jno2,1e-08 +PHOTO.jbigald1->,.14*jno2,1e-08 +PHOTO.jbigald2->,.20*jno2,1e-08 +PHOTO.jbigald3->,.20*jno2,1e-08 +PHOTO.jbigald4->,.006*jno2,1e-08 +PHOTO.jbrcl,1e-08 +PHOTO.jbro,1e-08 +PHOTO.jbrono2_a,1e-08 +PHOTO.jbrono2_b,1e-08 +PHOTO.jbzooh->,jch3ooh,1e-08 +PHOTO.jc2h5ooh->,jch3ooh,1e-08 +PHOTO.jc3h7ooh->,jch3ooh,1e-08 +PHOTO.jc6h5ooh->,jch3ooh,1e-08 +PHOTO.jccl4,1e-08 +PHOTO.jcf2cl2,1e-08 +PHOTO.jcf2clbr,1e-08 +PHOTO.jcf3br,1e-08 +PHOTO.jcfc113,1e-08 +PHOTO.jcfc114,1e-08 +PHOTO.jcfc115,1e-08 +PHOTO.jcfcl3,1e-08 +PHOTO.jch2br2,1e-08 +PHOTO.jch2o_a,1e-08 +PHOTO.jch2o_b,1e-08 +PHOTO.jch3br,1e-08 +PHOTO.jch3ccl3,1e-08 +PHOTO.jch3cho,1e-08 +PHOTO.jch3cl,1e-08 +PHOTO.jch3co3h->,0.28*jh2o2,1e-08 +PHOTO.jch3ooh,1e-08 +PHOTO.jch4_a,1e-08 +PHOTO.jch4_b,1e-08 +PHOTO.jchbr3,1e-08 +PHOTO.jcl2,1e-08 +PHOTO.jcl2o2,1e-08 +PHOTO.jclo,1e-08 +PHOTO.jclono2_a,1e-08 +PHOTO.jclono2_b,1e-08 +PHOTO.jco2,1e-08 +PHOTO.jcof2,1e-08 +PHOTO.jcofcl,1e-08 +PHOTO.jeooh->,jch3ooh,1e-08 +PHOTO.jglyald,1e-08 +PHOTO.jglyoxal->,jmgly,1e-08 +PHOTO.jh2402,1e-08 +PHOTO.jh2o2,1e-08 +PHOTO.jh2o_a,1e-08 +PHOTO.jh2o_b,1e-08 +PHOTO.jh2o_c,1e-08 +PHOTO.jh2so4,1e-08 +PHOTO.jhbr,1e-08 +PHOTO.jhcfc141b,1e-08 +PHOTO.jhcfc142b,1e-08 +PHOTO.jhcfc22,1e-08 +PHOTO.jhcl,1e-08 +PHOTO.jhf,1e-08 +PHOTO.jhno3,1e-08 +PHOTO.jho2no2_a,1e-08 +PHOTO.jho2no2_b,1e-08 +PHOTO.jhobr,1e-08 +PHOTO.jhocl,1e-08 +PHOTO.jhonitr->,jch2o_a,1e-08 +PHOTO.jhpald->,.006*jno2,1e-08 +PHOTO.jhyac,1e-08 +PHOTO.jisopnooh->,jch3ooh,1e-08 +PHOTO.jisopooh->,jch3ooh,1e-08 +PHOTO.jmacr_a,1e-08 +PHOTO.jmacr_b,1e-08 +PHOTO.jmek->,jacet,1e-08 +PHOTO.jmekooh->,jch3ooh,1e-08 +PHOTO.jmgly,1e-08 +PHOTO.jmpan->,jpan,1e-08 +PHOTO.jmvk,1e-08 +PHOTO.jn2o,1e-08 +PHOTO.jn2o5_a,1e-08 +PHOTO.jn2o5_b,1e-08 +PHOTO.jnc4cho->,jch2o_a,1e-08 +PHOTO.jno2,1e-08 +PHOTO.jno3_a,1e-08 +PHOTO.jno3_b,1e-08 +PHOTO.jno=userdefined,,1e-08 +PHOTO.jnoa->,jch2o_a,1e-08 +PHOTO.jnterpooh->,jch3ooh,1e-08 +PHOTO.jo2_a=userdefined,,1e-08 +PHOTO.jo2_b=userdefined,,1e-08 +PHOTO.jo3_a,1e-08 +PHOTO.jo3_b,1e-08 +PHOTO.joclo,1e-08 +PHOTO.jocs,1e-08 +PHOTO.jonitr->,jch3cho,1e-08 +PHOTO.jpan,1e-08 +PHOTO.jphenooh->,jch3ooh,1e-08 +PHOTO.jpooh->,jch3ooh,1e-08 +PHOTO.jrooh->,jch3ooh,1e-08 +PHOTO.jsf6,1e-08 +PHOTO.jso,1e-08 +PHOTO.jso2,1e-08 +PHOTO.jso3,1e-08 +PHOTO.jsoa1_a1->,.0004*jno2,1e-08 +PHOTO.jsoa1_a2->,.0004*jno2,1e-08 +PHOTO.jsoa2_a1->,.0004*jno2,1e-08 +PHOTO.jsoa2_a2->,.0004*jno2,1e-08 +PHOTO.jsoa3_a1->,.0004*jno2,1e-08 +PHOTO.jsoa3_a2->,.0004*jno2,1e-08 +PHOTO.jsoa4_a1->,.0004*jno2,1e-08 +PHOTO.jsoa4_a2->,.0004*jno2,1e-08 +PHOTO.jsoa5_a1->,.0004*jno2,1e-08 +PHOTO.jsoa5_a2->,.0004*jno2,1e-08 +PHOTO.jtepomuc->,.10*jno2,1e-08 +PHOTO.jterp2ooh->,jch3ooh,1e-08 +PHOTO.jterpnit->,jch3ooh,1e-08 +PHOTO.jterpooh->,jch3ooh,1e-08 +PHOTO.jterprd1->,jch3cho,1e-08 +PHOTO.jterprd2->,jch3cho,1e-08 +PHOTO.jtolooh->,jch3ooh,1e-08 +PHOTO.jxooh->,jch3ooh,1e-08 +PHOTO.jxylenooh->,jch3ooh,1e-08 +PHOTO.jxylolooh->,jch3ooh,1e-08 +USER.het1,1e-08 +USER.het10,1e-08 +USER.het11,1e-08 +USER.het12,1e-08 +USER.het13,1e-08 +USER.het14,1e-08 +USER.het15,1e-08 +USER.het16,1e-08 +USER.het17,1e-08 +USER.het2,1e-08 +USER.het3,1e-08 +USER.het4,1e-08 +USER.het5,1e-08 +USER.het6,1e-08 +USER.het7,1e-08 +USER.het8,1e-08 +USER.het9,1e-08 +USER.k_co_oh_jpl19,1e-08 \ No newline at end of file diff --git a/examples/TS1/reactions.json b/examples/configs/TS1/reactions.json similarity index 100% rename from examples/TS1/reactions.json rename to examples/configs/TS1/reactions.json diff --git a/examples/TS1/species.json b/examples/configs/TS1/species.json similarity index 100% rename from examples/TS1/species.json rename to examples/configs/TS1/species.json diff --git a/examples/configs/carbon_bond_5/config.json b/examples/configs/carbon_bond_5/config.json new file mode 100644 index 000000000..d046530bb --- /dev/null +++ b/examples/configs/carbon_bond_5/config.json @@ -0,0 +1,7 @@ +{ + "camp-files": [ + "species.json", + "reactions.json" + ] + } + \ No newline at end of file diff --git a/examples/configs/carbon_bond_5/initial_conditions.csv b/examples/configs/carbon_bond_5/initial_conditions.csv new file mode 100644 index 000000000..35bb46874 --- /dev/null +++ b/examples/configs/carbon_bond_5/initial_conditions.csv @@ -0,0 +1,38 @@ +ENV.temperature,287.45 +ENV.pressure,101319.9 +ENV.air_density,1e-6 +ENV.time_step,500 +EMIS.NO,1.44e-10 +EMIS.NO2,7.56e-12 +EMIS.CO,1.9600000000000003e-09 +EMIS.SO2,1.06e-09 +EMIS.FORM,1.02e-11 +EMIS.MEOH,5.920000000000001e-13 +EMIS.ALD2,4.25e-12 +EMIS.PAR,4.27e-10 +EMIS.ETH,4.62e-11 +EMIS.OLE,1.49e-11 +EMIS.IOLE,1.49e-11 +EMIS.TOL,1.53e-11 +EMIS.XYL,1.4e-11 +EMIS.ISOP,6.03e-12 +PHOTO.NO2,0.00477 +PHOTO.O3->O1D,2.26e-06 +PHOTO.O3->O3P,0.00025299999999999997 +PHOTO.NO3->NO2,0.11699999999999999 +PHOTO.NO3->NO,0.0144 +PHOTO.HONO,0.000918 +PHOTO.H2O2,2.59e-06 +PHOTO.PNA,1.89e-06 +PHOTO.HNO3,8.61e-08 +PHOTO.NTR,4.77e-07 +PHOTO.ROOH,1.81e-06 +PHOTO.MEPX,1.81e-06 +PHOTO.FORM->HO2,7.93e-06 +PHOTO.FORM->CO,2.2e-05 +PHOTO.ALD2,2.2e-06 +PHOTO.PACD,1.81e-06 +PHOTO.ALDX,2.2e-06 +PHOTO.OPEN,0.0006450000000000001 +PHOTO.MGLY,7.64e-05 +PHOTO.ISPD,1.98e-09 \ No newline at end of file diff --git a/examples/configs/carbon_bond_5/reactions.json b/examples/configs/carbon_bond_5/reactions.json new file mode 100644 index 000000000..763c7c013 --- /dev/null +++ b/examples/configs/carbon_bond_5/reactions.json @@ -0,0 +1,4766 @@ +{ + "camp-data": [ + { + "type": "MECHANISM", + "name": "music box interactive configuration", + "reactions": [ + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "NO2", + "reactants": { + "NO2": {} + }, + "products": { + "NO": { + "yield": 1 + }, + "O": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6e-34, + "Ea": 0, + "B": -2.4, + "D": 300, + "E": 0, + "reactants": { + "O": { + "qty": 1 + }, + "O2": { + "qty": 1 + }, + "M": { + "qty": 1 + } + }, + "products": { + "O3": { + "yield": 1 + }, + "M": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.6e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "NO": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 2.5e-31, + "k0_B": -1.8, + "k0_C": 0, + "kinf_A": 2.2e-11, + "kinf_B": -0.7, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "O": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "NO3": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 9e-32, + "k0_B": -1.5, + "k0_C": 0, + "kinf_A": 3e-11, + "kinf_B": 0, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "O": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO2": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "NO3": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "O3->O3P", + "reactants": { + "O3": {} + }, + "products": { + "O": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "O3->O1D", + "reactants": { + "O3": {} + }, + "products": { + "O1D": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.1e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O1D": { + "qty": 1 + }, + "M": { + "qty": 1 + } + }, + "products": { + "O": { + "yield": 1 + }, + "M": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.2e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O1D": { + "qty": 1 + }, + "H2O": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "NO3->NO2", + "reactants": { + "NO3": {} + }, + "products": { + "NO2": { + "yield": 1 + }, + "O": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "NO3->NO", + "reactants": { + "NO3": {} + }, + "products": { + "NO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.5e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.5e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "NO": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 2e-30, + "k0_B": -4.4, + "k0_C": 0, + "kinf_A": 1.4e-12, + "kinf_B": -0.7, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "NO3": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "N2O5": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.5e-22, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "N2O5": { + "qty": 1 + }, + "H2O": { + "qty": 1 + } + }, + "products": { + "HNO3": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-39, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "N2O5": { + "qty": 1 + }, + "H2O": { + "qty": 2 + } + }, + "products": { + "HNO3": { + "yield": 2 + } + } + }, + { + "type": "TROE", + "k0_A": 0.001, + "k0_B": -3.5, + "k0_C": -11000, + "kinf_A": 970000000000000, + "kinf_B": 0.1, + "kinf_C": -11080, + "Fc": 0.45, + "N": 1, + "reactants": { + "N2O5": { + "qty": 1 + } + }, + "products": { + "NO3": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-39, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO": { + "qty": 2 + }, + "O2": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5e-40, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO": { + "qty": 1 + }, + "NO2": { + "qty": 1 + }, + "H2O": { + "qty": 1 + } + }, + "products": { + "HONO": { + "yield": 2 + } + } + }, + { + "type": "TROE", + "k0_A": 7e-31, + "k0_B": -2.6, + "k0_C": 0, + "kinf_A": 3.6e-11, + "kinf_B": -0.1, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "NO": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HONO": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "HONO", + "reactants": { + "HONO": {} + }, + "products": { + "NO": { + "yield": 1 + }, + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "HONO": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-20, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HONO": { + "qty": 2 + } + }, + "products": { + "NO": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 2e-30, + "k0_B": -3, + "k0_C": 0, + "kinf_A": 2.5e-11, + "kinf_B": 0, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "NO2": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HNO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.4e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "HNO3": { + "qty": 1 + } + }, + "products": { + "NO3": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 6.5e-34, + "k0_B": 0, + "k0_C": 1335, + "kinf_A": 2.7e-17, + "kinf_B": 0, + "kinf_C": 2199, + "Fc": 1, + "N": 1, + "reactants": { + "OH": { + "qty": 1 + }, + "HNO3": { + "qty": 1 + } + }, + "products": { + "NO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 1.8e-31, + "k0_B": -3.2, + "k0_C": 0, + "kinf_A": 4.7e-12, + "kinf_B": 0, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "HO2": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "PNA": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 4.1e-05, + "k0_B": 0, + "k0_C": -10650, + "kinf_A": 4800000000000000, + "kinf_B": 0, + "kinf_C": -11170, + "Fc": 0.6, + "N": 1, + "reactants": { + "PNA": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "PNA": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HO2": { + "qty": 2 + } + }, + "products": { + "H2O2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-33, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HO2": { + "qty": 2 + }, + "M": { + "qty": 1 + } + }, + "products": { + "H2O2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.22e-34, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HO2": { + "qty": 2 + }, + "H2O": { + "qty": 1 + } + }, + "products": { + "H2O2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.38e-54, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HO2": { + "qty": 2 + }, + "H2O": { + "qty": 1 + }, + "M": { + "qty": 1 + } + }, + "products": { + "H2O2": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "H2O2", + "reactants": { + "H2O2": {} + }, + "products": { + "OH": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "H2O2": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.1e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O1D": { + "qty": 1 + }, + "H2": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.5e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "H2": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.2e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 2 + } + }, + "products": { + "O": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 6.9e-31, + "k0_B": -1, + "k0_C": 0, + "kinf_A": 2.6e-11, + "kinf_B": 0, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "OH": { + "qty": 2 + } + }, + "products": { + "H2O2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HO2": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "H2O2": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.2e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "HNO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-17, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.5e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 2 + } + }, + "products": { + "NO2": { + "yield": 2 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "PNA", + "reactants": { + "PNA": {} + }, + "products": { + "HO2": { + "yield": 0.61 + }, + "NO2": { + "yield": 0.61 + }, + "OH": { + "yield": 0.39 + }, + "NO3": { + "yield": 0.39 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "HNO3", + "reactants": { + "HNO3": {} + }, + "products": { + "OH": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "N2O5", + "reactants": { + "N2O5": {} + }, + "products": { + "NO2": { + "yield": 1 + }, + "NO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "XO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.6e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "XO2N": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NTR": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "XO2": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "ROOH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7.5e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "XO2N": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "ROOH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.9e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NTR": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HNO3": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "FORM": { + "yield": 0.33 + }, + "ALD2": { + "yield": 0.33 + }, + "ALDX": { + "yield": 0.33 + }, + "PAR": { + "yield": -0.66 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "NTR", + "reactants": { + "NTR": {} + }, + "products": { + "NO2": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "FORM": { + "yield": 0.33 + }, + "ALD2": { + "yield": 0.33 + }, + "ALDX": { + "yield": 0.33 + }, + "PAR": { + "yield": -0.66 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.01e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ROOH": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "XO2": { + "yield": 1 + }, + "ALD2": { + "yield": 0.5 + }, + "ALDX": { + "yield": 0.5 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "ROOH", + "reactants": { + "ROOH": {} + }, + "products": { + "OH": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "ALD2": { + "yield": 0.5 + }, + "ALDX": { + "yield": 0.5 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.44e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "CO": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.43e-33, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "CO": { + "qty": 1 + }, + "M": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.45e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "CH4": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.8e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "MEO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "FORM": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.1e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "MEO2": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "MEPX": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 9.5e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "MEO2": { + "qty": 2 + } + }, + "products": { + "FORM": { + "yield": 1.37 + }, + "HO2": { + "yield": 0.74 + }, + "MEOH": { + "yield": 0.63 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.8e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "MEPX": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 0.7 + }, + "XO2": { + "yield": 0.3 + }, + "HO2": { + "yield": 0.3 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "MEPX", + "reactants": { + "MEPX": {} + }, + "products": { + "FORM": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7.3e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "MEOH": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "FORM": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 9e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "FORM": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "FORM->HO2", + "reactants": { + "FORM": {} + }, + "products": { + "HO2": { + "yield": 2 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "FORM->CO", + "reactants": { + "FORM": {} + }, + "products": { + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.4e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "FORM": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.8e-16, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "FORM": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "HNO3": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 9.7e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "FORM": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "HCO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2400000000000, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HCO3": { + "qty": 1 + } + }, + "products": { + "FORM": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.6e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HCO3": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "FACD": { + "yield": 1 + }, + "NO2": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.6e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "HCO3": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "MEPX": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "FACD": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ALD2": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "C2O3": { + "yield": 1 + }, + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.6e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ALD2": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "C2O3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ALD2": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "C2O3": { + "yield": 1 + }, + "HNO3": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "ALD2", + "reactants": { + "ALD2": {} + }, + "products": { + "MEO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.1e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "C2O3": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 2.7e-28, + "k0_B": -7.1, + "k0_C": 0, + "kinf_A": 1.2e-11, + "kinf_B": -0.9, + "kinf_C": 0, + "Fc": 0.3, + "N": 1, + "reactants": { + "C2O3": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "PAN": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 0.0049, + "k0_B": 0, + "k0_C": -12100, + "kinf_A": 54000000000000000, + "kinf_B": 0, + "kinf_C": -13830, + "Fc": 0.3, + "N": 1, + "reactants": { + "PAN": { + "qty": 1 + } + }, + "products": { + "C2O3": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "PAN", + "reactants": { + "PAN": {} + }, + "products": { + "C2O3": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "C2O3": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "PACD": { + "yield": 0.8 + }, + "AACD": { + "yield": 0.2 + }, + "O3": { + "yield": 0.2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "C2O3": { + "qty": 1 + }, + "MEO2": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 0.9 + }, + "HO2": { + "yield": 0.9 + }, + "FORM": { + "yield": 1 + }, + "AACD": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.4e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "C2O3": { + "qty": 1 + }, + "XO2": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 0.9 + }, + "AACD": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "C2O3": { + "qty": 2 + } + }, + "products": { + "MEO2": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "PACD": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "C2O3": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "PACD", + "reactants": { + "PACD": {} + }, + "products": { + "MEO2": { + "yield": 1 + }, + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "AACD": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ALDX": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "CXO3": { + "yield": 1 + }, + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.1e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ALDX": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "CXO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.5e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ALDX": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "CXO3": { + "yield": 1 + }, + "HNO3": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "ALDX", + "reactants": { + "ALDX": {} + }, + "products": { + "MEO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CXO3": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "XO2": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 2.7e-28, + "k0_B": -7.1, + "k0_C": 0, + "kinf_A": 1.2e-11, + "kinf_B": -0.9, + "kinf_C": 0, + "Fc": 0.3, + "N": 1, + "reactants": { + "CXO3": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "PANX": { + "yield": 1 + } + } + }, + { + "type": "TROE", + "k0_A": 0.0049, + "k0_B": 0, + "k0_C": -12100, + "kinf_A": 54000000000000000, + "kinf_B": 0, + "kinf_C": -13830, + "Fc": 0.3, + "N": 1, + "reactants": { + "PANX": { + "qty": 1 + } + }, + "products": { + "CXO3": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "PANX", + "reactants": { + "PANX": {} + }, + "products": { + "CXO3": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "PANX": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CXO3": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "PACD": { + "yield": 0.8 + }, + "AACD": { + "yield": 0.2 + }, + "O3": { + "yield": 0.2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CXO3": { + "qty": 1 + }, + "MEO2": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 0.9 + }, + "XO2": { + "yield": 0.9 + }, + "HO2": { + "yield": 1 + }, + "AACD": { + "yield": 0.1 + }, + "FORM": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.4e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CXO3": { + "qty": 1 + }, + "XO2": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 0.9 + }, + "AACD": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CXO3": { + "qty": 2 + } + }, + "products": { + "ALD2": { + "yield": 2 + }, + "XO2": { + "yield": 2 + }, + "HO2": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.9e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CXO3": { + "qty": 1 + }, + "C2O3": { + "qty": 1 + } + }, + "products": { + "MEO2": { + "yield": 1 + }, + "XO2": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "ALD2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.1e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "PAR": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "XO2": { + "yield": 0.87 + }, + "XO2N": { + "yield": 0.13 + }, + "HO2": { + "yield": 0.11 + }, + "ALD2": { + "yield": 0.06 + }, + "PAR": { + "yield": -0.11 + }, + "ROR": { + "yield": 0.76 + }, + "ALDX": { + "yield": 0.05 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1000000000000000, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ROR": { + "qty": 1 + } + }, + "products": { + "XO2": { + "yield": 0.96 + }, + "ALD2": { + "yield": 0.6 + }, + "HO2": { + "yield": 0.94 + }, + "PAR": { + "yield": -2.1 + }, + "XO2N": { + "yield": 0.04 + }, + "ROR": { + "yield": 0.02 + }, + "ALDX": { + "yield": 0.5 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1600, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ROR": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.5e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "ROR": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "NTR": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O": { + "qty": 1 + }, + "OLE": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 0.2 + }, + "ALDX": { + "yield": 0.3 + }, + "HO2": { + "yield": 0.3 + }, + "XO2": { + "yield": 0.2 + }, + "CO": { + "yield": 0.2 + }, + "FORM": { + "yield": 0.2 + }, + "XO2N": { + "yield": 0.01 + }, + "PAR": { + "yield": 0.2 + }, + "OH": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.2e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "OLE": { + "qty": 1 + } + }, + "products": { + "FORM": { + "yield": 0.8 + }, + "ALD2": { + "yield": 0.33 + }, + "ALDX": { + "yield": 0.62 + }, + "XO2": { + "yield": 0.8 + }, + "HO2": { + "yield": 0.95 + }, + "PAR": { + "yield": -0.7 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.5e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "OLE": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 0.18 + }, + "FORM": { + "yield": 0.74 + }, + "ALDX": { + "yield": 0.32 + }, + "XO2": { + "yield": 0.22 + }, + "OH": { + "yield": 0.1 + }, + "CO": { + "yield": 0.33 + }, + "HO2": { + "yield": 0.44 + }, + "PAR": { + "yield": -1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "OLE": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + }, + "FORM": { + "yield": 1 + }, + "XO2": { + "yield": 0.91 + }, + "XO2N": { + "yield": 0.09 + }, + "ALDX": { + "yield": 0.56 + }, + "ALD2": { + "yield": 0.35 + }, + "PAR": { + "yield": -1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.04e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O": { + "qty": 1 + }, + "ETH": { + "qty": 1 + } + }, + "products": { + "FORM": { + "yield": 1 + }, + "HO2": { + "yield": 1.7 + }, + "CO": { + "yield": 1 + }, + "XO2": { + "yield": 0.7 + }, + "OH": { + "yield": 0.3 + } + } + }, + { + "type": "TROE", + "k0_A": 1e-28, + "k0_B": -0.8, + "k0_C": 0, + "kinf_A": 8.8e-12, + "kinf_B": 0, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "OH": { + "qty": 1 + }, + "ETH": { + "qty": 1 + } + }, + "products": { + "XO2": { + "yield": 1 + }, + "FORM": { + "yield": 1.56 + }, + "ALDX": { + "yield": 0.22 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "ETH": { + "qty": 1 + } + }, + "products": { + "FORM": { + "yield": 1 + }, + "CO": { + "yield": 0.63 + }, + "HO2": { + "yield": 0.13 + }, + "OH": { + "yield": 0.13 + }, + "FACD": { + "yield": 0.37 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.3e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "ETH": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 1 + }, + "XO2": { + "yield": 1 + }, + "FORM": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "IOLE": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 1.24 + }, + "ALDX": { + "yield": 0.66 + }, + "HO2": { + "yield": 0.1 + }, + "XO2": { + "yield": 0.1 + }, + "CO": { + "yield": 0.1 + }, + "PAR": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "IOLE": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 1.3 + }, + "ALDX": { + "yield": 0.7 + }, + "HO2": { + "yield": 1 + }, + "XO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.4e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "IOLE": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 0.65 + }, + "ALDX": { + "yield": 0.35 + }, + "FORM": { + "yield": 0.25 + }, + "CO": { + "yield": 0.25 + }, + "O": { + "yield": 0.5 + }, + "OH": { + "yield": 0.5 + }, + "HO2": { + "yield": 0.5 + } + } + }, + { + "type": "ARRHENIUS", + "A": 9.6e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "IOLE": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 1.18 + }, + "ALDX": { + "yield": 0.64 + }, + "HO2": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TOL": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 0.44 + }, + "XO2": { + "yield": 0.08 + }, + "CRES": { + "yield": 0.36 + }, + "TO2": { + "yield": 0.56 + }, + "TOLRO2": { + "yield": 0.765 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.1e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 0.9 + }, + "HO2": { + "yield": 0.9 + }, + "OPEN": { + "yield": 0.9 + }, + "NTR": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.2, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TO2": { + "qty": 1 + } + }, + "products": { + "CRES": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.1e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "CRES": { + "qty": 1 + } + }, + "products": { + "CRO": { + "yield": 0.4 + }, + "XO2": { + "yield": 0.6 + }, + "HO2": { + "yield": 0.6 + }, + "OPEN": { + "yield": 0.3 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.2e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CRES": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "CRO": { + "yield": 1 + }, + "HNO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.4e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CRO": { + "qty": 1 + }, + "NO2": { + "qty": 1 + } + }, + "products": { + "NTR": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.5e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CRO": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "CRES": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 9, + "MUSICA name": "OPEN", + "reactants": { + "OPEN": {} + }, + "products": { + "C2O3": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OPEN": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "XO2": { + "yield": 1 + }, + "CO": { + "yield": 2 + }, + "HO2": { + "yield": 2 + }, + "C2O3": { + "yield": 1 + }, + "FORM": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.4e-17, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OPEN": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "ALDX": { + "yield": 0.03 + }, + "C2O3": { + "yield": 0.62 + }, + "FORM": { + "yield": 0.7 + }, + "XO2": { + "yield": 0.03 + }, + "CO": { + "yield": 0.69 + }, + "OH": { + "yield": 0.08 + }, + "HO2": { + "yield": 0.76 + }, + "MGLY": { + "yield": 0.2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.7e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "XYL": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 0.7 + }, + "XO2": { + "yield": 0.5 + }, + "CRES": { + "yield": 0.2 + }, + "MGLY": { + "yield": 0.8 + }, + "PAR": { + "yield": 1.1 + }, + "TO2": { + "yield": 0.3 + }, + "XYLRO2": { + "yield": 0.804 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.8e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "MGLY": { + "qty": 1 + } + }, + "products": { + "XO2": { + "yield": 1 + }, + "C2O3": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "MGLY", + "reactants": { + "MGLY": {} + }, + "products": { + "C2O3": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.6e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O": { + "qty": 1 + }, + "ISOP": { + "qty": 1 + } + }, + "products": { + "ISPD": { + "yield": 0.75 + }, + "FORM": { + "yield": 0.5 + }, + "XO2": { + "yield": 0.25 + }, + "HO2": { + "yield": 0.25 + }, + "CXO3": { + "yield": 0.25 + }, + "PAR": { + "yield": 0.25 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.54e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "ISOP": { + "qty": 1 + } + }, + "products": { + "ISPD": { + "yield": 0.912 + }, + "FORM": { + "yield": 0.629 + }, + "XO2": { + "yield": 0.991 + }, + "HO2": { + "yield": 0.912 + }, + "XO2N": { + "yield": 0.088 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7.86e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "ISOP": { + "qty": 1 + } + }, + "products": { + "ISPD": { + "yield": 0.65 + }, + "FORM": { + "yield": 0.6 + }, + "XO2": { + "yield": 0.2 + }, + "HO2": { + "yield": 0.066 + }, + "OH": { + "yield": 0.266 + }, + "CXO3": { + "yield": 0.2 + }, + "ALDX": { + "yield": 0.15 + }, + "PAR": { + "yield": 0.35 + }, + "CO": { + "yield": 0.066 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.03e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "ISOP": { + "qty": 1 + } + }, + "products": { + "ISPD": { + "yield": 0.2 + }, + "NTR": { + "yield": 0.8 + }, + "XO2": { + "yield": 1 + }, + "HO2": { + "yield": 0.8 + }, + "NO2": { + "yield": 0.2 + }, + "ALDX": { + "yield": 0.8 + }, + "PAR": { + "yield": 2.4 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.36e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "ISPD": { + "qty": 1 + } + }, + "products": { + "PAR": { + "yield": 1.565 + }, + "FORM": { + "yield": 0.167 + }, + "XO2": { + "yield": 0.713 + }, + "HO2": { + "yield": 0.503 + }, + "CO": { + "yield": 0.334 + }, + "MGLY": { + "yield": 0.168 + }, + "ALD2": { + "yield": 0.252 + }, + "C2O3": { + "yield": 0.21 + }, + "CXO3": { + "yield": 0.25 + }, + "ALDX": { + "yield": 0.12 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7.1e-18, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "O3": { + "qty": 1 + }, + "ISPD": { + "qty": 1 + } + }, + "products": { + "C2O3": { + "yield": 0.114 + }, + "FORM": { + "yield": 0.15 + }, + "MGLY": { + "yield": 0.85 + }, + "HO2": { + "yield": 0.154 + }, + "OH": { + "yield": 0.268 + }, + "XO2": { + "yield": 0.064 + }, + "ALD2": { + "yield": 0.02 + }, + "PAR": { + "yield": 0.36 + }, + "CO": { + "yield": 0.225 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO3": { + "qty": 1 + }, + "ISPD": { + "qty": 1 + } + }, + "products": { + "ALDX": { + "yield": 0.357 + }, + "FORM": { + "yield": 0.282 + }, + "PAR": { + "yield": 1.282 + }, + "HO2": { + "yield": 0.925 + }, + "CO": { + "yield": 0.643 + }, + "NTR": { + "yield": 0.85 + }, + "CXO3": { + "yield": 0.075 + }, + "XO2": { + "yield": 0.075 + }, + "HNO3": { + "yield": 0.15 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 0.0036, + "MUSICA name": "ISPD", + "reactants": { + "ISPD": {} + }, + "products": { + "CO": { + "yield": 0.333 + }, + "ALD2": { + "yield": 0.067 + }, + "FORM": { + "yield": 0.9 + }, + "PAR": { + "yield": 0.832 + }, + "HO2": { + "yield": 1.033 + }, + "XO2": { + "yield": 0.7 + }, + "C2O3": { + "yield": 0.967 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.6e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TERP": { + "qty": 1 + }, + "O": { + "qty": 1 + } + }, + "products": { + "ALDX": { + "yield": 0.15 + }, + "PAR": { + "yield": 5.12 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.5e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TERP": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 0.75 + }, + "XO2": { + "yield": 1.25 + }, + "XO2N": { + "yield": 0.25 + }, + "FORM": { + "yield": 0.28 + }, + "PAR": { + "yield": 1.66 + }, + "ALDX": { + "yield": 0.47 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.2e-15, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TERP": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 0.57 + }, + "HO2": { + "yield": 0.07 + }, + "XO2": { + "yield": 0.76 + }, + "XO2N": { + "yield": 0.18 + }, + "FORM": { + "yield": 0.24 + }, + "CO": { + "yield": 0.001 + }, + "PAR": { + "yield": 7 + }, + "ALDX": { + "yield": 0.21 + }, + "CXO3": { + "yield": 0.39 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TERP": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "NO2": { + "yield": 0.47 + }, + "HO2": { + "yield": 0.28 + }, + "XO2": { + "yield": 1.03 + }, + "XO2N": { + "yield": 0.25 + }, + "ALDX": { + "yield": 0.47 + }, + "NTR": { + "yield": 0.53 + } + } + }, + { + "type": "TROE", + "k0_A": 3e-31, + "k0_B": -3.3, + "k0_C": 0, + "kinf_A": 1.5e-12, + "kinf_B": 0, + "kinf_C": 0, + "Fc": 0.6, + "N": 1, + "reactants": { + "SO2": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "SULF": { + "yield": 1 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.9e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "ETOH": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + }, + "ALD2": { + "yield": 0.9 + }, + "ALDX": { + "yield": 0.05 + }, + "FORM": { + "yield": 0.1 + }, + "XO2": { + "yield": 0.1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "ETHA": { + "qty": 1 + } + }, + "products": { + "ALD2": { + "yield": 0.991 + }, + "XO2": { + "yield": 0.991 + }, + "XO2N": { + "yield": 0.009 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.5e-19, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "NO2": { + "qty": 1 + }, + "ISOP": { + "qty": 1 + } + }, + "products": { + "ISPD": { + "yield": 0.2 + }, + "NTR": { + "yield": 0.8 + }, + "XO2": { + "yield": 1 + }, + "HO2": { + "yield": 0.8 + }, + "NO": { + "yield": 0.2 + }, + "ALDX": { + "yield": 0.8 + }, + "PAR": { + "yield": 2.4 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "CL2", + "reactants": { + "CL2": {} + }, + "products": { + "CL": { + "yield": 2 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.3e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "CLO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.63e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CLO": { + "qty": 2 + } + }, + "products": { + "CL2": { + "yield": 0.3 + }, + "CL": { + "yield": 1.4 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.4e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CLO": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "CL": { + "yield": 1 + }, + "NO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CLO": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "HOCL": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "OH": { + "qty": 1 + }, + "FMCL": { + "qty": 1 + } + }, + "products": { + "CL": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.6e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "CH4": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "MEO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "PAR": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "XO2": { + "yield": 0.87 + }, + "XO2N": { + "yield": 0.13 + }, + "HO2": { + "yield": 0.11 + }, + "ALD2": { + "yield": 0.06 + }, + "PAR": { + "yield": -0.11 + }, + "ROR": { + "yield": 0.76 + }, + "ALDX": { + "yield": 0.05 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.3e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "ETHA": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "ALD2": { + "yield": 0.991 + }, + "XO2": { + "yield": 0.991 + }, + "XO2N": { + "yield": 0.009 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.07e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "ETH": { + "qty": 1 + } + }, + "products": { + "FMCL": { + "yield": 1 + }, + "XO2": { + "yield": 2 + }, + "HO2": { + "yield": 1 + }, + "FORM": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.5e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "OLE": { + "qty": 1 + } + }, + "products": { + "FMCL": { + "yield": 1 + }, + "ALD2": { + "yield": 0.33 + }, + "ALDX": { + "yield": 0.67 + }, + "XO2": { + "yield": 2 + }, + "HO2": { + "yield": 1 + }, + "PAR": { + "yield": -1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 3.5e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "IOLE": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 0.3 + }, + "FMCL": { + "yield": 0.7 + }, + "ALD2": { + "yield": 0.45 + }, + "ALDX": { + "yield": 0.55 + }, + "OLE": { + "yield": 0.3 + }, + "PAR": { + "yield": 0.3 + }, + "XO2": { + "yield": 1.7 + }, + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 4.3e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "ISOP": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 0.15 + }, + "XO2": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "FMCL": { + "yield": 0.85 + }, + "ISPD": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.2e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "FORM": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "CO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 7.9e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "ALD2": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "C2O3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.3e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "ALDX": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "CXO3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 5.5e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "MEOH": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "FORM": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 8.2e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "CL": { + "qty": 1 + }, + "ETOH": { + "qty": 1 + } + }, + "products": { + "HCL": { + "yield": 1 + }, + "HO2": { + "yield": 1 + }, + "ALD2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 6.58e-13, + "Ea": 0, + "B": 1.16, + "D": 300, + "E": 0, + "reactants": { + "HCL": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "CL": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TOLRO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "TOLRO2": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "XYLRO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "XYLRO2": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.47e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "BENZENE": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + }, + "BENZRO2": { + "yield": 0.764 + } + } + }, + { + "type": "ARRHENIUS", + "A": 2.7e-12, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "BENZRO2": { + "qty": 1 + }, + "NO": { + "qty": 1 + } + }, + "products": { + "NO": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-13, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "BENZRO2": { + "qty": 1 + }, + "HO2": { + "qty": 1 + } + }, + "products": { + "HO2": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.16e-14, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "SESQ": { + "qty": 1 + }, + "O3": { + "qty": 1 + } + }, + "products": { + "O3": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.97e-10, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "SESQ": { + "qty": 1 + }, + "OH": { + "qty": 1 + } + }, + "products": { + "OH": { + "yield": 1 + } + } + }, + { + "type": "ARRHENIUS", + "A": 1.9e-11, + "Ea": 0, + "B": 0, + "D": 300, + "E": 0, + "reactants": { + "SESQ": { + "qty": 1 + }, + "NO3": { + "qty": 1 + } + }, + "products": { + "NO3": { + "yield": 1 + } + } + }, + { + "type": "PHOTOLYSIS", + "scaling factor": 1, + "MUSICA name": "O2", + "reactants": { + "O2": {} + }, + "products": { + "O": { + "yield": 2 + } + } + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "NO", + "species": "NO" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "NO2", + "species": "NO2" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "CO", + "species": "CO" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "SO2", + "species": "SO2" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "FORM", + "species": "FORM" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "MEOH", + "species": "MEOH" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "ALD2", + "species": "ALD2" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "PAR", + "species": "PAR" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "ETH", + "species": "ETH" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "OLE", + "species": "OLE" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "IOLE", + "species": "IOLE" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "TOL", + "species": "TOL" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "XYL", + "species": "XYL" + }, + { + "type": "EMISSION", + "scaling factor": 1, + "MUSICA name": "ISOP", + "species": "ISOP" + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/configs/carbon_bond_5/species.json b/examples/configs/carbon_bond_5/species.json new file mode 100644 index 000000000..d96ac8098 --- /dev/null +++ b/examples/configs/carbon_bond_5/species.json @@ -0,0 +1 @@ +{"camp-data": [{"name": "AACD", "type": "CHEM_SPEC"}, {"name": "ALD2", "type": "CHEM_SPEC"}, {"name": "ALDX", "type": "CHEM_SPEC"}, {"name": "BENZENE", "type": "CHEM_SPEC"}, {"name": "BENZRO2", "type": "CHEM_SPEC"}, {"name": "C2O3", "type": "CHEM_SPEC"}, {"name": "CH4", "type": "CHEM_SPEC"}, {"name": "CL", "type": "CHEM_SPEC"}, {"name": "CL2", "type": "CHEM_SPEC"}, {"name": "CLO", "type": "CHEM_SPEC"}, {"name": "CO", "type": "CHEM_SPEC"}, {"name": "CRES", "type": "CHEM_SPEC"}, {"name": "CRO", "type": "CHEM_SPEC"}, {"name": "CXO3", "type": "CHEM_SPEC"}, {"name": "ETH", "type": "CHEM_SPEC"}, {"name": "ETHA", "type": "CHEM_SPEC"}, {"name": "ETOH", "type": "CHEM_SPEC"}, {"name": "FACD", "type": "CHEM_SPEC"}, {"name": "FMCL", "type": "CHEM_SPEC"}, {"name": "FORM", "type": "CHEM_SPEC"}, {"name": "H2", "type": "CHEM_SPEC"}, {"name": "H2O", "type": "CHEM_SPEC"}, {"name": "H2O2", "type": "CHEM_SPEC"}, {"name": "HCL", "type": "CHEM_SPEC"}, {"name": "HCO3", "type": "CHEM_SPEC"}, {"name": "HNO3", "type": "CHEM_SPEC"}, {"name": "HO2", "type": "CHEM_SPEC"}, {"name": "HOCL", "type": "CHEM_SPEC"}, {"name": "HONO", "type": "CHEM_SPEC"}, {"name": "IOLE", "type": "CHEM_SPEC"}, {"name": "ISOP", "type": "CHEM_SPEC"}, {"name": "ISPD", "type": "CHEM_SPEC"}, {"name": "M", "type": "CHEM_SPEC"}, {"name": "MEO2", "type": "CHEM_SPEC"}, {"name": "MEOH", "type": "CHEM_SPEC"}, {"name": "MEPX", "type": "CHEM_SPEC"}, {"name": "MGLY", "type": "CHEM_SPEC"}, {"name": "N2O5", "type": "CHEM_SPEC"}, {"name": "NO", "type": "CHEM_SPEC"}, {"name": "NO2", "type": "CHEM_SPEC"}, {"name": "NO3", "type": "CHEM_SPEC"}, {"name": "NTR", "type": "CHEM_SPEC"}, {"name": "O", "type": "CHEM_SPEC"}, {"name": "O1D", "type": "CHEM_SPEC"}, {"name": "O3", "type": "CHEM_SPEC"}, {"name": "OH", "type": "CHEM_SPEC"}, {"name": "O2", "type": "CHEM_SPEC"}, {"name": "OLE", "type": "CHEM_SPEC"}, {"name": "OPEN", "type": "CHEM_SPEC"}, {"name": "PACD", "type": "CHEM_SPEC"}, {"name": "PAN", "type": "CHEM_SPEC"}, {"name": "PANX", "type": "CHEM_SPEC"}, {"name": "PAR", "type": "CHEM_SPEC"}, {"name": "PNA", "type": "CHEM_SPEC"}, {"name": "ROOH", "type": "CHEM_SPEC"}, {"name": "ROR", "type": "CHEM_SPEC"}, {"name": "SESQ", "type": "CHEM_SPEC"}, {"name": "SO2", "type": "CHEM_SPEC"}, {"name": "SULF", "type": "CHEM_SPEC"}, {"name": "TERP", "type": "CHEM_SPEC"}, {"name": "TO2", "type": "CHEM_SPEC"}, {"name": "TOL", "type": "CHEM_SPEC"}, {"name": "TOLRO2", "type": "CHEM_SPEC"}, {"name": "XO2", "type": "CHEM_SPEC"}, {"name": "XO2N", "type": "CHEM_SPEC"}, {"name": "XYL", "type": "CHEM_SPEC"}, {"name": "XYLRO2", "type": "CHEM_SPEC"}]} \ No newline at end of file diff --git a/examples/configs/chapman/config.json b/examples/configs/chapman/config.json new file mode 100644 index 000000000..04d0ef289 --- /dev/null +++ b/examples/configs/chapman/config.json @@ -0,0 +1,6 @@ +{ + "camp-files": [ + "species.json", + "reactions.json" + ] +} diff --git a/examples/configs/chapman/initial_conditions.csv b/examples/configs/chapman/initial_conditions.csv new file mode 100644 index 000000000..026fecfb7 --- /dev/null +++ b/examples/configs/chapman/initial_conditions.csv @@ -0,0 +1,12 @@ +CONC.M,0.779127760953859 +CONC.O1D,0.925827943758015 +CONC.O,0.566253459937848 +CONC.O2,0.566253459937848 +CONC.O3,0.566253459937848 +ENV.temperature,227.0 +ENV.pressure,1200.0 +ENV.air_density,1e6 +ENV.time_step,3600 +PHOTO.R1,6.0e-11 +PHOTO.R3,1.0e-3 +PHOTO.R5,1.0e-3 \ No newline at end of file diff --git a/examples/Chapman/reactions.json b/examples/configs/chapman/reactions.json similarity index 99% rename from examples/Chapman/reactions.json rename to examples/configs/chapman/reactions.json index f37fd2030..679593f9b 100644 --- a/examples/Chapman/reactions.json +++ b/examples/configs/chapman/reactions.json @@ -94,4 +94,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/examples/Chapman/species.json b/examples/configs/chapman/species.json similarity index 100% rename from examples/Chapman/species.json rename to examples/configs/chapman/species.json diff --git a/examples/configs/robertson/config.json b/examples/configs/robertson/config.json new file mode 100644 index 000000000..514d11240 --- /dev/null +++ b/examples/configs/robertson/config.json @@ -0,0 +1,6 @@ +{ + "camp-files": [ + "species.json", + "reactions.json" + ] + } \ No newline at end of file diff --git a/examples/configs/robertson/initial_conditions.csv b/examples/configs/robertson/initial_conditions.csv new file mode 100644 index 000000000..b9edac99c --- /dev/null +++ b/examples/configs/robertson/initial_conditions.csv @@ -0,0 +1,10 @@ +CONC.A,1 +CONC.B,0 +CONC.C,0 +ENV.temperature,272.5 +ENV.pressure,101253.3 +ENV.air_density,1e6 +ENV.time_step,200.0 +PHOTO.r1,0.04 +PHOTO.r2,3e7 +PHOTO.r3,1e4 \ No newline at end of file diff --git a/examples/configs/robertson/reactions.json b/examples/configs/robertson/reactions.json new file mode 100644 index 000000000..cbffae848 --- /dev/null +++ b/examples/configs/robertson/reactions.json @@ -0,0 +1,43 @@ +{ + "camp-data": [ + { + "name": "reaction rates no user defined", + "type": "MECHANISM", + "reactions": [ + { + "type": "PHOTOLYSIS", + "reactants": { + "A": {} + }, + "products": { + "B": {} + }, + "MUSICA name": "r1" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": { "qty": 2} + }, + "products": { + "B": {}, + "C": {} + }, + "MUSICA name": "r2" + }, + { + "type": "PHOTOLYSIS", + "reactants": { + "B": {}, + "C": {} + }, + "products": { + "A": {}, + "C": {} + }, + "MUSICA name": "r3" + } + ] + } + ] +} \ No newline at end of file diff --git a/examples/configs/robertson/species.json b/examples/configs/robertson/species.json new file mode 100644 index 000000000..5a1a8db68 --- /dev/null +++ b/examples/configs/robertson/species.json @@ -0,0 +1,16 @@ +{ + "camp-data": [ + { + "name": "A", + "type": "CHEM_SPEC" + }, + { + "name": "B", + "type": "CHEM_SPEC" + }, + { + "name": "C", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/examples/example.cpp b/examples/example.cpp new file mode 100644 index 000000000..ae7c93cf1 --- /dev/null +++ b/examples/example.cpp @@ -0,0 +1,261 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +// Each rate constant is in its own header file +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; +using namespace micm; + +/// @brief Struct of mapping keys in certain type to its initial condition values +struct InitialConditions +{ + std::unordered_map environments; + std::unordered_map> concentrations; + std::unordered_map> custom_rate_params; + + bool empty() + { + if (!environments.empty()) + return false; + else + return true; + } + + bool is_incomplete() + { + return !status_; + } + + void incomplete_parsing() + { + status_ = false; + } + + private: + bool status_ = true; +}; + +/// @brief Reads CSV file and creates InitialConditions object +/// holding initial values for input data +/// @param filename +/// @return InitialCondtions object +InitialConditions readCSVToMap(const std::string& filename) +{ + const std::string CONC_PREFIX = "CONC."; + const std::string ENV_PREFIX = "ENV."; + const std::string PHOTO_PREFIX = "PHOTO."; + const std::string EMIS_PREFIX = "EMIS."; + const std::string LOSS_PREFIX = "LOSS."; + const std::string USER_PREFIX = "USER."; + constexpr int CONC_POS = 5; + constexpr int ENV_POS = 4; + + InitialConditions dataMap; + std::ifstream file(filename); + + if (!file.is_open()) + { + std::cerr << "Error: Unable to open file \"" << filename << "\"" << std::endl; + return dataMap; + } + + std::string line; + std::string key, value_string; + size_t delimiter_pos; + double value; + + while (std::getline(file, line)) + { + if (line.empty()) continue; + + // Find concentrations + else if (line.find(CONC_PREFIX) != std::string::npos) + { + delimiter_pos = line.find_last_of(','); + if (delimiter_pos == std::string::npos) + { + std::cerr << "Error: Unable to find the delimiter \',' in \"" << line << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + + key = line.substr(CONC_POS, delimiter_pos - CONC_POS); + value_string = line.substr(delimiter_pos + 1); + + try + { + value = std::stod(value_string); + dataMap.concentrations[key].emplace_back(value); + } + catch(std::invalid_argument) + { + std::cerr << "Parsing Error: Unable to convert string to double for the value of "<< "\""<< value_string << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + } + + // Find environment parameters (temp, pressure, density) + else if (line.find(ENV_PREFIX) != std::string::npos) + { + delimiter_pos = line.find_last_of(','); + if (delimiter_pos == std::string::npos) + { + std::cerr << "Error: Unable to find the delimiter \',' in \"" << line << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + + key = line.substr(ENV_POS, delimiter_pos - ENV_POS); + value_string = line.substr(delimiter_pos + 1); + + try + { + value = std::stod(value_string); + dataMap.environments[key] = value; + } + catch(std::invalid_argument) + { + std::cerr << "Parsing Error: Unable to convert string to double for the value of "<< "\""<< value_string << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + } + // Find custom rate constants + else if (line.find(PHOTO_PREFIX) != std::string::npos + || line.find(EMIS_PREFIX) != std::string::npos + || line.find(LOSS_PREFIX) != std::string::npos + || line.find(USER_PREFIX) != std::string::npos) + { + delimiter_pos = line.find_last_of(','); + if (delimiter_pos == std::string::npos) + { + std::cerr << "Error: Unable to find the delimiter \',' in \"" << line << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + + key = line.substr(0, delimiter_pos); + value_string = line.substr(delimiter_pos + 1); + + try + { + value = std::stod(value_string); + dataMap.custom_rate_params[key].emplace_back(value); + } + catch(std::invalid_argument) + { + std::cerr << "Parsing Error: Unable to convert string to double for the value of "<< "\""<< value_string << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + } + else + { + std::cerr << "Error: Unable to parse string \"" << line << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + } + + return dataMap; +} + + +int main(const int argc, const char *argv[]) +{ + if (argc < 2) + { + std::cout << " path Path to the folder holding the configuration data" << std::endl; + std::cout << " initial condition csv file contanining initial condtions" << std::endl; + return 1; + } + + fs::path config_path {argv[1]}; + std::string initial_conditions_file {argv[2]}; + + // Read configure files + SolverConfig solverConfig; + ConfigParseStatus status = solverConfig.ReadAndParse(config_path); + if (status != ConfigParseStatus::Success) + { + std::cout << "Error: Parsing configuration data failed" << std::endl; + return 1; + } + + // Read csv file containing initial conditions + auto dataMap = readCSVToMap(initial_conditions_file); + if (dataMap.empty()) + { + std::cout << "Error: Failed in reading CSV file. Please verify file path and/or file name of \"" << initial_conditions_file << "\""<< std::endl; + return 1; + } + else if (dataMap.is_incomplete()) + { + std::cout << "Error: Parsing csv file is incomplete . Please verify content formatting of \"" << initial_conditions_file << "\""<< std::endl; + return 1; + } + + SolverParameters solver_params = solverConfig.GetSolverParams(); + auto chemical_system = solver_params.system_; + auto reactions = solver_params.processes_; + + RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + + State state = solver.GetState(); + + state.conditions_[0].temperature_ = dataMap.environments["temperature"]; // K + state.conditions_[0].pressure_ = dataMap.environments["pressure"]; // Pa + state.conditions_[0].air_density_ = dataMap.environments["air_density"]; // mol m-3 + + state.SetConcentrations(dataMap.concentrations); + + if (!dataMap.custom_rate_params.empty()) + { + for(auto& [rate_constant_key, rate_constant_val] : dataMap.custom_rate_params) + { + state.SetCustomRateParameter(rate_constant_key, rate_constant_val[0]); + } + } + + // choose a timestep and print the initial state + double time_step = dataMap.environments["time_step"]; // s + + state.PrintHeader(); + state.PrintState(0); + + // Depending on how stiff the system is + // the solver integration step may not be able to solve for the full time step + // so we need to track how much time the solver was able to integrate for and continue + // solving until we finish + double elapsed_solve_time = 0; + + while (elapsed_solve_time < time_step) + { + auto result = solver.Solve(time_step - elapsed_solve_time, state); + elapsed_solve_time = result.final_time_; + state.variables_ = result.result_; + } + state.PrintState(time_step); + + return 0; +} \ No newline at end of file diff --git a/include/micm/solver/state.hpp b/include/micm/solver/state.hpp index 5b7750439..fa958d05b 100644 --- a/include/micm/solver/state.hpp +++ b/include/micm/solver/state.hpp @@ -13,6 +13,7 @@ #include #include #include +#include namespace micm { @@ -75,6 +76,13 @@ namespace micm /// @param value new parameter value void SetCustomRateParameter(const std::string& label, double value); void SetCustomRateParameter(const std::string& label, const std::vector& values); + + /// @brief Print a header of species to display concentrations with respect to time + void PrintHeader(); + + /// @brief Print state (concentrations) at the given time + /// @param time solving time + void PrintState(double time); }; } // namespace micm diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index 6197fabe5..750b8baaf 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -94,7 +94,7 @@ namespace micm { auto param = custom_rate_parameter_map_.find(label); if (param == custom_rate_parameter_map_.end()) - throw std::invalid_argument("Unkonwn rate constant parameter '" + label + "'"); + throw std::invalid_argument("Unknown rate constant parameter '" + label + "'"); if (custom_rate_parameters_.size() != 1) throw std::invalid_argument("Incorrect number of custom rate parameter values passed to multi-gridcell State"); custom_rate_parameters_[0][param->second] = value; @@ -105,11 +105,51 @@ namespace micm { auto param = custom_rate_parameter_map_.find(label); if (param == custom_rate_parameter_map_.end()) - throw std::invalid_argument("Unkonwn rate constant parameter '" + label + "'"); + throw std::invalid_argument("Unknown rate constant parameter '" + label + "'"); if (custom_rate_parameters_.size() != values.size()) throw std::invalid_argument("Incorrect number of custom rate parameter values passed to multi-gridcell State"); for (std::size_t i = 0; i < custom_rate_parameters_.size(); ++i) custom_rate_parameters_[i][param->second] = values[i]; } + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::PrintHeader() + { + auto largest_str_iter = std::max_element(variable_names_.begin(), variable_names_.end(), + [](const auto& a, const auto& b) { + return a.size() < b.size();}); + int largest_str_size = largest_str_iter->size(); + int width = (largest_str_size < 10) ? 11 : largest_str_size + 2; + + std::cout << std::setw(5) << "time"; + + for(auto& species : variable_names_) + { + std::cout << "," << std::setw(width) << species; + } + std::cout << std::endl; + } + + template class MatrixPolicy, template class SparseMatrixPolicy> + inline void State::PrintState(double time) + { + std::ios oldState(nullptr); + oldState.copyfmt(std::cout); + + auto largest_str_iter = std::max_element(variable_names_.begin(), variable_names_.end(), + [](const auto& a, const auto& b) { + return a.size() < b.size();}); + int largest_str_size = largest_str_iter->size(); + int width = (largest_str_size < 10) ? 11 : largest_str_size + 2; + + std::cout << std::setw(5) << time << std::flush; + + for(auto& species : variable_names_) + { + std::cout << std::scientific << "," << std::setw(width) << std::setprecision(2) << variables_[0][variable_map_[species]]; + } + std::cout << std::endl; + std::cout.copyfmt(oldState); + } + } // namespace micm diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt index bcd85040e..b3d56dd63 100644 --- a/test/examples/CMakeLists.txt +++ b/test/examples/CMakeLists.txt @@ -1,11 +1,7 @@ -################################################################################ -# Copy test data - -add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/examples ${CMAKE_BINARY_DIR}/examples) - ################################################################################ # Build example tests -create_standard_test(NAME Chapman SOURCES test_Chapman.cpp) +create_standard_test(NAME Chapman SOURCES test_chapman.cpp) +create_standard_test(NAME carbon_bond_5 SOURCES test_carbon_bond_5.cpp) +create_standard_test(NAME robertson SOURCES test_robertson.cpp) create_standard_test(NAME TS1 SOURCES test_TS1.cpp) diff --git a/test/examples/test_TS1.cpp b/test/examples/test_TS1.cpp index 7f6aca75f..4ce2e141c 100644 --- a/test/examples/test_TS1.cpp +++ b/test/examples/test_TS1.cpp @@ -18,7 +18,7 @@ TEST(Examples, TS1) { micm::SolverConfig config; - std::string config_path = "./examples/TS1"; + std::string config_path = "./examples/configs/TS1"; auto status = config.ReadAndParse(config_path); EXPECT_EQ(status, micm::ConfigParseStatus::Success); auto solver_params = config.GetSolverParams(); diff --git a/test/examples/test_carbon_bond_5.cpp b/test/examples/test_carbon_bond_5.cpp new file mode 100644 index 000000000..396fd4fe2 --- /dev/null +++ b/test/examples/test_carbon_bond_5.cpp @@ -0,0 +1,84 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +// +// Test of the TS1 example mechanism (MZ327_TS1.2_20230307 in the Chemistry Cafe) +// +// This only tests that a solver can be built for this mechanism and that it +// can be run for a given set of initial conditions without solver errors. +// +// Comparisons with other chemistry solvers are included in +// https://github.com/NCAR/MUSICA-Performance-Comparison + +#include + +#include +#include + +TEST(Examples, carbon_bond_5) +{ + micm::SolverConfig config; + std::string config_path = "./examples/configs/carbon_bond_5"; + auto status = config.ReadAndParse(config_path); + EXPECT_EQ(status, micm::ConfigParseStatus::Success); + auto solver_params = config.GetSolverParams(); + micm::RosenbrockSolver<> solver{ solver_params.system_, + solver_params.processes_, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + micm::State state = solver.GetState(); + + const double temperature = 287.45; // K + const double pressure = 101319.9; // Pa + const double air_density = 1e6; // mol m-3 + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + std::unordered_map> custom_rate_constants = { + {"EMIS.NO", {1.44e-10}}, + {"EMIS.NO2", {7.56e-12}}, + {"EMIS.CO", {1.9600000000000003e-09}}, + {"EMIS.SO2", {1.06e-09}}, + {"EMIS.FORM", {1.02e-11}}, + {"EMIS.MEOH", {5.920000000000001e-13}}, + {"EMIS.ALD2", {4.25e-12}}, + {"EMIS.PAR", {4.27e-10}}, + {"EMIS.ETH", {4.62e-11}}, + {"EMIS.OLE", {1.49e-11}}, + {"EMIS.IOLE", {1.49e-11}}, + {"EMIS.TOL", {1.53e-11}}, + {"EMIS.XYL", {1.4e-11}}, + {"EMIS.ISOP", {6.03e-12}}, + {"PHOTO.NO2", {0.00477}}, + {"PHOTO.O3->O1D", {2.26e-06}}, + {"PHOTO.O3->O3P", {0.00025299999999999997}}, + {"PHOTO.NO3->NO2", {0.11699999999999999}}, + {"PHOTO.NO3->NO", {0.0144}}, + {"PHOTO.HONO", {0.000918}}, + {"PHOTO.H2O2", {2.59e-06}}, + {"PHOTO.PNA", {1.89e-06}}, + {"PHOTO.HNO3", {8.61e-08}}, + {"PHOTO.NTR", {4.77e-07}}, + {"PHOTO.ROOH", {1.81e-06}}, + {"PHOTO.MEPX", {1.81e-06}}, + {"PHOTO.FORM->HO2", {7.93e-06}}, + {"PHOTO.FORM->CO", {2.2e-05}}, + {"PHOTO.ALD2", {2.2e-06}}, + {"PHOTO.PACD", {1.81e-06}}, + {"PHOTO.ALDX", {2.2e-06}}, + {"PHOTO.OPEN", {0.0006450000000000001}}, + {"PHOTO.MGLY", {7.64e-05}}, + {"PHOTO.ISPD", {1.98e-09}} + }; + + state.SetCustomRateParameters(custom_rate_constants); + + double time_step = 150.0; // s + + auto result = solver.Solve(time_step, state); + + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); +} + + diff --git a/test/examples/test_Chapman.cpp b/test/examples/test_chapman.cpp similarity index 99% rename from test/examples/test_Chapman.cpp rename to test/examples/test_chapman.cpp index 1a2a633f9..2f873f8dd 100644 --- a/test/examples/test_Chapman.cpp +++ b/test/examples/test_chapman.cpp @@ -38,7 +38,7 @@ int main(const int argc, const char* argv[]) micm::SolverConfig solver_config; // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./examples/Chapman"); + micm::ConfigParseStatus status = solver_config.ReadAndParse("./examples/configs/chapman"); if (status != micm::ConfigParseStatus::Success) { diff --git a/test/examples/test_robertson.cpp b/test/examples/test_robertson.cpp new file mode 100644 index 000000000..91771c256 --- /dev/null +++ b/test/examples/test_robertson.cpp @@ -0,0 +1,60 @@ +// Copyright (C) 2023 National Center for Atmospheric Research, +// +// SPDX-License-Identifier: Apache-2.0 +// +// Test of the TS1 example mechanism (MZ327_TS1.2_20230307 in the Chemistry Cafe) +// +// This only tests that a solver can be built for this mechanism and that it +// can be run for a given set of initial conditions without solver errors. +// +// Comparisons with other chemistry solvers are included in +// https://github.com/NCAR/MUSICA-Performance-Comparison + +#include + +#include +#include + +TEST(Examples, robertson) +{ + micm::SolverConfig config; + std::string config_path = "./examples/configs/robertson"; + auto status = config.ReadAndParse(config_path); + EXPECT_EQ(status, micm::ConfigParseStatus::Success); + auto solver_params = config.GetSolverParams(); + micm::RosenbrockSolver<> solver{ solver_params.system_, + solver_params.processes_, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + micm::State state = solver.GetState(); + + const double temperature = 272.5; // K + const double pressure = 101253.3; // Pa + const double air_density = 1e6; // mol m-3 + + state.conditions_[0].temperature_ = temperature; + state.conditions_[0].pressure_ = pressure; + state.conditions_[0].air_density_ = air_density; + + std::unordered_map> initial_concentrations = { + { "A", { 1.0} }, // mol m-3 + { "B", { 0.0} }, // mol m-3 + { "C", { 0.0} }, // mol m-3 + }; + state.SetConcentrations(initial_concentrations); + + std::unordered_map> custom_rate_constants = { + {"PHOTO.r1", {0.04}}, + {"PHOTO.r2", {3e7}}, + {"PHOTO.r3", {1e4}} + }; + + state.SetCustomRateParameters(custom_rate_constants); + + double time_step = 200.0; // s + + auto result = solver.Solve(time_step, state); + + EXPECT_EQ(result.state_, (micm::SolverState::Converged)); +} + + diff --git a/test/tutorial/configs/carbon_bond_5/config.json b/test/tutorial/configs/carbon_bond_5/config.json deleted file mode 100644 index 03622f157..000000000 --- a/test/tutorial/configs/carbon_bond_5/config.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-files": ["species.json", "reactions.json"]} \ No newline at end of file diff --git a/test/tutorial/configs/carbon_bond_5/reactions.json b/test/tutorial/configs/carbon_bond_5/reactions.json deleted file mode 100644 index 617f98810..000000000 --- a/test/tutorial/configs/carbon_bond_5/reactions.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-data": [{"type": "MECHANISM", "name": "music box interactive configuration", "reactions": [{"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NO2", "reactants": {"NO2": {}}, "products": {"NO": {"yield": 1}, "O": {"yield": 1}, "irr__0": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6e-34, "Ea": 0, "B": -2.4, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "O2": {"qty": 1}, "M": {"qty": 1}}, "products": {"O3": {"yield": 1}, "M": {"yield": 1}, "irr__1": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__2": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__3": {"yield": 1}}}, {"type": "TROE", "k0_A": 2.5e-31, "k0_B": -1.8, "k0_C": 0, "kinf_A": 2.2e-11, "kinf_B": -0.7, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"O": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__4": {"yield": 1}}}, {"type": "TROE", "k0_A": 9e-32, "k0_B": -1.5, "k0_C": 0, "kinf_A": 3e-11, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"O": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__5": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.2e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO2": {"qty": 1}, "O3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__6": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "O3->O3P", "reactants": {"O3": {}}, "products": {"O": {"yield": 1}, "irr__7": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "O3->O1D", "reactants": {"O3": {}}, "products": {"O1D": {"yield": 1}, "irr__8": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O1D": {"qty": 1}, "M": {"qty": 1}}, "products": {"O": {"yield": 1}, "M": {"yield": 1}, "irr__9": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O1D": {"qty": 1}, "H2O": {"qty": 1}}, "products": {"OH": {"yield": 2}, "irr__10": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__11": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"OH": {"yield": 1}, "irr__12": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NO3->NO2", "reactants": {"NO3": {}}, "products": {"NO2": {"yield": 1}, "O": {"yield": 1}, "irr__13": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NO3->NO", "reactants": {"NO3": {}}, "products": {"NO": {"yield": 1}, "irr__14": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 2}, "irr__15": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.5e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NO": {"yield": 1}, "NO2": {"yield": 1}, "irr__16": {"yield": 1}}}, {"type": "TROE", "k0_A": 2e-30, "k0_B": -4.4, "k0_C": 0, "kinf_A": 1.4e-12, "kinf_B": -0.7, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"NO3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"N2O5": {"yield": 1}, "irr__17": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.5e-22, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"N2O5": {"qty": 1}, "H2O": {"qty": 1}}, "products": {"HNO3": {"yield": 2}, "irr__18": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-39, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"N2O5": {"qty": 1}, "H2O": {"qty": 2}}, "products": {"HNO3": {"yield": 2}, "irr__19": {"yield": 1}}}, {"type": "TROE", "k0_A": 0.001, "k0_B": -3.5, "k0_C": -11000, "kinf_A": 970000000000000, "kinf_B": 0.1, "kinf_C": -11080, "Fc": 0.45, "N": 1, "reactants": {"N2O5": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "NO2": {"yield": 1}, "irr__20": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.3e-39, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO": {"qty": 2}, "O2": {"qty": 1}}, "products": {"NO2": {"yield": 2}, "irr__21": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5e-40, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO": {"qty": 1}, "NO2": {"qty": 1}, "H2O": {"qty": 1}}, "products": {"HONO": {"yield": 2}, "irr__22": {"yield": 1}}}, {"type": "TROE", "k0_A": 7e-31, "k0_B": -2.6, "k0_C": 0, "kinf_A": 3.6e-11, "kinf_B": -0.1, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"NO": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HONO": {"yield": 1}, "irr__23": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "HONO", "reactants": {"HONO": {}}, "products": {"NO": {"yield": 1}, "OH": {"yield": 1}, "irr__24": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "HONO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__25": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-20, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HONO": {"qty": 2}}, "products": {"NO": {"yield": 1}, "NO2": {"yield": 1}, "irr__26": {"yield": 1}}}, {"type": "TROE", "k0_A": 2e-30, "k0_B": -3, "k0_C": 0, "kinf_A": 2.5e-11, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"NO2": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "irr__27": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.4e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "HNO3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__28": {"yield": 1}}}, {"type": "TROE", "k0_A": 6.5e-34, "k0_B": 0, "k0_C": 1335, "kinf_A": 2.7e-17, "kinf_B": 0, "kinf_C": 2199, "Fc": 1, "N": 1, "reactants": {"OH": {"qty": 1}, "HNO3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__29": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"OH": {"yield": 1}, "NO2": {"yield": 1}, "irr__30": {"yield": 1}}}, {"type": "TROE", "k0_A": 1.8e-31, "k0_B": -3.2, "k0_C": 0, "kinf_A": 4.7e-12, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"HO2": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"PNA": {"yield": 1}, "irr__31": {"yield": 1}}}, {"type": "TROE", "k0_A": 4.1e-05, "k0_B": 0, "k0_C": -10650, "kinf_A": 4800000000000000, "kinf_B": 0, "kinf_C": -11170, "Fc": 0.6, "N": 1, "reactants": {"PNA": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__32": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "PNA": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__33": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}}, "products": {"H2O2": {"yield": 1}, "irr__34": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.7e-33, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}, "M": {"qty": 1}}, "products": {"H2O2": {"yield": 1}, "irr__35": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.22e-34, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}, "H2O": {"qty": 1}}, "products": {"H2O2": {"yield": 1}, "irr__36": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.38e-54, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 2}, "H2O": {"qty": 1}, "M": {"qty": 1}}, "products": {"H2O2": {"yield": 1}, "irr__37": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "H2O2", "reactants": {"H2O2": {}}, "products": {"OH": {"yield": 2}, "irr__38": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "H2O2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__39": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.1e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O1D": {"qty": 1}, "H2": {"qty": 1}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "irr__40": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "H2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__41": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "O": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__42": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.2e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 2}}, "products": {"O": {"yield": 1}, "irr__43": {"yield": 1}}}, {"type": "TROE", "k0_A": 6.9e-31, "k0_B": -1, "k0_C": 0, "kinf_A": 2.6e-11, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"OH": {"qty": 2}}, "products": {"H2O2": {"yield": 1}, "irr__44": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"irr__45": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HO2": {"qty": 1}, "O": {"qty": 1}}, "products": {"OH": {"yield": 1}, "irr__46": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.4e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"H2O2": {"qty": 1}, "O": {"qty": 1}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "irr__47": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "O": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__48": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__49": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "irr__50": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-17, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "O3": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__51": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 2}}, "products": {"NO2": {"yield": 2}, "irr__52": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PNA", "reactants": {"PNA": {}}, "products": {"HO2": {"yield": 0.61}, "NO2": {"yield": 0.61}, "OH": {"yield": 0.39}, "NO3": {"yield": 0.39}, "irr__53": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "HNO3", "reactants": {"HNO3": {}}, "products": {"OH": {"yield": 1}, "NO2": {"yield": 1}, "irr__54": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "N2O5", "reactants": {"N2O5": {}}, "products": {"NO2": {"yield": 1}, "NO3": {"yield": 1}, "irr__55": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "irr__56": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2N": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NTR": {"yield": 1}, "irr__57": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"ROOH": {"yield": 1}, "irr__58": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2N": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"ROOH": {"yield": 1}, "irr__59": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.8e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 2}}, "products": {"irr__60": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.8e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2N": {"qty": 2}}, "products": {"irr__61": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.8e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XO2": {"qty": 1}, "XO2N": {"qty": 1}}, "products": {"irr__62": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NTR": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "HO2": {"yield": 1}, "FORM": {"yield": 0.33}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.33}, "PAR": {"yield": -0.66}, "irr__63": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "NTR", "reactants": {"NTR": {}}, "products": {"NO2": {"yield": 1}, "HO2": {"yield": 1}, "FORM": {"yield": 0.33}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.33}, "PAR": {"yield": -0.66}, "irr__64": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.01e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROOH": {"qty": 1}, "OH": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "ALD2": {"yield": 0.5}, "ALDX": {"yield": 0.5}, "irr__65": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "ROOH", "reactants": {"ROOH": {}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "ALD2": {"yield": 0.5}, "ALDX": {"yield": 0.5}, "irr__66": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.44e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CO": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__67": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.43e-33, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CO": {"qty": 1}, "M": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__68": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.45e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CH4": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "irr__69": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.8e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__70": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.1e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"MEPX": {"yield": 1}, "irr__71": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9.5e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEO2": {"qty": 2}}, "products": {"FORM": {"yield": 1.37}, "HO2": {"yield": 0.74}, "MEOH": {"yield": 0.63}, "irr__72": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.8e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEPX": {"qty": 1}, "OH": {"qty": 1}}, "products": {"MEO2": {"yield": 0.7}, "XO2": {"yield": 0.3}, "HO2": {"yield": 0.3}, "irr__73": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "MEPX", "reactants": {"MEPX": {}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "OH": {"yield": 1}, "irr__74": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"MEOH": {"qty": 1}, "OH": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "irr__75": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__76": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "FORM->HO2", "reactants": {"FORM": {}}, "products": {"HO2": {"yield": 2}, "CO": {"yield": 1}, "irr__77": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "FORM->CO", "reactants": {"FORM": {}}, "products": {"CO": {"yield": 1}, "irr__78": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.4e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "O": {"qty": 1}}, "products": {"OH": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__79": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.8e-16, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"HNO3": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__80": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9.7e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FORM": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HCO3": {"yield": 1}, "irr__81": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2400000000000, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HCO3": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1}, "irr__82": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HCO3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"FACD": {"yield": 1}, "NO2": {"yield": 1}, "HO2": {"yield": 1}, "irr__83": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"HCO3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"MEPX": {"yield": 1}, "irr__84": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"FACD": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__85": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALD2": {"qty": 1}, "O": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "OH": {"yield": 1}, "irr__86": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALD2": {"qty": 1}, "OH": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "irr__87": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.4e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALD2": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "HNO3": {"yield": 1}, "irr__88": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "ALD2", "reactants": {"ALD2": {}}, "products": {"MEO2": {"yield": 1}, "CO": {"yield": 1}, "HO2": {"yield": 1}, "irr__89": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.1e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__90": {"yield": 1}}}, {"type": "TROE", "k0_A": 2.7e-28, "k0_B": -7.1, "k0_C": 0, "kinf_A": 1.2e-11, "kinf_B": -0.9, "kinf_C": 0, "Fc": 0.3, "N": 1, "reactants": {"C2O3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"PAN": {"yield": 1}, "irr__91": {"yield": 1}}}, {"type": "TROE", "k0_A": 0.0049, "k0_B": 0, "k0_C": -12100, "kinf_A": 54000000000000000, "kinf_B": 0, "kinf_C": -13830, "Fc": 0.3, "N": 1, "reactants": {"PAN": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "NO2": {"yield": 1}, "irr__92": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PAN", "reactants": {"PAN": {}}, "products": {"C2O3": {"yield": 1}, "NO2": {"yield": 1}, "irr__93": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"PACD": {"yield": 0.8}, "AACD": {"yield": 0.2}, "O3": {"yield": 0.2}, "irr__94": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "MEO2": {"qty": 1}}, "products": {"MEO2": {"yield": 0.9}, "HO2": {"yield": 0.9}, "FORM": {"yield": 1}, "AACD": {"yield": 0.1}, "irr__95": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 1}, "XO2": {"qty": 1}}, "products": {"MEO2": {"yield": 0.9}, "AACD": {"yield": 0.1}, "irr__96": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"C2O3": {"qty": 2}}, "products": {"MEO2": {"yield": 2}, "irr__97": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"PACD": {"qty": 1}, "OH": {"qty": 1}}, "products": {"C2O3": {"yield": 1}, "irr__98": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PACD", "reactants": {"PACD": {}}, "products": {"MEO2": {"yield": 1}, "OH": {"yield": 1}, "irr__99": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"AACD": {"qty": 1}, "OH": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "irr__100": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALDX": {"qty": 1}, "O": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "OH": {"yield": 1}, "irr__101": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.1e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALDX": {"qty": 1}, "OH": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "irr__102": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.5e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ALDX": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "HNO3": {"yield": 1}, "irr__103": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "ALDX", "reactants": {"ALDX": {}}, "products": {"MEO2": {"yield": 1}, "CO": {"yield": 1}, "HO2": {"yield": 1}, "irr__104": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "NO": {"qty": 1}}, "products": {"ALD2": {"yield": 1}, "NO2": {"yield": 1}, "HO2": {"yield": 1}, "XO2": {"yield": 1}, "irr__105": {"yield": 1}}}, {"type": "TROE", "k0_A": 2.7e-28, "k0_B": -7.1, "k0_C": 0, "kinf_A": 1.2e-11, "kinf_B": -0.9, "kinf_C": 0, "Fc": 0.3, "N": 1, "reactants": {"CXO3": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"PANX": {"yield": 1}, "irr__106": {"yield": 1}}}, {"type": "TROE", "k0_A": 0.0049, "k0_B": 0, "k0_C": -12100, "kinf_A": 54000000000000000, "kinf_B": 0, "kinf_C": -13830, "Fc": 0.3, "N": 1, "reactants": {"PANX": {"qty": 1}}, "products": {"CXO3": {"yield": 1}, "NO2": {"yield": 1}, "irr__107": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "PANX", "reactants": {"PANX": {}}, "products": {"CXO3": {"yield": 1}, "NO2": {"yield": 1}, "irr__108": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"PANX": {"qty": 1}, "OH": {"qty": 1}}, "products": {"ALD2": {"yield": 1}, "NO2": {"yield": 1}, "irr__109": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.3e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"PACD": {"yield": 0.8}, "AACD": {"yield": 0.2}, "O3": {"yield": 0.2}, "irr__110": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "MEO2": {"qty": 1}}, "products": {"ALD2": {"yield": 0.9}, "XO2": {"yield": 0.9}, "HO2": {"yield": 1}, "AACD": {"yield": 0.1}, "FORM": {"yield": 0.1}, "irr__111": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.4e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "XO2": {"qty": 1}}, "products": {"ALD2": {"yield": 0.9}, "AACD": {"yield": 0.1}, "irr__112": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 2}}, "products": {"ALD2": {"yield": 2}, "XO2": {"yield": 2}, "HO2": {"yield": 2}, "irr__113": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CXO3": {"qty": 1}, "C2O3": {"qty": 1}}, "products": {"MEO2": {"yield": 1}, "XO2": {"yield": 1}, "HO2": {"yield": 1}, "ALD2": {"yield": 1}, "irr__114": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.1e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"PAR": {"qty": 1}, "OH": {"qty": 1}}, "products": {"XO2": {"yield": 0.87}, "XO2N": {"yield": 0.13}, "HO2": {"yield": 0.11}, "ALD2": {"yield": 0.06}, "PAR": {"yield": -0.11}, "ROR": {"yield": 0.76}, "ALDX": {"yield": 0.05}, "irr__115": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1000000000000000, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROR": {"qty": 1}}, "products": {"XO2": {"yield": 0.96}, "ALD2": {"yield": 0.6}, "HO2": {"yield": 0.94}, "PAR": {"yield": -2.1}, "XO2N": {"yield": 0.04}, "ROR": {"yield": 0.02}, "ALDX": {"yield": 0.5}, "irr__116": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1600, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROR": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__117": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"ROR": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NTR": {"yield": 1}, "irr__118": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"ALD2": {"yield": 0.2}, "ALDX": {"yield": 0.3}, "HO2": {"yield": 0.3}, "XO2": {"yield": 0.2}, "CO": {"yield": 0.2}, "FORM": {"yield": 0.2}, "XO2N": {"yield": 0.01}, "PAR": {"yield": 0.2}, "OH": {"yield": 0.1}, "irr__119": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"FORM": {"yield": 0.8}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.62}, "XO2": {"yield": 0.8}, "HO2": {"yield": 0.95}, "PAR": {"yield": -0.7}, "irr__120": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.5e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"ALD2": {"yield": 0.18}, "FORM": {"yield": 0.74}, "ALDX": {"yield": 0.32}, "XO2": {"yield": 0.22}, "OH": {"yield": 0.1}, "CO": {"yield": 0.33}, "HO2": {"yield": 0.44}, "PAR": {"yield": -1}, "irr__121": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "FORM": {"yield": 1}, "XO2": {"yield": 0.91}, "XO2N": {"yield": 0.09}, "ALDX": {"yield": 0.56}, "ALD2": {"yield": 0.35}, "PAR": {"yield": -1}, "irr__122": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.04e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "HO2": {"yield": 1.7}, "CO": {"yield": 1}, "XO2": {"yield": 0.7}, "OH": {"yield": 0.3}, "irr__123": {"yield": 1}}}, {"type": "TROE", "k0_A": 1e-28, "k0_B": -0.8, "k0_C": 0, "kinf_A": 8.8e-12, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"OH": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "FORM": {"yield": 1.56}, "ALDX": {"yield": 0.22}, "HO2": {"yield": 1}, "irr__124": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.2e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"FORM": {"yield": 1}, "CO": {"yield": 0.63}, "HO2": {"yield": 0.13}, "OH": {"yield": 0.13}, "FACD": {"yield": 0.37}, "irr__125": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.3e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"NO2": {"yield": 1}, "XO2": {"yield": 1}, "FORM": {"yield": 2}, "irr__126": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "O": {"qty": 1}}, "products": {"ALD2": {"yield": 1.24}, "ALDX": {"yield": 0.66}, "HO2": {"yield": 0.1}, "XO2": {"yield": 0.1}, "CO": {"yield": 0.1}, "PAR": {"yield": 0.1}, "irr__127": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "OH": {"qty": 1}}, "products": {"ALD2": {"yield": 1.3}, "ALDX": {"yield": 0.7}, "HO2": {"yield": 1}, "XO2": {"yield": 1}, "irr__128": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.4e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "O3": {"qty": 1}}, "products": {"ALD2": {"yield": 0.65}, "ALDX": {"yield": 0.35}, "FORM": {"yield": 0.25}, "CO": {"yield": 0.25}, "O": {"yield": 0.5}, "OH": {"yield": 0.5}, "HO2": {"yield": 0.5}, "irr__129": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 9.6e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"IOLE": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"ALD2": {"yield": 1.18}, "ALDX": {"yield": 0.64}, "HO2": {"yield": 1}, "NO2": {"yield": 1}, "irr__130": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TOL": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 0.44}, "XO2": {"yield": 0.08}, "CRES": {"yield": 0.36}, "TO2": {"yield": 0.56}, "TOLRO2": {"yield": 0.765}, "irr__131": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.1e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO2": {"yield": 0.9}, "HO2": {"yield": 0.9}, "OPEN": {"yield": 0.9}, "NTR": {"yield": 0.1}, "irr__132": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.2, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TO2": {"qty": 1}}, "products": {"CRES": {"yield": 1}, "HO2": {"yield": 1}, "irr__133": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.1e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "CRES": {"qty": 1}}, "products": {"CRO": {"yield": 0.4}, "XO2": {"yield": 0.6}, "HO2": {"yield": 0.6}, "OPEN": {"yield": 0.3}, "irr__134": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CRES": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"CRO": {"yield": 1}, "HNO3": {"yield": 1}, "irr__135": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.4e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CRO": {"qty": 1}, "NO2": {"qty": 1}}, "products": {"NTR": {"yield": 1}, "irr__136": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.5e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CRO": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"CRES": {"yield": 1}, "irr__137": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 9, "MUSICA name": "OPEN", "reactants": {"OPEN": {}}, "products": {"C2O3": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__138": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OPEN": {"qty": 1}, "OH": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "CO": {"yield": 2}, "HO2": {"yield": 2}, "C2O3": {"yield": 1}, "FORM": {"yield": 1}, "irr__139": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.4e-17, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OPEN": {"qty": 1}, "O3": {"qty": 1}}, "products": {"ALDX": {"yield": 0.03}, "C2O3": {"yield": 0.62}, "FORM": {"yield": 0.7}, "XO2": {"yield": 0.03}, "CO": {"yield": 0.69}, "OH": {"yield": 0.08}, "HO2": {"yield": 0.76}, "MGLY": {"yield": 0.2}, "irr__140": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.7e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "XYL": {"qty": 1}}, "products": {"HO2": {"yield": 0.7}, "XO2": {"yield": 0.5}, "CRES": {"yield": 0.2}, "MGLY": {"yield": 0.8}, "PAR": {"yield": 1.1}, "TO2": {"yield": 0.3}, "XYLRO2": {"yield": 0.804}, "irr__141": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.8e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "MGLY": {"qty": 1}}, "products": {"XO2": {"yield": 1}, "C2O3": {"yield": 1}, "irr__142": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "MGLY", "reactants": {"MGLY": {}}, "products": {"C2O3": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__143": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.6e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.75}, "FORM": {"yield": 0.5}, "XO2": {"yield": 0.25}, "HO2": {"yield": 0.25}, "CXO3": {"yield": 0.25}, "PAR": {"yield": 0.25}, "irr__144": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.54e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.912}, "FORM": {"yield": 0.629}, "XO2": {"yield": 0.991}, "HO2": {"yield": 0.912}, "XO2N": {"yield": 0.088}, "irr__145": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.86e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.65}, "FORM": {"yield": 0.6}, "XO2": {"yield": 0.2}, "HO2": {"yield": 0.066}, "OH": {"yield": 0.266}, "CXO3": {"yield": 0.2}, "ALDX": {"yield": 0.15}, "PAR": {"yield": 0.35}, "CO": {"yield": 0.066}, "irr__146": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.03e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.2}, "NTR": {"yield": 0.8}, "XO2": {"yield": 1}, "HO2": {"yield": 0.8}, "NO2": {"yield": 0.2}, "ALDX": {"yield": 0.8}, "PAR": {"yield": 2.4}, "irr__147": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.36e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ISPD": {"qty": 1}}, "products": {"PAR": {"yield": 1.565}, "FORM": {"yield": 0.167}, "XO2": {"yield": 0.713}, "HO2": {"yield": 0.503}, "CO": {"yield": 0.334}, "MGLY": {"yield": 0.168}, "ALD2": {"yield": 0.252}, "C2O3": {"yield": 0.21}, "CXO3": {"yield": 0.25}, "ALDX": {"yield": 0.12}, "irr__148": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.1e-18, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"O3": {"qty": 1}, "ISPD": {"qty": 1}}, "products": {"C2O3": {"yield": 0.114}, "FORM": {"yield": 0.15}, "MGLY": {"yield": 0.85}, "HO2": {"yield": 0.154}, "OH": {"yield": 0.268}, "XO2": {"yield": 0.064}, "ALD2": {"yield": 0.02}, "PAR": {"yield": 0.36}, "CO": {"yield": 0.225}, "irr__149": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO3": {"qty": 1}, "ISPD": {"qty": 1}}, "products": {"ALDX": {"yield": 0.357}, "FORM": {"yield": 0.282}, "PAR": {"yield": 1.282}, "HO2": {"yield": 0.925}, "CO": {"yield": 0.643}, "NTR": {"yield": 0.85}, "CXO3": {"yield": 0.075}, "XO2": {"yield": 0.075}, "HNO3": {"yield": 0.15}, "irr__150": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 0.0036, "MUSICA name": "ISPD", "reactants": {"ISPD": {}}, "products": {"CO": {"yield": 0.333}, "ALD2": {"yield": 0.067}, "FORM": {"yield": 0.9}, "PAR": {"yield": 0.832}, "HO2": {"yield": 1.033}, "XO2": {"yield": 0.7}, "C2O3": {"yield": 0.967}, "irr__151": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.6e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "O": {"qty": 1}}, "products": {"ALDX": {"yield": 0.15}, "PAR": {"yield": 5.12}, "irr__152": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "OH": {"qty": 1}}, "products": {"HO2": {"yield": 0.75}, "XO2": {"yield": 1.25}, "XO2N": {"yield": 0.25}, "FORM": {"yield": 0.28}, "PAR": {"yield": 1.66}, "ALDX": {"yield": 0.47}, "irr__153": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.2e-15, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "O3": {"qty": 1}}, "products": {"OH": {"yield": 0.57}, "HO2": {"yield": 0.07}, "XO2": {"yield": 0.76}, "XO2N": {"yield": 0.18}, "FORM": {"yield": 0.24}, "CO": {"yield": 0.001}, "PAR": {"yield": 7}, "ALDX": {"yield": 0.21}, "CXO3": {"yield": 0.39}, "irr__154": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TERP": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"NO2": {"yield": 0.47}, "HO2": {"yield": 0.28}, "XO2": {"yield": 1.03}, "XO2N": {"yield": 0.25}, "ALDX": {"yield": 0.47}, "NTR": {"yield": 0.53}, "irr__155": {"yield": 1}}}, {"type": "TROE", "k0_A": 3e-31, "k0_B": -3.3, "k0_C": 0, "kinf_A": 1.5e-12, "kinf_B": 0, "kinf_C": 0, "Fc": 0.6, "N": 1, "reactants": {"SO2": {"qty": 1}, "OH": {"qty": 1}}, "products": {"SULF": {"yield": 1}, "HO2": {"yield": 1}, "irr__156": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.9e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ETOH": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "ALD2": {"yield": 0.9}, "ALDX": {"yield": 0.05}, "FORM": {"yield": 0.1}, "XO2": {"yield": 0.1}, "irr__157": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "ETHA": {"qty": 1}}, "products": {"ALD2": {"yield": 0.991}, "XO2": {"yield": 0.991}, "XO2N": {"yield": 0.009}, "HO2": {"yield": 1}, "irr__158": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.5e-19, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"NO2": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"ISPD": {"yield": 0.2}, "NTR": {"yield": 0.8}, "XO2": {"yield": 1}, "HO2": {"yield": 0.8}, "NO": {"yield": 0.2}, "ALDX": {"yield": 0.8}, "PAR": {"yield": 2.4}, "irr__159": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "CL2", "reactants": {"CL2": {}}, "products": {"CL": {"yield": 2}, "irr__160": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "O3": {"qty": 1}}, "products": {"CLO": {"yield": 1}, "irr__161": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.63e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CLO": {"qty": 2}}, "products": {"CL2": {"yield": 0.3}, "CL": {"yield": 1.4}, "irr__162": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.4e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CLO": {"qty": 1}, "NO": {"qty": 1}}, "products": {"CL": {"yield": 1}, "NO2": {"yield": 1}, "irr__163": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CLO": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HOCL": {"yield": 1}, "irr__164": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"OH": {"qty": 1}, "FMCL": {"qty": 1}}, "products": {"CL": {"yield": 1}, "CO": {"yield": 1}, "irr__165": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.6e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "CH4": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "MEO2": {"yield": 1}, "irr__166": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "PAR": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "XO2": {"yield": 0.87}, "XO2N": {"yield": 0.13}, "HO2": {"yield": 0.11}, "ALD2": {"yield": 0.06}, "PAR": {"yield": -0.11}, "ROR": {"yield": 0.76}, "ALDX": {"yield": 0.05}, "irr__167": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.3e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ETHA": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "ALD2": {"yield": 0.991}, "XO2": {"yield": 0.991}, "XO2N": {"yield": 0.009}, "HO2": {"yield": 1}, "irr__168": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.07e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ETH": {"qty": 1}}, "products": {"FMCL": {"yield": 1}, "XO2": {"yield": 2}, "HO2": {"yield": 1}, "FORM": {"yield": 1}, "irr__169": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.5e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "OLE": {"qty": 1}}, "products": {"FMCL": {"yield": 1}, "ALD2": {"yield": 0.33}, "ALDX": {"yield": 0.67}, "XO2": {"yield": 2}, "HO2": {"yield": 1}, "PAR": {"yield": -1}, "irr__170": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 3.5e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "IOLE": {"qty": 1}}, "products": {"HCL": {"yield": 0.3}, "FMCL": {"yield": 0.7}, "ALD2": {"yield": 0.45}, "ALDX": {"yield": 0.55}, "OLE": {"yield": 0.3}, "PAR": {"yield": 0.3}, "XO2": {"yield": 1.7}, "HO2": {"yield": 1}, "irr__171": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 4.3e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ISOP": {"qty": 1}}, "products": {"HCL": {"yield": 0.15}, "XO2": {"yield": 1}, "HO2": {"yield": 1}, "FMCL": {"yield": 0.85}, "ISPD": {"yield": 1}, "irr__172": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "FORM": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "HO2": {"yield": 1}, "CO": {"yield": 1}, "irr__173": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 7.9e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ALD2": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "C2O3": {"yield": 1}, "irr__174": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.3e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ALDX": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "CXO3": {"yield": 1}, "irr__175": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 5.5e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "MEOH": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "HO2": {"yield": 1}, "FORM": {"yield": 1}, "irr__176": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 8.2e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"CL": {"qty": 1}, "ETOH": {"qty": 1}}, "products": {"HCL": {"yield": 1}, "HO2": {"yield": 1}, "ALD2": {"yield": 1}, "irr__177": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 6.58e-13, "Ea": 0, "B": 1.16, "D": 300, "E": 0, "reactants": {"HCL": {"qty": 1}, "OH": {"qty": 1}}, "products": {"CL": {"yield": 1}, "irr__178": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TOLRO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__179": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"TOLRO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__180": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XYLRO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__181": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"XYLRO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__182": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.47e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"BENZENE": {"qty": 1}, "OH": {"qty": 1}}, "products": {"OH": {"yield": 1}, "BENZRO2": {"yield": 0.764}, "irr__183": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 2.7e-12, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"BENZRO2": {"qty": 1}, "NO": {"qty": 1}}, "products": {"NO": {"yield": 1}, "irr__184": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-13, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"BENZRO2": {"qty": 1}, "HO2": {"qty": 1}}, "products": {"HO2": {"yield": 1}, "irr__185": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.16e-14, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"SESQ": {"qty": 1}, "O3": {"qty": 1}}, "products": {"O3": {"yield": 1}, "irr__186": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.97e-10, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"SESQ": {"qty": 1}, "OH": {"qty": 1}}, "products": {"OH": {"yield": 1}, "irr__187": {"yield": 1}}}, {"type": "ARRHENIUS", "A": 1.9e-11, "Ea": 0, "B": 0, "D": 300, "E": 0, "reactants": {"SESQ": {"qty": 1}, "NO3": {"qty": 1}}, "products": {"NO3": {"yield": 1}, "irr__188": {"yield": 1}}}, {"type": "PHOTOLYSIS", "scaling_factor": 1, "MUSICA name": "O2", "reactants": {"O2": {}}, "products": {"O": {"yield": 2}, "irr__189": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "NO", "species": "NO", "products": {"irr__190": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "NO2", "species": "NO2", "products": {"irr__191": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "CO", "species": "CO", "products": {"irr__192": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "SO2", "species": "SO2", "products": {"irr__193": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "FORM", "species": "FORM", "products": {"irr__194": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "MEOH", "species": "MEOH", "products": {"irr__195": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "ALD2", "species": "ALD2", "products": {"irr__196": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "PAR", "species": "PAR", "products": {"irr__197": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "ETH", "species": "ETH", "products": {"irr__198": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "OLE", "species": "OLE", "products": {"irr__199": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "IOLE", "species": "IOLE", "products": {"irr__200": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "TOL", "species": "TOL", "products": {"irr__201": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "XYL", "species": "XYL", "products": {"irr__202": {"yield": 1}}}, {"type": "EMISSION", "scaling factor": 1, "MUSICA name": "ISOP", "species": "ISOP", "products": {"irr__203": {"yield": 1}}}]}]} \ No newline at end of file diff --git a/test/tutorial/configs/carbon_bond_5/species.json b/test/tutorial/configs/carbon_bond_5/species.json deleted file mode 100644 index 8a101b2be..000000000 --- a/test/tutorial/configs/carbon_bond_5/species.json +++ /dev/null @@ -1 +0,0 @@ -{"camp-data": [{"name": "AACD", "type": "CHEM_SPEC"}, {"name": "ALD2", "type": "CHEM_SPEC"}, {"name": "ALDX", "type": "CHEM_SPEC"}, {"name": "BENZENE", "type": "CHEM_SPEC"}, {"name": "BENZRO2", "type": "CHEM_SPEC"}, {"name": "C2O3", "type": "CHEM_SPEC"}, {"name": "CH4", "type": "CHEM_SPEC"}, {"name": "CL", "type": "CHEM_SPEC"}, {"name": "CL2", "type": "CHEM_SPEC"}, {"name": "CLO", "type": "CHEM_SPEC"}, {"name": "CO", "type": "CHEM_SPEC"}, {"name": "CRES", "type": "CHEM_SPEC"}, {"name": "CRO", "type": "CHEM_SPEC"}, {"name": "CXO3", "type": "CHEM_SPEC"}, {"name": "ETH", "type": "CHEM_SPEC"}, {"name": "ETHA", "type": "CHEM_SPEC"}, {"name": "ETOH", "type": "CHEM_SPEC"}, {"name": "FACD", "type": "CHEM_SPEC"}, {"name": "FMCL", "type": "CHEM_SPEC"}, {"name": "FORM", "type": "CHEM_SPEC"}, {"name": "H2", "type": "CHEM_SPEC"}, {"name": "H2O", "type": "CHEM_SPEC"}, {"name": "H2O2", "type": "CHEM_SPEC"}, {"name": "HCL", "type": "CHEM_SPEC"}, {"name": "HCO3", "type": "CHEM_SPEC"}, {"name": "HNO3", "type": "CHEM_SPEC"}, {"name": "HO2", "type": "CHEM_SPEC"}, {"name": "HOCL", "type": "CHEM_SPEC"}, {"name": "HONO", "type": "CHEM_SPEC"}, {"name": "IOLE", "type": "CHEM_SPEC"}, {"name": "ISOP", "type": "CHEM_SPEC"}, {"name": "ISPD", "type": "CHEM_SPEC"}, {"name": "M", "type": "CHEM_SPEC"}, {"name": "MEO2", "type": "CHEM_SPEC"}, {"name": "MEOH", "type": "CHEM_SPEC"}, {"name": "MEPX", "type": "CHEM_SPEC"}, {"name": "MGLY", "type": "CHEM_SPEC"}, {"name": "N2O5", "type": "CHEM_SPEC"}, {"name": "NO", "type": "CHEM_SPEC"}, {"name": "NO2", "type": "CHEM_SPEC"}, {"name": "NO3", "type": "CHEM_SPEC"}, {"name": "NTR", "type": "CHEM_SPEC"}, {"name": "O", "type": "CHEM_SPEC"}, {"name": "O1D", "type": "CHEM_SPEC"}, {"name": "O3", "type": "CHEM_SPEC"}, {"name": "OH", "type": "CHEM_SPEC"}, {"name": "O2", "type": "CHEM_SPEC"}, {"name": "OLE", "type": "CHEM_SPEC"}, {"name": "OPEN", "type": "CHEM_SPEC"}, {"name": "PACD", "type": "CHEM_SPEC"}, {"name": "PAN", "type": "CHEM_SPEC"}, {"name": "PANX", "type": "CHEM_SPEC"}, {"name": "PAR", "type": "CHEM_SPEC"}, {"name": "PNA", "type": "CHEM_SPEC"}, {"name": "ROOH", "type": "CHEM_SPEC"}, {"name": "ROR", "type": "CHEM_SPEC"}, {"name": "SESQ", "type": "CHEM_SPEC"}, {"name": "SO2", "type": "CHEM_SPEC"}, {"name": "SULF", "type": "CHEM_SPEC"}, {"name": "TERP", "type": "CHEM_SPEC"}, {"name": "TO2", "type": "CHEM_SPEC"}, {"name": "TOL", "type": "CHEM_SPEC"}, {"name": "TOLRO2", "type": "CHEM_SPEC"}, {"name": "XO2", "type": "CHEM_SPEC"}, {"name": "XO2N", "type": "CHEM_SPEC"}, {"name": "XYL", "type": "CHEM_SPEC"}, {"name": "XYLRO2", "type": "CHEM_SPEC"}, {"name": "irr__0", "type": "CHEM_SPEC"}, {"name": "irr__1", "type": "CHEM_SPEC"}, {"name": "irr__2", "type": "CHEM_SPEC"}, {"name": "irr__3", "type": "CHEM_SPEC"}, {"name": "irr__4", "type": "CHEM_SPEC"}, {"name": "irr__5", "type": "CHEM_SPEC"}, {"name": "irr__6", "type": "CHEM_SPEC"}, {"name": "irr__7", "type": "CHEM_SPEC"}, {"name": "irr__8", "type": "CHEM_SPEC"}, {"name": "irr__9", "type": "CHEM_SPEC"}, {"name": "irr__10", "type": "CHEM_SPEC"}, {"name": "irr__11", "type": "CHEM_SPEC"}, {"name": "irr__12", "type": "CHEM_SPEC"}, {"name": "irr__13", "type": "CHEM_SPEC"}, {"name": "irr__14", "type": "CHEM_SPEC"}, {"name": "irr__15", "type": "CHEM_SPEC"}, {"name": "irr__16", "type": "CHEM_SPEC"}, {"name": "irr__17", "type": "CHEM_SPEC"}, {"name": "irr__18", "type": "CHEM_SPEC"}, {"name": "irr__19", "type": "CHEM_SPEC"}, {"name": "irr__20", "type": "CHEM_SPEC"}, {"name": "irr__21", "type": "CHEM_SPEC"}, {"name": "irr__22", "type": "CHEM_SPEC"}, {"name": "irr__23", "type": "CHEM_SPEC"}, {"name": "irr__24", "type": "CHEM_SPEC"}, {"name": "irr__25", "type": "CHEM_SPEC"}, {"name": "irr__26", "type": "CHEM_SPEC"}, {"name": "irr__27", "type": "CHEM_SPEC"}, {"name": "irr__28", "type": "CHEM_SPEC"}, {"name": "irr__29", "type": "CHEM_SPEC"}, {"name": "irr__30", "type": "CHEM_SPEC"}, {"name": "irr__31", "type": "CHEM_SPEC"}, {"name": "irr__32", "type": "CHEM_SPEC"}, {"name": "irr__33", "type": "CHEM_SPEC"}, {"name": "irr__34", "type": "CHEM_SPEC"}, {"name": "irr__35", "type": "CHEM_SPEC"}, {"name": "irr__36", "type": "CHEM_SPEC"}, {"name": "irr__37", "type": "CHEM_SPEC"}, {"name": "irr__38", "type": "CHEM_SPEC"}, {"name": "irr__39", "type": "CHEM_SPEC"}, {"name": "irr__40", "type": "CHEM_SPEC"}, {"name": "irr__41", "type": "CHEM_SPEC"}, {"name": "irr__42", "type": "CHEM_SPEC"}, {"name": "irr__43", "type": "CHEM_SPEC"}, {"name": "irr__44", "type": "CHEM_SPEC"}, {"name": "irr__45", "type": "CHEM_SPEC"}, {"name": "irr__46", "type": "CHEM_SPEC"}, {"name": "irr__47", "type": "CHEM_SPEC"}, {"name": "irr__48", "type": "CHEM_SPEC"}, {"name": "irr__49", "type": "CHEM_SPEC"}, {"name": "irr__50", "type": "CHEM_SPEC"}, {"name": "irr__51", "type": "CHEM_SPEC"}, {"name": "irr__52", "type": "CHEM_SPEC"}, {"name": "irr__53", "type": "CHEM_SPEC"}, {"name": "irr__54", "type": "CHEM_SPEC"}, {"name": "irr__55", "type": "CHEM_SPEC"}, {"name": "irr__56", "type": "CHEM_SPEC"}, {"name": "irr__57", "type": "CHEM_SPEC"}, {"name": "irr__58", "type": "CHEM_SPEC"}, {"name": "irr__59", "type": "CHEM_SPEC"}, {"name": "irr__60", "type": "CHEM_SPEC"}, {"name": "irr__61", "type": "CHEM_SPEC"}, {"name": "irr__62", "type": "CHEM_SPEC"}, {"name": "irr__63", "type": "CHEM_SPEC"}, {"name": "irr__64", "type": "CHEM_SPEC"}, {"name": "irr__65", "type": "CHEM_SPEC"}, {"name": "irr__66", "type": "CHEM_SPEC"}, {"name": "irr__67", "type": "CHEM_SPEC"}, {"name": "irr__68", "type": "CHEM_SPEC"}, {"name": "irr__69", "type": "CHEM_SPEC"}, {"name": "irr__70", "type": "CHEM_SPEC"}, {"name": "irr__71", "type": "CHEM_SPEC"}, {"name": "irr__72", "type": "CHEM_SPEC"}, {"name": "irr__73", "type": "CHEM_SPEC"}, {"name": "irr__74", "type": "CHEM_SPEC"}, {"name": "irr__75", "type": "CHEM_SPEC"}, {"name": "irr__76", "type": "CHEM_SPEC"}, {"name": "irr__77", "type": "CHEM_SPEC"}, {"name": "irr__78", "type": "CHEM_SPEC"}, {"name": "irr__79", "type": "CHEM_SPEC"}, {"name": "irr__80", "type": "CHEM_SPEC"}, {"name": "irr__81", "type": "CHEM_SPEC"}, {"name": "irr__82", "type": "CHEM_SPEC"}, {"name": "irr__83", "type": "CHEM_SPEC"}, {"name": "irr__84", "type": "CHEM_SPEC"}, {"name": "irr__85", "type": "CHEM_SPEC"}, {"name": "irr__86", "type": "CHEM_SPEC"}, {"name": "irr__87", "type": "CHEM_SPEC"}, {"name": "irr__88", "type": "CHEM_SPEC"}, {"name": "irr__89", "type": "CHEM_SPEC"}, {"name": "irr__90", "type": "CHEM_SPEC"}, {"name": "irr__91", "type": "CHEM_SPEC"}, {"name": "irr__92", "type": "CHEM_SPEC"}, {"name": "irr__93", "type": "CHEM_SPEC"}, {"name": "irr__94", "type": "CHEM_SPEC"}, {"name": "irr__95", "type": "CHEM_SPEC"}, {"name": "irr__96", "type": "CHEM_SPEC"}, {"name": "irr__97", "type": "CHEM_SPEC"}, {"name": "irr__98", "type": "CHEM_SPEC"}, {"name": "irr__99", "type": "CHEM_SPEC"}, {"name": "irr__100", "type": "CHEM_SPEC"}, {"name": "irr__101", "type": "CHEM_SPEC"}, {"name": "irr__102", "type": "CHEM_SPEC"}, {"name": "irr__103", "type": "CHEM_SPEC"}, {"name": "irr__104", "type": "CHEM_SPEC"}, {"name": "irr__105", "type": "CHEM_SPEC"}, {"name": "irr__106", "type": "CHEM_SPEC"}, {"name": "irr__107", "type": "CHEM_SPEC"}, {"name": "irr__108", "type": "CHEM_SPEC"}, {"name": "irr__109", "type": "CHEM_SPEC"}, {"name": "irr__110", "type": "CHEM_SPEC"}, {"name": "irr__111", "type": "CHEM_SPEC"}, {"name": "irr__112", "type": "CHEM_SPEC"}, {"name": "irr__113", "type": "CHEM_SPEC"}, {"name": "irr__114", "type": "CHEM_SPEC"}, {"name": "irr__115", "type": "CHEM_SPEC"}, {"name": "irr__116", "type": "CHEM_SPEC"}, {"name": "irr__117", "type": "CHEM_SPEC"}, {"name": "irr__118", "type": "CHEM_SPEC"}, {"name": "irr__119", "type": "CHEM_SPEC"}, {"name": "irr__120", "type": "CHEM_SPEC"}, {"name": "irr__121", "type": "CHEM_SPEC"}, {"name": "irr__122", "type": "CHEM_SPEC"}, {"name": "irr__123", "type": "CHEM_SPEC"}, {"name": "irr__124", "type": "CHEM_SPEC"}, {"name": "irr__125", "type": "CHEM_SPEC"}, {"name": "irr__126", "type": "CHEM_SPEC"}, {"name": "irr__127", "type": "CHEM_SPEC"}, {"name": "irr__128", "type": "CHEM_SPEC"}, {"name": "irr__129", "type": "CHEM_SPEC"}, {"name": "irr__130", "type": "CHEM_SPEC"}, {"name": "irr__131", "type": "CHEM_SPEC"}, {"name": "irr__132", "type": "CHEM_SPEC"}, {"name": "irr__133", "type": "CHEM_SPEC"}, {"name": "irr__134", "type": "CHEM_SPEC"}, {"name": "irr__135", "type": "CHEM_SPEC"}, {"name": "irr__136", "type": "CHEM_SPEC"}, {"name": "irr__137", "type": "CHEM_SPEC"}, {"name": "irr__138", "type": "CHEM_SPEC"}, {"name": "irr__139", "type": "CHEM_SPEC"}, {"name": "irr__140", "type": "CHEM_SPEC"}, {"name": "irr__141", "type": "CHEM_SPEC"}, {"name": "irr__142", "type": "CHEM_SPEC"}, {"name": "irr__143", "type": "CHEM_SPEC"}, {"name": "irr__144", "type": "CHEM_SPEC"}, {"name": "irr__145", "type": "CHEM_SPEC"}, {"name": "irr__146", "type": "CHEM_SPEC"}, {"name": "irr__147", "type": "CHEM_SPEC"}, {"name": "irr__148", "type": "CHEM_SPEC"}, {"name": "irr__149", "type": "CHEM_SPEC"}, {"name": "irr__150", "type": "CHEM_SPEC"}, {"name": "irr__151", "type": "CHEM_SPEC"}, {"name": "irr__152", "type": "CHEM_SPEC"}, {"name": "irr__153", "type": "CHEM_SPEC"}, {"name": "irr__154", "type": "CHEM_SPEC"}, {"name": "irr__155", "type": "CHEM_SPEC"}, {"name": "irr__156", "type": "CHEM_SPEC"}, {"name": "irr__157", "type": "CHEM_SPEC"}, {"name": "irr__158", "type": "CHEM_SPEC"}, {"name": "irr__159", "type": "CHEM_SPEC"}, {"name": "irr__160", "type": "CHEM_SPEC"}, {"name": "irr__161", "type": "CHEM_SPEC"}, {"name": "irr__162", "type": "CHEM_SPEC"}, {"name": "irr__163", "type": "CHEM_SPEC"}, {"name": "irr__164", "type": "CHEM_SPEC"}, {"name": "irr__165", "type": "CHEM_SPEC"}, {"name": "irr__166", "type": "CHEM_SPEC"}, {"name": "irr__167", "type": "CHEM_SPEC"}, {"name": "irr__168", "type": "CHEM_SPEC"}, {"name": "irr__169", "type": "CHEM_SPEC"}, {"name": "irr__170", "type": "CHEM_SPEC"}, {"name": "irr__171", "type": "CHEM_SPEC"}, {"name": "irr__172", "type": "CHEM_SPEC"}, {"name": "irr__173", "type": "CHEM_SPEC"}, {"name": "irr__174", "type": "CHEM_SPEC"}, {"name": "irr__175", "type": "CHEM_SPEC"}, {"name": "irr__176", "type": "CHEM_SPEC"}, {"name": "irr__177", "type": "CHEM_SPEC"}, {"name": "irr__178", "type": "CHEM_SPEC"}, {"name": "irr__179", "type": "CHEM_SPEC"}, {"name": "irr__180", "type": "CHEM_SPEC"}, {"name": "irr__181", "type": "CHEM_SPEC"}, {"name": "irr__182", "type": "CHEM_SPEC"}, {"name": "irr__183", "type": "CHEM_SPEC"}, {"name": "irr__184", "type": "CHEM_SPEC"}, {"name": "irr__185", "type": "CHEM_SPEC"}, {"name": "irr__186", "type": "CHEM_SPEC"}, {"name": "irr__187", "type": "CHEM_SPEC"}, {"name": "irr__188", "type": "CHEM_SPEC"}, {"name": "irr__189", "type": "CHEM_SPEC"}, {"name": "irr__190", "type": "CHEM_SPEC"}, {"name": "irr__191", "type": "CHEM_SPEC"}, {"name": "irr__192", "type": "CHEM_SPEC"}, {"name": "irr__193", "type": "CHEM_SPEC"}, {"name": "irr__194", "type": "CHEM_SPEC"}, {"name": "irr__195", "type": "CHEM_SPEC"}, {"name": "irr__196", "type": "CHEM_SPEC"}, {"name": "irr__197", "type": "CHEM_SPEC"}, {"name": "irr__198", "type": "CHEM_SPEC"}, {"name": "irr__199", "type": "CHEM_SPEC"}, {"name": "irr__200", "type": "CHEM_SPEC"}, {"name": "irr__201", "type": "CHEM_SPEC"}, {"name": "irr__202", "type": "CHEM_SPEC"}, {"name": "irr__203", "type": "CHEM_SPEC"}]} \ No newline at end of file From abeed9640d7000bade074d48f41f3b4432d902d6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 21 Nov 2023 09:37:12 -0800 Subject: [PATCH 301/318] Auto-format code changes (#372) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/solver/state.hpp | 4 +- test/examples/test_carbon_bond_5.cpp | 76 ++++++++++++++-------------- test/examples/test_robertson.cpp | 22 ++++---- 3 files changed, 48 insertions(+), 54 deletions(-) diff --git a/include/micm/solver/state.hpp b/include/micm/solver/state.hpp index fa958d05b..06a2e00e7 100644 --- a/include/micm/solver/state.hpp +++ b/include/micm/solver/state.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -13,7 +14,6 @@ #include #include #include -#include namespace micm { @@ -80,7 +80,7 @@ namespace micm /// @brief Print a header of species to display concentrations with respect to time void PrintHeader(); - /// @brief Print state (concentrations) at the given time + /// @brief Print state (concentrations) at the given time /// @param time solving time void PrintState(double time); }; diff --git a/test/examples/test_carbon_bond_5.cpp b/test/examples/test_carbon_bond_5.cpp index 396fd4fe2..1d7a76999 100644 --- a/test/examples/test_carbon_bond_5.cpp +++ b/test/examples/test_carbon_bond_5.cpp @@ -27,49 +27,49 @@ TEST(Examples, carbon_bond_5) micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; micm::State state = solver.GetState(); - const double temperature = 287.45; // K - const double pressure = 101319.9; // Pa - const double air_density = 1e6; // mol m-3 + const double temperature = 287.45; // K + const double pressure = 101319.9; // Pa + const double air_density = 1e6; // mol m-3 state.conditions_[0].temperature_ = temperature; state.conditions_[0].pressure_ = pressure; state.conditions_[0].air_density_ = air_density; std::unordered_map> custom_rate_constants = { - {"EMIS.NO", {1.44e-10}}, - {"EMIS.NO2", {7.56e-12}}, - {"EMIS.CO", {1.9600000000000003e-09}}, - {"EMIS.SO2", {1.06e-09}}, - {"EMIS.FORM", {1.02e-11}}, - {"EMIS.MEOH", {5.920000000000001e-13}}, - {"EMIS.ALD2", {4.25e-12}}, - {"EMIS.PAR", {4.27e-10}}, - {"EMIS.ETH", {4.62e-11}}, - {"EMIS.OLE", {1.49e-11}}, - {"EMIS.IOLE", {1.49e-11}}, - {"EMIS.TOL", {1.53e-11}}, - {"EMIS.XYL", {1.4e-11}}, - {"EMIS.ISOP", {6.03e-12}}, - {"PHOTO.NO2", {0.00477}}, - {"PHOTO.O3->O1D", {2.26e-06}}, - {"PHOTO.O3->O3P", {0.00025299999999999997}}, - {"PHOTO.NO3->NO2", {0.11699999999999999}}, - {"PHOTO.NO3->NO", {0.0144}}, - {"PHOTO.HONO", {0.000918}}, - {"PHOTO.H2O2", {2.59e-06}}, - {"PHOTO.PNA", {1.89e-06}}, - {"PHOTO.HNO3", {8.61e-08}}, - {"PHOTO.NTR", {4.77e-07}}, - {"PHOTO.ROOH", {1.81e-06}}, - {"PHOTO.MEPX", {1.81e-06}}, - {"PHOTO.FORM->HO2", {7.93e-06}}, - {"PHOTO.FORM->CO", {2.2e-05}}, - {"PHOTO.ALD2", {2.2e-06}}, - {"PHOTO.PACD", {1.81e-06}}, - {"PHOTO.ALDX", {2.2e-06}}, - {"PHOTO.OPEN", {0.0006450000000000001}}, - {"PHOTO.MGLY", {7.64e-05}}, - {"PHOTO.ISPD", {1.98e-09}} + { "EMIS.NO", { 1.44e-10 } }, + { "EMIS.NO2", { 7.56e-12 } }, + { "EMIS.CO", { 1.9600000000000003e-09 } }, + { "EMIS.SO2", { 1.06e-09 } }, + { "EMIS.FORM", { 1.02e-11 } }, + { "EMIS.MEOH", { 5.920000000000001e-13 } }, + { "EMIS.ALD2", { 4.25e-12 } }, + { "EMIS.PAR", { 4.27e-10 } }, + { "EMIS.ETH", { 4.62e-11 } }, + { "EMIS.OLE", { 1.49e-11 } }, + { "EMIS.IOLE", { 1.49e-11 } }, + { "EMIS.TOL", { 1.53e-11 } }, + { "EMIS.XYL", { 1.4e-11 } }, + { "EMIS.ISOP", { 6.03e-12 } }, + { "PHOTO.NO2", { 0.00477 } }, + { "PHOTO.O3->O1D", { 2.26e-06 } }, + { "PHOTO.O3->O3P", { 0.00025299999999999997 } }, + { "PHOTO.NO3->NO2", { 0.11699999999999999 } }, + { "PHOTO.NO3->NO", { 0.0144 } }, + { "PHOTO.HONO", { 0.000918 } }, + { "PHOTO.H2O2", { 2.59e-06 } }, + { "PHOTO.PNA", { 1.89e-06 } }, + { "PHOTO.HNO3", { 8.61e-08 } }, + { "PHOTO.NTR", { 4.77e-07 } }, + { "PHOTO.ROOH", { 1.81e-06 } }, + { "PHOTO.MEPX", { 1.81e-06 } }, + { "PHOTO.FORM->HO2", { 7.93e-06 } }, + { "PHOTO.FORM->CO", { 2.2e-05 } }, + { "PHOTO.ALD2", { 2.2e-06 } }, + { "PHOTO.PACD", { 1.81e-06 } }, + { "PHOTO.ALDX", { 2.2e-06 } }, + { "PHOTO.OPEN", { 0.0006450000000000001 } }, + { "PHOTO.MGLY", { 7.64e-05 } }, + { "PHOTO.ISPD", { 1.98e-09 } } }; state.SetCustomRateParameters(custom_rate_constants); @@ -80,5 +80,3 @@ TEST(Examples, carbon_bond_5) EXPECT_EQ(result.state_, (micm::SolverState::Converged)); } - - diff --git a/test/examples/test_robertson.cpp b/test/examples/test_robertson.cpp index 91771c256..7caf63d0c 100644 --- a/test/examples/test_robertson.cpp +++ b/test/examples/test_robertson.cpp @@ -27,26 +27,24 @@ TEST(Examples, robertson) micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; micm::State state = solver.GetState(); - const double temperature = 272.5; // K - const double pressure = 101253.3; // Pa - const double air_density = 1e6; // mol m-3 + const double temperature = 272.5; // K + const double pressure = 101253.3; // Pa + const double air_density = 1e6; // mol m-3 state.conditions_[0].temperature_ = temperature; state.conditions_[0].pressure_ = pressure; state.conditions_[0].air_density_ = air_density; std::unordered_map> initial_concentrations = { - { "A", { 1.0} }, // mol m-3 - { "B", { 0.0} }, // mol m-3 - { "C", { 0.0} }, // mol m-3 + { "A", { 1.0 } }, // mol m-3 + { "B", { 0.0 } }, // mol m-3 + { "C", { 0.0 } }, // mol m-3 }; state.SetConcentrations(initial_concentrations); - std::unordered_map> custom_rate_constants = { - {"PHOTO.r1", {0.04}}, - {"PHOTO.r2", {3e7}}, - {"PHOTO.r3", {1e4}} - }; + std::unordered_map> custom_rate_constants = { { "PHOTO.r1", { 0.04 } }, + { "PHOTO.r2", { 3e7 } }, + { "PHOTO.r3", { 1e4 } } }; state.SetCustomRateParameters(custom_rate_constants); @@ -56,5 +54,3 @@ TEST(Examples, robertson) EXPECT_EQ(result.state_, (micm::SolverState::Converged)); } - - From 5eb45434c67b19a5c91e85f7ad669aaeac8380bc Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 22 Nov 2023 17:10:34 -0800 Subject: [PATCH 302/318] Update Boltzmann constant (#375) Update constants.hpp Adjust the Boltzmann constant to match what's used in the ChemistryCafe --- include/micm/util/constants.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/micm/util/constants.hpp b/include/micm/util/constants.hpp index ee9ddb684..dc03d040f 100644 --- a/include/micm/util/constants.hpp +++ b/include/micm/util/constants.hpp @@ -3,6 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -static constexpr double BOLTZMANN_CONSTANT = 1.3806505e-23; // J K^{-1} +static constexpr double BOLTZMANN_CONSTANT = 1.380649e-23; // J K^{-1} static constexpr double AVOGADRO_CONSTANT = 6.02214076e23; // # mol^{-1} -static constexpr double GAS_CONSTANT = BOLTZMANN_CONSTANT * AVOGADRO_CONSTANT; // J K^{-1} mol^{-1} \ No newline at end of file +static constexpr double GAS_CONSTANT = BOLTZMANN_CONSTANT * AVOGADRO_CONSTANT; // J K^{-1} mol^{-1} From 7631529ed613272aa73a5d13c95e78cde999a835 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 22 Nov 2023 17:11:59 -0800 Subject: [PATCH 303/318] Auto-format code changes (#376) Auto-format code using Clang-Format Co-authored-by: GitHub Actions --- include/micm/util/constants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/util/constants.hpp b/include/micm/util/constants.hpp index dc03d040f..e8315b64a 100644 --- a/include/micm/util/constants.hpp +++ b/include/micm/util/constants.hpp @@ -3,6 +3,6 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once -static constexpr double BOLTZMANN_CONSTANT = 1.380649e-23; // J K^{-1} +static constexpr double BOLTZMANN_CONSTANT = 1.380649e-23; // J K^{-1} static constexpr double AVOGADRO_CONSTANT = 6.02214076e23; // # mol^{-1} static constexpr double GAS_CONSTANT = BOLTZMANN_CONSTANT * AVOGADRO_CONSTANT; // J K^{-1} mol^{-1} From 38167053c55c0160d1ec7ceb3abfda2a3fcb344f Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 22 Nov 2023 17:19:51 -0800 Subject: [PATCH 304/318] Update CMakeLists.txt Only copy examples folder when MICM is top-level project --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 01e7c665e..71d4faffa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,7 +118,7 @@ endif() ################################################################################ # Copy configuration data -if(ENABLE_TESTS OR ENABLE_EXAMPLES) -add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_SOURCE_DIR}/examples/configs ${CMAKE_BINARY_DIR}/examples/configs) -endif() \ No newline at end of file +if(PROJECT_IS_TOP_LEVEL AND (ENABLE_TESTS OR ENABLE_EXAMPLES)) + add_custom_target(copy_example_configs ALL ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/examples/configs ${CMAKE_BINARY_DIR}/examples/configs) +endif() From e3ab08fa888cb597d17ff1ff6e6313236b5bee13 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 1 Dec 2023 10:47:11 -0600 Subject: [PATCH 305/318] 374 tutorial for each example in the examples folder (#378) * adding initial conditions for cb5, correctly outputting information * initial conditions * removing examples tests --- .github/workflows/clang_format.yml | 2 +- examples/configs/TS1/initial_conditions.csv | 500 ++++++++++++------ examples/configs/TS1/reactions.json | 206 ++++---- .../carbon_bond_5/initial_conditions.csv | 387 ++++++++++++-- examples/configs/carbon_bond_5/species.json | 273 +++++++++- examples/example.cpp | 6 +- include/micm/solver/state.hpp | 2 +- include/micm/solver/state.inl | 6 +- test/CMakeLists.txt | 3 - test/examples/CMakeLists.txt | 7 - test/examples/test_TS1.cpp | 216 -------- test/examples/test_carbon_bond_5.cpp | 82 --- test/examples/test_chapman.cpp | 138 ----- test/examples/test_robertson.cpp | 56 -- 14 files changed, 1071 insertions(+), 813 deletions(-) delete mode 100644 test/examples/CMakeLists.txt delete mode 100644 test/examples/test_TS1.cpp delete mode 100644 test/examples/test_carbon_bond_5.cpp delete mode 100644 test/examples/test_chapman.cpp delete mode 100644 test/examples/test_robertson.cpp diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml index 6c2f804ef..bc75fa61d 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/clang_format.yml @@ -24,7 +24,7 @@ jobs: 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' \) -exec clang-format -i --style=file {} + + find test -not -path "./test/tutorial/*" -type f \( -name '*.hpp' -o -name '*.h' -o -name '*.cpp' \) -exec clang-format -i --style=file {} + - name: Check for changes id: check-changes diff --git a/examples/configs/TS1/initial_conditions.csv b/examples/configs/TS1/initial_conditions.csv index 4c0b92bff..274fee21f 100644 --- a/examples/configs/TS1/initial_conditions.csv +++ b/examples/configs/TS1/initial_conditions.csv @@ -1,163 +1,341 @@ -CONC.CH4,7.63078e-05 -CONC.CL2,4.23932e-08 -CONC.CO,4.23932e-08 -CONC.CO2,0.0163214 -CONC.DMS,4.23932e-08 -CONC.H2O,0.0423932 -CONC.H2O2,4.23932e-08 -CONC.HNO3,4.23932e-08 -CONC.ISOP,4.23932e-08 -CONC.M,42.3932 -CONC.N2,33.0667 -CONC.NH3,4.23932e-08 -CONC.NO,4.23932e-08 -CONC.NO2,4.23932e-08 -CONC.O2,8.90257 -CONC.O3,2.11966e-06 -CONC.OH,4.23932e-11 -CONC.SO2,4.23932e-08 +CONC.O2,8.90261411 +CONC.N2,33.1007671 +CONC.ALKNIT,8.72E-10 +CONC.BZOOH,2.68E-11 +CONC.C6H5OOH,4.20E-10 +CONC.COF2,5.24E-11 +CONC.COFCL,7.92E-12 +CONC.HF,1.81E-11 +CONC.F,3.89E-28 +CONC.BENZO2,2.09E-13 +CONC.BZOO,8.81E-13 +CONC.PAN,4.25E-08 +CONC.MVK,2.61E-08 +CONC.MACROOH,1.21E-09 +CONC.SOAG0,3.33E-11 +CONC.SOAG1,2.14E-10 +CONC.SOAG2,1.40E-09 +CONC.SOAG3,4.27E-09 +CONC.SOAG4,2.25E-08 +CONC.ISOPNITB,1.05E-09 +CONC.SO3,1.75E-17 +CONC.OCS,1.96E-08 +CONC.SO,1.22E-20 +CONC.S,1.10E-26 +CONC.H,9.67E-18 +CONC.MEK,1.04E-08 +CONC.MTERP,1.09E-09 +CONC.N2O5,1.00E-11 +CONC.HCN,1.90E-08 +CONC.SVOC,1.98E-10 +CONC.ISOPNO3,5.35E-13 +CONC.RO2,7.13E-12 +CONC.PHENO2,2.54E-13 +CONC.IVOC,2.06E-09 +CONC.TERPNIT,8.28E-10 +CONC.HNO3,1.04E-07 +CONC.ACBZO2,3.54E-13 +CONC.CH3COOOH,7.19E-09 +CONC.SO2,6.15E-08 +CONC.CH4,7.03E-05 +CONC.CH3CL,2.07E-08 +CONC.CH3CO3,8.32E-11 +CONC.C6H5O2,6.23E-12 +CONC.TERPROD1,1.68E-09 +CONC.HYAC,4.88E-08 +CONC.HPALD,3.56E-10 +CONC.H2O,7.46E-01 +CONC.NO2,6.37E-08 +CONC.EOOH,3.56E-09 +CONC.NTERPOOH,9.37E-11 +CONC.CCL4,2.96E-09 +CONC.CF2CLBR,1.24E-10 +CONC.CF3BR,1.26E-10 +CONC.CFC11,8.50E-09 +CONC.CFC113,2.66E-09 +CONC.CFC114,6.19E-10 +CONC.CFC115,3.29E-10 +CONC.CFC12,1.92E-08 +CONC.CH2BR2,4.32E-11 +CONC.CH3BR,2.37E-10 +CONC.CH3CCL3,5.55E-11 +CONC.NO,1.47E-08 +CONC.BR,3.67E-14 +CONC.BRCL,9.18E-19 +CONC.BRO,1.95E-13 +CONC.BRONO2,1.22E-11 +CONC.CL,1.07E-15 +CONC.CL2,1.01E-16 +CONC.CL2O2,1.89E-23 +CONC.CLO,8.06E-14 +CONC.CLONO2,4.09E-11 +CONC.HCOOH,8.79E-09 +CONC.HBR,4.48E-11 +CONC.HOBR,1.65E-12 +CONC.HOCL,2.11E-12 +CONC.BIGENE,6.59E-10 +CONC.C2H4,2.51E-08 +CONC.C2H5O2,1.55E-12 +CONC.CH3COCHO,1.95E-08 +CONC.CH3COCH3,7.37E-08 +CONC.O,2.85E-14 +CONC.OCLO,7.68E-19 +CONC.O1D,1.53E-19 +CONC.PHENO,1.00E-12 +CONC.HCFC141B,9.29E-10 +CONC.HCFC142B,8.24E-10 +CONC.HCFC22,9.07E-09 +CONC.DMS,1.74E-10 +CONC.C2H5OH,2.75E-08 +CONC.HCL,8.17E-10 +CONC.BEPOMUC,5.40E-12 +CONC.CHBR3,3.87E-11 +CONC.H2402,1.52E-11 +CONC.CO2,1.56E-02 +CONC.BZALD,4.70E-10 +CONC.BENZENE,2.67E-09 +CONC.C3H7O2,2.31E-12 +CONC.CH3O2,3.34E-10 +CONC.BCARY,5.04E-12 +CONC.BIGALD,1.33E-11 +CONC.BIGALD2,6.58E-11 +CONC.BIGALD3,6.83E-11 +CONC.BIGALD4,7.34E-10 +CONC.BIGALK,1.90E-08 +CONC.H2O2,7.36E-08 +CONC.C2H5OOH,7.99E-11 +CONC.C2H6,2.93E-08 +CONC.C3H8,9.70E-09 +CONC.C3H6,2.42E-09 +CONC.CH2O,2.44E-07 +CONC.CH3CN,4.66E-09 +CONC.C2H2,2.13E-08 +CONC.CH3OH,2.74E-07 +CONC.CH3OOH,1.69E-08 +CONC.CRESOL,1.06E-10 +CONC.ENEO2,7.98E-12 +CONC.MACRO2,1.09E-10 +CONC.ISOPAO2,1.39E-10 +CONC.MALO2,1.07E-13 +CONC.ISOPBO2,9.08E-11 +CONC.MCO3,9.23E-12 +CONC.MDIALO2,1.61E-13 +CONC.MEKO2,2.81E-12 +CONC.EO2,4.04E-11 +CONC.EO,9.93E-18 +CONC.GLYOXAL,1.25E-08 +CONC.MPAN,3.11E-09 +CONC.NC4CH2OH,7.30E-13 +CONC.ISOPOOH,4.68E-09 +CONC.GLYALD,4.58E-08 +CONC.HO2,1.69E-09 +CONC.HOCH2OO,1.07E-13 +CONC.H2,1.93E-05 +CONC.HYDRALD,1.49E-08 +CONC.ISOP,1.23E-08 +CONC.NTERPO2,9.18E-13 +CONC.TOLO2,3.23E-12 +CONC.TERP2O2,2.15E-11 +CONC.XYLENO2,4.98E-12 +CONC.TERPO2,1.29E-11 +CONC.XYLOLO2,4.62E-13 +CONC.PBZNIT,1.78E-10 +CONC.XYLENES,2.82E-09 +CONC.PO2,1.46E-11 +CONC.TOLUENE,4.88E-09 +CONC.XO2,1.12E-10 +CONC.XOOH,7.93E-09 +CONC.TERPROD2,3.81E-09 +CONC.MEKOOH,6.15E-11 +CONC.MACR,1.06E-08 +CONC.HONITR,6.79E-09 +CONC.ISOPNITA,1.62E-09 +CONC.ISOPNOOH,9.16E-11 +CONC.IEPOX,5.84E-09 +CONC.ONITR,2.67E-09 +CONC.H2SO4,2.27E-11 +CONC.N2O,1.27E-05 +CONC.NO3,9.32E-12 +CONC.OH,2.52E-11 +CONC.PHENOOH,9.15E-12 +CONC.PHENOL,9.49E-11 +CONC.XYLOL,9.92E-11 +CONC.ROOH,4.40E-10 +CONC.O3,2.49E-06 +CONC.TERPOOH,2.46E-10 +CONC.TOLOOH,9.69E-11 +CONC.XYLENOOH,1.50E-10 +CONC.XYLOLOOH,1.31E-11 +CONC.CO,3.25E-06 +CONC.CH3COOH,8.20E-09 +CONC.CH3CHO,2.51E-08 +CONC.BIGALD1,6.47E-11 +CONC.ALKOOH,3.79E-10 +CONC.DICARBO2,1.55E-13 +CONC.BENZOOH,5.78E-12 +CONC.ALKO2,1.39E-11 +CONC.HO2NO2,7.99E-10 +CONC.C3H7OOH,7.87E-11 +CONC.NH3,7.59E-08 +CONC.TERP2OOH,5.96E-10 +CONC.POOH,9.24E-10 +CONC.NOA,5.46E-09 +CONC.NC4CHO,3.67E-11 +CONC.TEPOMUC,2.21E-10 +PHOTO.jacet,1.25E-06 +PHOTO.jalknit,5.95E-06 +PHOTO.jalkooh,5.95E-06 +PHOTO.jbenzooh,5.95E-06 +PHOTO.jbepomuc,1.01E-03 +PHOTO.jbigald,2.01E-03 +PHOTO.jbigald1,1.41E-03 +PHOTO.jbigald2,2.01E-03 +PHOTO.jbigald3,2.01E-03 +PHOTO.jbigald4,6.03E-05 +PHOTO.jbrcl,1.12E-02 +PHOTO.jbro,4.07E-02 +PHOTO.jbrono2_a,1.27E-03 +PHOTO.jbrono2_b,2.24E-04 +PHOTO.jbzooh,5.95E-06 +PHOTO.jc2h5ooh,5.95E-06 +PHOTO.jc3h7ooh,5.95E-06 +PHOTO.jc6h5ooh,5.95E-06 +PHOTO.jccl4,1.28E-23 +PHOTO.jcf2cl2,2.50E-24 +PHOTO.jcf2clbr,6.95E-09 +PHOTO.jcf3br,1.45E-12 +PHOTO.jcfc113,2.60E-24 +PHOTO.jcfc114,2.09E-25 +PHOTO.jcfc115,1.23E-26 +PHOTO.jcfcl3,1.38E-23 +PHOTO.jch2br2,1.15E-09 +PHOTO.jch2o_a,3.47E-05 +PHOTO.jch2o_b,4.92E-05 +PHOTO.jch3br,9.35E-16 +PHOTO.jch3ccl3,1.79E-23 +PHOTO.jch3cho,7.18E-06 +PHOTO.jch3cl,4.68E-25 +PHOTO.jch3co3h,2.23E-06 +PHOTO.jch3ooh,5.95E-06 +PHOTO.jch4_a,0.00E+00 +PHOTO.jch4_b,0.00E+00 +PHOTO.jchbr3,1.81E-06 +PHOTO.jcl2,2.52E-03 +PHOTO.jcl2o2,1.98E-03 +PHOTO.jclo,2.40E-04 +PHOTO.jclono2_a,4.59E-05 +PHOTO.jclono2_b,1.00E-05 +PHOTO.jco2,5.28E-30 +PHOTO.jcof2,3.30E-25 +PHOTO.jcofcl,2.30E-24 +PHOTO.jeooh,5.95E-06 +PHOTO.jglyald,6.18E-06 +PHOTO.jglyoxal,1.40E-04 +PHOTO.jh2402,3.68E-09 +PHOTO.jh2o2,7.98E-06 +PHOTO.jh2o_a,8.96E-31 +PHOTO.jh2o_b,0.00E+00 +PHOTO.jh2o_c,0.00E+00 +PHOTO.jh2so4,3.10E-10 +PHOTO.jhbr,2.55E-23 +PHOTO.jhcfc141b,4.36E-24 +PHOTO.jhcfc142b,3.98E-26 +PHOTO.jhcfc22,8.55E-27 +PHOTO.jhcl,6.59E-25 +PHOTO.jhf,0.00E+00 +PHOTO.jhno3,8.54E-07 +PHOTO.jho2no2_a,1.71E-06 +PHOTO.jho2no2_b,1.72E-05 +PHOTO.jhobr,2.37E-03 +PHOTO.jhocl,3.04E-04 +PHOTO.jhonitr,3.47E-05 +PHOTO.jhpald,6.03E-05 +PHOTO.jhyac,2.47E-06 +PHOTO.jisopnooh,5.95E-06 +PHOTO.jisopooh,5.95E-06 +PHOTO.jmacr_a,2.77E-06 +PHOTO.jmacr_b,2.77E-06 +PHOTO.jmek,1.25E-06 +PHOTO.jmekooh,5.95E-06 +PHOTO.jmgly,1.40E-04 +PHOTO.jmpan,9.69E-07 +PHOTO.jmvk,5.20E-06 +PHOTO.jn2o,9.25E-25 +PHOTO.jn2o5_a,5.54E-05 +PHOTO.jn2o5_b,3.76E-09 +PHOTO.jnc4cho,3.47E-05 +PHOTO.jno,0.00E+00 +PHOTO.jno2,1.01E-02 +PHOTO.jno3_a,1.99E-01 +PHOTO.jno3_b,1.24E-02 +PHOTO.jnoa,3.47E-05 +PHOTO.jnterpooh,5.95E-06 +PHOTO.jo2_a,0.00E+00 +PHOTO.jo2_b,1.40E-28 +PHOTO.jo3_a,5.13E-05 +PHOTO.jo3_b,4.49E-04 +PHOTO.joclo,7.26E-02 +PHOTO.jocs,6.18E-12 +PHOTO.jonitr,7.18E-06 +PHOTO.jpan,9.69E-07 +PHOTO.jphenooh,5.95E-06 +PHOTO.jpooh,5.95E-06 +PHOTO.jrooh,5.95E-06 +PHOTO.jso,2.58E-22 +PHOTO.jso2,1.18E-22 +PHOTO.jso3,2.36E-07 +PHOTO.jtepomuc,1.01E-03 +PHOTO.jterp2ooh,5.95E-06 +PHOTO.jterpnit,5.95E-06 +PHOTO.jterpooh,5.95E-06 +PHOTO.jterprd1,7.18E-06 +PHOTO.jterprd2,7.18E-06 +PHOTO.jtolooh,5.95E-06 +PHOTO.jxooh,5.95E-06 +PHOTO.jxylenooh,5.95E-06 +PHOTO.jxylolooh,5.95E-06 +PHOTO.jsf6,1.0e-20 +PHOTO.jsoa1_a1,1.0e-20 +PHOTO.jsoa1_a2,1.0e-20 +PHOTO.jsoa2_a1,1.0e-20 +PHOTO.jsoa2_a2,1.0e-20 +PHOTO.jsoa3_a1,1.0e-20 +PHOTO.jsoa3_a2,1.0e-20 +PHOTO.jsoa4_a1,1.0e-20 +PHOTO.jsoa4_a2,1.0e-20 +PHOTO.jsoa5_a1,1.0e-20 +PHOTO.jsoa5_a2,1.0e-20 +USER.het1,1.0E-20 +USER.het2,1.0E-20 +USER.het3,1.0E-20 +USER.het4,6.022140760000E-03 +USER.het5,6.022140760000E-03 +USER.het6,6.022140760000E-03 +USER.het7,1.0E-20 +USER.het8,1.0E-20 +USER.het9,6.022140760000E-03 +USER.het10,6.022140760000E-03 +USER.het11,1.0E-20 +USER.het12,1.0E-20 +USER.het13,1.0E-20 +USER.het14,1.0E-20 +USER.het15,6.022140760000E-03 +USER.het16,6.022140760000E-03 +USER.het17,6.022140760000E-03 +SURF.usr_NO2_aer,2.820947917738780E-08,1.0e11 +SURF.usr_HO2_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NO3_aer,2.820947917738780E-08,1.0e11 +SURF.usr_ISOPNITA_aer,2.820947917738780E-08,1.0e11 +SURF.usr_ISOPNITB_aer,2.820947917738780E-08,1.0e11 +SURF.usr_GLYOXAL_aer,2.820947917738780E-08,1.0e11 +SURF.usr_ONITR_aer,2.820947917738780E-08,1.0e11 +SURF.usr_N2O5_aer,2.820947917738780E-08,1.0e11 +SURF.usr_HONITR_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NC4CHO_aer,2.820947917738780E-08,1.0e11 +SURF.usr_TERPNIT_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NTERPOOH_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NC4CH2OH_aer,2.820947917738780E-08,1.0e11 ENV.temperature,287.45 ENV.pressure,101319.9 -ENV.air_density,42.3932 -ENV.time_step,150.0 -PHOTO.jacet,1e-08 -PHOTO.jalknit->,jch3ooh,1e-08 -PHOTO.jalkooh->,jch3ooh,1e-08 -PHOTO.jbenzooh->,jch3ooh,1e-08 -PHOTO.jbepomuc->,.10*jno2,1e-08 -PHOTO.jbigald->,0.2*jno2,1e-08 -PHOTO.jbigald1->,.14*jno2,1e-08 -PHOTO.jbigald2->,.20*jno2,1e-08 -PHOTO.jbigald3->,.20*jno2,1e-08 -PHOTO.jbigald4->,.006*jno2,1e-08 -PHOTO.jbrcl,1e-08 -PHOTO.jbro,1e-08 -PHOTO.jbrono2_a,1e-08 -PHOTO.jbrono2_b,1e-08 -PHOTO.jbzooh->,jch3ooh,1e-08 -PHOTO.jc2h5ooh->,jch3ooh,1e-08 -PHOTO.jc3h7ooh->,jch3ooh,1e-08 -PHOTO.jc6h5ooh->,jch3ooh,1e-08 -PHOTO.jccl4,1e-08 -PHOTO.jcf2cl2,1e-08 -PHOTO.jcf2clbr,1e-08 -PHOTO.jcf3br,1e-08 -PHOTO.jcfc113,1e-08 -PHOTO.jcfc114,1e-08 -PHOTO.jcfc115,1e-08 -PHOTO.jcfcl3,1e-08 -PHOTO.jch2br2,1e-08 -PHOTO.jch2o_a,1e-08 -PHOTO.jch2o_b,1e-08 -PHOTO.jch3br,1e-08 -PHOTO.jch3ccl3,1e-08 -PHOTO.jch3cho,1e-08 -PHOTO.jch3cl,1e-08 -PHOTO.jch3co3h->,0.28*jh2o2,1e-08 -PHOTO.jch3ooh,1e-08 -PHOTO.jch4_a,1e-08 -PHOTO.jch4_b,1e-08 -PHOTO.jchbr3,1e-08 -PHOTO.jcl2,1e-08 -PHOTO.jcl2o2,1e-08 -PHOTO.jclo,1e-08 -PHOTO.jclono2_a,1e-08 -PHOTO.jclono2_b,1e-08 -PHOTO.jco2,1e-08 -PHOTO.jcof2,1e-08 -PHOTO.jcofcl,1e-08 -PHOTO.jeooh->,jch3ooh,1e-08 -PHOTO.jglyald,1e-08 -PHOTO.jglyoxal->,jmgly,1e-08 -PHOTO.jh2402,1e-08 -PHOTO.jh2o2,1e-08 -PHOTO.jh2o_a,1e-08 -PHOTO.jh2o_b,1e-08 -PHOTO.jh2o_c,1e-08 -PHOTO.jh2so4,1e-08 -PHOTO.jhbr,1e-08 -PHOTO.jhcfc141b,1e-08 -PHOTO.jhcfc142b,1e-08 -PHOTO.jhcfc22,1e-08 -PHOTO.jhcl,1e-08 -PHOTO.jhf,1e-08 -PHOTO.jhno3,1e-08 -PHOTO.jho2no2_a,1e-08 -PHOTO.jho2no2_b,1e-08 -PHOTO.jhobr,1e-08 -PHOTO.jhocl,1e-08 -PHOTO.jhonitr->,jch2o_a,1e-08 -PHOTO.jhpald->,.006*jno2,1e-08 -PHOTO.jhyac,1e-08 -PHOTO.jisopnooh->,jch3ooh,1e-08 -PHOTO.jisopooh->,jch3ooh,1e-08 -PHOTO.jmacr_a,1e-08 -PHOTO.jmacr_b,1e-08 -PHOTO.jmek->,jacet,1e-08 -PHOTO.jmekooh->,jch3ooh,1e-08 -PHOTO.jmgly,1e-08 -PHOTO.jmpan->,jpan,1e-08 -PHOTO.jmvk,1e-08 -PHOTO.jn2o,1e-08 -PHOTO.jn2o5_a,1e-08 -PHOTO.jn2o5_b,1e-08 -PHOTO.jnc4cho->,jch2o_a,1e-08 -PHOTO.jno2,1e-08 -PHOTO.jno3_a,1e-08 -PHOTO.jno3_b,1e-08 -PHOTO.jno=userdefined,,1e-08 -PHOTO.jnoa->,jch2o_a,1e-08 -PHOTO.jnterpooh->,jch3ooh,1e-08 -PHOTO.jo2_a=userdefined,,1e-08 -PHOTO.jo2_b=userdefined,,1e-08 -PHOTO.jo3_a,1e-08 -PHOTO.jo3_b,1e-08 -PHOTO.joclo,1e-08 -PHOTO.jocs,1e-08 -PHOTO.jonitr->,jch3cho,1e-08 -PHOTO.jpan,1e-08 -PHOTO.jphenooh->,jch3ooh,1e-08 -PHOTO.jpooh->,jch3ooh,1e-08 -PHOTO.jrooh->,jch3ooh,1e-08 -PHOTO.jsf6,1e-08 -PHOTO.jso,1e-08 -PHOTO.jso2,1e-08 -PHOTO.jso3,1e-08 -PHOTO.jsoa1_a1->,.0004*jno2,1e-08 -PHOTO.jsoa1_a2->,.0004*jno2,1e-08 -PHOTO.jsoa2_a1->,.0004*jno2,1e-08 -PHOTO.jsoa2_a2->,.0004*jno2,1e-08 -PHOTO.jsoa3_a1->,.0004*jno2,1e-08 -PHOTO.jsoa3_a2->,.0004*jno2,1e-08 -PHOTO.jsoa4_a1->,.0004*jno2,1e-08 -PHOTO.jsoa4_a2->,.0004*jno2,1e-08 -PHOTO.jsoa5_a1->,.0004*jno2,1e-08 -PHOTO.jsoa5_a2->,.0004*jno2,1e-08 -PHOTO.jtepomuc->,.10*jno2,1e-08 -PHOTO.jterp2ooh->,jch3ooh,1e-08 -PHOTO.jterpnit->,jch3ooh,1e-08 -PHOTO.jterpooh->,jch3ooh,1e-08 -PHOTO.jterprd1->,jch3cho,1e-08 -PHOTO.jterprd2->,jch3cho,1e-08 -PHOTO.jtolooh->,jch3ooh,1e-08 -PHOTO.jxooh->,jch3ooh,1e-08 -PHOTO.jxylenooh->,jch3ooh,1e-08 -PHOTO.jxylolooh->,jch3ooh,1e-08 -USER.het1,1e-08 -USER.het10,1e-08 -USER.het11,1e-08 -USER.het12,1e-08 -USER.het13,1e-08 -USER.het14,1e-08 -USER.het15,1e-08 -USER.het16,1e-08 -USER.het17,1e-08 -USER.het2,1e-08 -USER.het3,1e-08 -USER.het4,1e-08 -USER.het5,1e-08 -USER.het6,1e-08 -USER.het7,1e-08 -USER.het8,1e-08 -USER.het9,1e-08 -USER.k_co_oh_jpl19,1e-08 \ No newline at end of file +ENV.air_density,42.3934005 +ENV.time_step,60.0 \ No newline at end of file diff --git a/examples/configs/TS1/reactions.json b/examples/configs/TS1/reactions.json index fb2bc1c43..6ca324c69 100644 --- a/examples/configs/TS1/reactions.json +++ b/examples/configs/TS1/reactions.json @@ -13,7 +13,7 @@ "type": "TROE", "k0_A": 5.5e-30, "kinf_A": 8.3e-13, - "N": -2, + "kinf_B": 2, "reactants": { "C2H2": { }, "OH": { } @@ -57,7 +57,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jterpnit->,jch3ooh", + "MUSICA name": "jterpnit", "reactants": { "TERPNIT": { } }, @@ -113,7 +113,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jo2_b=userdefined,", + "MUSICA name": "jo2_b", "reactants": { "O2": { } }, @@ -160,7 +160,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jxylenooh->,jch3ooh", + "MUSICA name": "jxylenooh", "reactants": { "XYLENOOH": { } }, @@ -220,12 +220,12 @@ }, { "type": "TROE", - "k0_A": 1.07767, - "k0_B": -5.6, + "k0_A": 0.81103, + "k0_B": -4.1, "k0_C": -14000, - "kinf_A": 1.03323e+17, + "kinf_A": 1.05545e17, "kinf_C": -14000, - "N": 1.5, + "kinf_B": -1.6, "reactants": { "PAN": { } }, @@ -315,7 +315,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jterp2ooh->,jch3ooh", + "MUSICA name": "jterp2ooh", "reactants": { "TERP2OOH": { } }, @@ -441,7 +441,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbigald4->,.006*jno2", + "MUSICA name": "jbigald4", "reactants": { "BIGALD4": { } }, @@ -491,16 +491,18 @@ } }, { - "type": "ARRHENIUS", - "A": 1.38889e+16, - "Ea": 8.40401e-20, + "type": "TROE", + "k0_A": 8.796296296E-6, + "k0_B": -3.6, + "k0_C": -8537.0, + "kinf_A": 1.712962963E+15, + "kinf_B": -1.6, + "kinf_C": -8537.0, "reactants": { - "CL2O2": { }, - "M": { } + "CL2O2": { } }, "products": { - "CLO": { "yield": 2 }, - "M": { } + "CLO": { "yield": 2 } } }, { @@ -517,7 +519,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa3_a2->,.0004*jno2", + "MUSICA name": "jsoa3_a2", "reactants": { "soa3_a2": { } }, @@ -605,7 +607,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jtepomuc->,.10*jno2", + "MUSICA name": "jtepomuc", "reactants": { "TEPOMUC": { } }, @@ -620,7 +622,7 @@ "k0_A": 9.7e-29, "k0_B": -5.6, "kinf_A": 9.3e-12, - "N": 1.5, + "kinf_B": -1.5, "reactants": { "MDIALO2": { }, "NO2": { } @@ -696,7 +698,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jno=userdefined,", + "MUSICA name": "jno", "reactants": { "NO": { } }, @@ -833,7 +835,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jc3h7ooh->,jch3ooh", + "MUSICA name": "jc3h7ooh", "reactants": { "C3H7OOH": { } }, @@ -879,7 +881,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jnoa->,jch2o_a", + "MUSICA name": "jnoa", "reactants": { "NOA": { } }, @@ -955,8 +957,8 @@ "k0_A": 6.1e-33, "k0_B": -1.5, "kinf_A": 9.8e-15, + "kinf_B": 4.6, "Fc": 0.8, - "N": -4.6, "reactants": { "OH": { }, "HCN": { } @@ -1015,7 +1017,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jnterpooh->,jch3ooh", + "MUSICA name": "jnterpooh", "reactants": { "NTERPOOH": { } }, @@ -1041,7 +1043,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jrooh->,jch3ooh", + "MUSICA name": "jrooh", "reactants": { "ROOH": { } }, @@ -1133,7 +1135,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jpooh->,jch3ooh", + "MUSICA name": "jpooh", "reactants": { "POOH": { } }, @@ -1173,7 +1175,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa2_a2->,.0004*jno2", + "MUSICA name": "jsoa2_a2", "reactants": { "soa2_a2": { } }, @@ -1432,7 +1434,7 @@ "k0_A": 2.9e-31, "k0_B": -4.1, "kinf_A": 1.7e-12, - "N": -0.2, + "kinf_B": 0.2, "reactants": { "SO2": { }, "OH": { } @@ -1447,7 +1449,6 @@ "k0_A": 1.8e-30, "k0_B": -3, "kinf_A": 2.8e-11, - "N": 0, "reactants": { "OH": { }, "NO2": { } @@ -1461,7 +1462,7 @@ "k0_A": 1.8e-31, "k0_B": -3.4, "kinf_A": 1.5e-11, - "N": 1.9, + "kinf_B": -1.9, "reactants": { "CLO": { }, "NO2": { } @@ -1590,7 +1591,6 @@ "k0_B": -3.5, "kinf_A": 3.0e-11, "Fc": 0.5, - "N": 0, "reactants": { "OH": { }, "C3H6": { } @@ -1630,12 +1630,12 @@ }, { "type": "TROE", - "k0_A": 0.000413793, + "k0_A": 4.13793120e-04, "k0_B": -3, "k0_C": -10840, - "kinf_A": 2.75862e+14, + "kinf_A": 2.7586208e+14, "kinf_C": -10840, - "N": -0.1, + "kinf_B": 0.1, "reactants": { "N2O5": { } }, @@ -1773,7 +1773,7 @@ "k0_A": 1.9e-32, "k0_B": -3.6, "kinf_A": 3.7e-12, - "N": 1.6, + "kinf_B": -1.6, "reactants": { "CLO": { "qty": 2 } }, @@ -1783,7 +1783,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa5_a2->,.0004*jno2", + "MUSICA name": "jsoa5_a2", "reactants": { "soa5_a2": { } }, @@ -1889,7 +1889,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbenzooh->,jch3ooh", + "MUSICA name": "jbenzooh", "reactants": { "BENZOOH": { } }, @@ -2068,7 +2068,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jisopnooh->,jch3ooh", + "MUSICA name": "jisopnooh", "reactants": { "ISOPNOOH": { } }, @@ -2080,7 +2080,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jalkooh->,jch3ooh", + "MUSICA name": "jalkooh", "reactants": { "ALKOOH": { } }, @@ -2134,7 +2134,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa1_a1->,.0004*jno2", + "MUSICA name": "jsoa1_a1", "reactants": { "soa1_a1": { } }, @@ -2148,7 +2148,7 @@ "k0_B": -3.1, "kinf_A": 9.0e-12, "Fc": 0.48, - "N": 0.85, + "kinf_B": -0.85, "reactants": { "C2H4": { }, "OH": { } @@ -2162,7 +2162,7 @@ "k0_A": 5.3e-32, "k0_B": -1.8, "kinf_A": 9.5e-11, - "N": -0.4, + "kinf_B": 0.4, "reactants": { "O2": { }, "H": { } @@ -2261,7 +2261,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jeooh->,jch3ooh", + "MUSICA name": "jeooh", "reactants": { "EOOH": { } }, @@ -2309,7 +2309,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jisopooh->,jch3ooh", + "MUSICA name": "jisopooh", "reactants": { "ISOPOOH": { } }, @@ -2365,7 +2365,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jc6h5ooh->,jch3ooh", + "MUSICA name": "jc6h5ooh", "reactants": { "C6H5OOH": { } }, @@ -2429,7 +2429,7 @@ "k0_A": 9.7e-29, "k0_B": -5.6, "kinf_A": 9.3e-12, - "N": 1.5, + "kinf_B": -1.5, "reactants": { "MALO2": { }, "NO2": { } @@ -2610,7 +2610,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbigald2->,.20*jno2", + "MUSICA name": "jbigald2", "reactants": { "BIGALD2": { } }, @@ -2661,7 +2661,7 @@ "k0_A": 9.7e-29, "k0_B": -5.6, "kinf_A": 9.3e-12, - "N": 1.5, + "kinf_B": -1.5, "reactants": { "ACBZO2": { }, "NO2": { } @@ -2916,7 +2916,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jglyoxal->,jmgly", + "MUSICA name": "jglyoxal", "reactants": { "GLYOXAL": { } }, @@ -3045,7 +3045,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jhpald->,.006*jno2", + "MUSICA name": "jhpald", "reactants": { "HPALD": { } }, @@ -3098,7 +3098,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbzooh->,jch3ooh", + "MUSICA name": "jbzooh", "reactants": { "BZOOH": { } }, @@ -3198,7 +3198,7 @@ "k0_A": 5.2e-30, "k0_B": -2.4, "kinf_A": 2.2e-10, - "N": 0.7, + "kinf_B": -0.7, "reactants": { "C2H2": { }, "CL": { } @@ -3234,7 +3234,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jalknit->,jch3ooh", + "MUSICA name": "jalknit", "reactants": { "ALKNIT": { } }, @@ -3279,7 +3279,7 @@ "k0_A": 9.7e-29, "k0_B": -5.6, "kinf_A": 9.3e-12, - "N": 1.5, + "kinf_B": -1.5, "reactants": { "MCO3": { }, "NO2": { } @@ -3340,7 +3340,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbigald->,0.2*jno2", + "MUSICA name": "jbigald", "reactants": { "BIGALD": { } }, @@ -3382,7 +3382,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa5_a1->,.0004*jno2", + "MUSICA name": "jsoa5_a1", "reactants": { "soa5_a1": { } }, @@ -3420,7 +3420,7 @@ "k0_A": 7.3e-29, "k0_B": -4.1, "kinf_A": 9.5e-12, - "N": 1.6, + "kinf_B": -1.6, "reactants": { "CH3CO3": { }, "NO2": { } @@ -3674,7 +3674,6 @@ "k0_B": -3.5, "kinf_A": 3.0e-11, "Fc": 0.5, - "N": 0, "reactants": { "OH": { }, "MPAN": { } @@ -3693,8 +3692,8 @@ "k0_B": -5.6, "k0_C": -14000, "kinf_A": 1.03323e+17, + "kinf_B": -1.5, "kinf_C": -14000, - "N": 1.5, "reactants": { "PBZNIT": { } }, @@ -3741,7 +3740,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jterprd2->,jch3cho", + "MUSICA name": "jterprd2", "reactants": { "TERPROD2": { } }, @@ -3815,7 +3814,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jmek->,jacet", + "MUSICA name": "jmek", "reactants": { "MEK": { } }, @@ -3907,7 +3906,6 @@ "k0_A": 9.0e-32, "k0_B": -1.5, "kinf_A": 3.0e-11, - "N": 0, "reactants": { "NO": { }, "O": { } @@ -4014,7 +4012,7 @@ "k0_A": 1.9e-31, "k0_B": -3.4, "kinf_A": 4.0e-12, - "N": 0.3, + "kinf_B": -0.3, "reactants": { "NO2": { }, "HO2": { } @@ -4104,7 +4102,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jxylolooh->,jch3ooh", + "MUSICA name": "jxylolooh", "reactants": { "XYLOLOOH": { } }, @@ -4128,7 +4126,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jmekooh->,jch3ooh", + "MUSICA name": "jmekooh", "reactants": { "MEKOOH": { } }, @@ -4193,7 +4191,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jphenooh->,jch3ooh", + "MUSICA name": "jphenooh", "reactants": { "PHENOOH": { } }, @@ -4299,7 +4297,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa4_a1->,.0004*jno2", + "MUSICA name": "jsoa4_a1", "reactants": { "soa4_a1": { } }, @@ -4346,7 +4344,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbigald3->,.20*jno2", + "MUSICA name": "jbigald3", "reactants": { "BIGALD3": { } }, @@ -4407,7 +4405,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbepomuc->,.10*jno2", + "MUSICA name": "jbepomuc", "reactants": { "BEPOMUC": { } }, @@ -4638,8 +4636,8 @@ "k0_B": -3.4, "k0_C": -10900, "kinf_A": 1.90476e+15, + "kinf_B": -0.3, "kinf_C": -10900, - "N": 0.3, "reactants": { "HO2NO2": { } }, @@ -4700,7 +4698,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jnc4cho->,jch2o_a", + "MUSICA name": "jnc4cho", "reactants": { "NC4CHO": { } }, @@ -4733,7 +4731,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jhonitr->,jch2o_a", + "MUSICA name": "jhonitr", "reactants": { "HONITR": { } }, @@ -4764,7 +4762,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jxooh->,jch3ooh", + "MUSICA name": "jxooh", "reactants": { "XOOH": { } }, @@ -4830,7 +4828,7 @@ "k0_A": 5.2e-31, "k0_B": -3.2, "kinf_A": 6.9e-12, - "N": 2.9, + "kinf_B": -2.9, "reactants": { "BRO": { }, "NO2": { } @@ -4855,6 +4853,7 @@ "k0_A": 1.6e-29, "k0_B": -3.3, "kinf_A": 3.1e-10, + "kinf_B": -1.0, "reactants": { "C2H4": { }, "CL": { } @@ -4921,7 +4920,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jch3co3h->,0.28*jh2o2", + "MUSICA name": "jch3co3h", "reactants": { "CH3COOOH": { } }, @@ -5018,7 +5017,6 @@ "k0_A": 6.9e-31, "k0_B": -1, "kinf_A": 2.6e-11, - "N": 0, "reactants": { "OH": { "qty": 2 } }, @@ -5028,7 +5026,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jonitr->,jch3cho", + "MUSICA name": "jonitr", "reactants": { "ONITR": { } }, @@ -5038,7 +5036,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jterpooh->,jch3ooh", + "MUSICA name": "jterpooh", "reactants": { "TERPOOH": { } }, @@ -5084,12 +5082,7 @@ } }, { - "type": "TROE", - "k0_A": 3.57e-43, - "k0_C": 7810, - "kinf_A": 3.09091e-12, - "kinf_C": 350, - "Fc": 1, + "type": "USER_DEFINED", "reactants": { "OH": { }, "DMS": { } @@ -5097,7 +5090,8 @@ "products": { "SO2": { "yield": 0.5 }, "HO2": { "yield": 0.5 } - } + }, + "MUSICA name": "usr_DMS_OH" }, { "type": "ARRHENIUS", @@ -5232,7 +5226,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jbigald1->,.14*jno2", + "MUSICA name": "jbigald1", "reactants": { "BIGALD1": { } }, @@ -5270,7 +5264,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jc2h5ooh->,jch3ooh", + "MUSICA name": "jc2h5ooh", "reactants": { "C2H5OOH": { } }, @@ -5418,7 +5412,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jterprd1->,jch3cho", + "MUSICA name": "jterprd1", "reactants": { "TERPROD1": { } }, @@ -5430,7 +5424,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jmpan->,jpan", + "MUSICA name": "jmpan", "reactants": { "MPAN": { } }, @@ -5492,7 +5486,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa3_a1->,.0004*jno2", + "MUSICA name": "jsoa3_a1", "reactants": { "soa3_a1": { } }, @@ -5502,7 +5496,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa4_a2->,.0004*jno2", + "MUSICA name": "jsoa4_a2", "reactants": { "soa4_a2": { } }, @@ -5665,7 +5659,7 @@ "k0_A": 9.7e-29, "k0_B": -5.6, "kinf_A": 9.3e-12, - "N": 1.5, + "kinf_B": -1.5, "reactants": { "DICARBO2": { }, "NO2": { } @@ -5758,10 +5752,10 @@ { "type": "ARRHENIUS", "A": 8.5e-41, - "Ea": -9.02944e-20, + "C": 6540, "reactants": { - "H2O": { }, - "SO3": { } + "H2O": { "qty": 2 }, + "SO3": {} }, "products": { "H2SO4": { }, @@ -5770,7 +5764,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jtolooh->,jch3ooh", + "MUSICA name": "jtolooh", "reactants": { "TOLOOH": { } }, @@ -5908,7 +5902,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa2_a1->,.0004*jno2", + "MUSICA name": "jsoa2_a1", "reactants": { "soa2_a1": { } }, @@ -6417,7 +6411,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jo2_a=userdefined,", + "MUSICA name": "jo2_a", "reactants": { "O2": { } }, @@ -6489,8 +6483,8 @@ "k0_B": -5.6, "k0_C": -14000, "kinf_A": 1.03323e+17, + "kinf_B": -1.5, "kinf_C": -14000, - "N": 1.5, "reactants": { "MPAN": { } }, @@ -6611,7 +6605,7 @@ }, { "type": "PHOTOLYSIS", - "MUSICA name": "jsoa1_a2->,.0004*jno2", + "MUSICA name": "jsoa1_a2", "reactants": { "soa1_a2": { } }, @@ -6717,7 +6711,7 @@ "k0_A": 2.5e-31, "k0_B": -1.8, "kinf_A": 2.2e-11, - "N": 0.7, + "kinf_B": -0.7, "reactants": { "NO2": { }, "O": { } @@ -6780,7 +6774,7 @@ "k0_A": 2.4e-30, "k0_B": -3, "kinf_A": 1.6e-12, - "N": -0.1, + "kinf_B": 0.1, "reactants": { "NO3": { }, "NO2": { } @@ -7094,7 +7088,7 @@ }, { "type": "USER_DEFINED", - "MUSICA name": "usr_CO_OH; non-standard algorithm", + "__comments": "k_co_oh_jpl19; non-standard algorithm", "reactants": { "OH": { }, "CO": { } @@ -7103,7 +7097,7 @@ "CO2": { }, "HO2": { } }, - "MUSICA name": "k_co_oh_jpl19" + "MUSICA name": "usr_CO_OH" }, { "type": "SURFACE", @@ -7127,4 +7121,4 @@ } } ] -} +} \ No newline at end of file diff --git a/examples/configs/carbon_bond_5/initial_conditions.csv b/examples/configs/carbon_bond_5/initial_conditions.csv index 35bb46874..005df9960 100644 --- a/examples/configs/carbon_bond_5/initial_conditions.csv +++ b/examples/configs/carbon_bond_5/initial_conditions.csv @@ -1,38 +1,353 @@ +CONC.O2,8.90261411 +CONC.N2,33.1007671 +CONC.ALKNIT,8.72E-10 +CONC.BZOOH,2.68E-11 +CONC.C6H5OOH,4.20E-10 +CONC.COF2,5.24E-11 +CONC.COFCL,7.92E-12 +CONC.HF,1.81E-11 +CONC.F,3.89E-28 +CONC.BENZO2,2.09E-13 +CONC.BZOO,8.81E-13 +CONC.PAN,4.25E-08 +CONC.MVK,2.61E-08 +CONC.MACROOH,1.21E-09 +CONC.soa1_a1,1.0E-15 +CONC.soa1_a2,1.0E-15 +CONC.soa2_a1,1.0E-15 +CONC.soa2_a2,1.0E-15 +CONC.soa3_a1,1.0E-15 +CONC.soa3_a2,1.0E-15 +CONC.soa4_a1,1.0E-15 +CONC.soa4_a2,1.0E-15 +CONC.soa5_a1,1.0E-15 +CONC.soa5_a2,1.0E-15 +CONC.SOAG0,3.33E-11 +CONC.SOAG1,2.14E-10 +CONC.SOAG2,1.40E-09 +CONC.SOAG3,4.27E-09 +CONC.SOAG4,2.25E-08 +CONC.ISOPNITB,1.05E-09 +CONC.SO3,1.75E-17 +CONC.OCS,1.96E-08 +CONC.SO,1.22E-20 +CONC.S,1.10E-26 +CONC.H,9.67E-18 +CONC.MEK,1.04E-08 +CONC.MTERP,1.09E-09 +CONC.N2O5,1.00E-11 +CONC.HCN,1.90E-08 +CONC.SVOC,1.98E-10 +CONC.ISOPNO3,5.35E-13 +CONC.RO2,7.13E-12 +CONC.PHENO2,2.54E-13 +CONC.IVOC,2.06E-09 +CONC.TERPNIT,8.28E-10 +CONC.HNO3,1.04E-07 +CONC.ACBZO2,3.54E-13 +CONC.CH3COOOH,7.19E-09 +CONC.SO2,6.15E-08 +CONC.CH4,7.03E-05 +CONC.CH3CL,2.07E-08 +CONC.CH3CO3,8.32E-11 +CONC.C6H5O2,6.23E-12 +CONC.TERPROD1,1.68E-09 +CONC.HYAC,4.88E-08 +CONC.HPALD,3.56E-10 +CONC.H2O,7.46E-01 +CONC.NO2,6.37E-08 +CONC.EOOH,3.56E-09 +CONC.NTERPOOH,9.37E-11 +CONC.CCL4,2.96E-09 +CONC.CF2CLBR,1.24E-10 +CONC.CF3BR,1.26E-10 +CONC.CFC11,8.50E-09 +CONC.CFC113,2.66E-09 +CONC.CFC114,6.19E-10 +CONC.CFC115,3.29E-10 +CONC.CFC12,1.92E-08 +CONC.CH2BR2,4.32E-11 +CONC.CH3BR,2.37E-10 +CONC.CH3CCL3,5.55E-11 +CONC.NO,1.47E-08 +CONC.BR,3.67E-14 +CONC.BRCL,9.18E-19 +CONC.BRO,1.95E-13 +CONC.BRONO2,1.22E-11 +CONC.CL,1.07E-15 +CONC.CL2,1.01E-16 +CONC.CL2O2,1.89E-23 +CONC.CLO,8.06E-14 +CONC.CLONO2,4.09E-11 +CONC.HCOOH,8.79E-09 +CONC.HBR,4.48E-11 +CONC.HOBR,1.65E-12 +CONC.HOCL,2.11E-12 +CONC.BIGENE,6.59E-10 +CONC.C2H4,2.51E-08 +CONC.C2H5O2,1.55E-12 +CONC.CH3COCHO,1.95E-08 +CONC.CH3COCH3,7.37E-08 +CONC.O,2.85E-14 +CONC.OCLO,7.68E-19 +CONC.O1D,1.53E-19 +CONC.PHENO,1.00E-12 +CONC.HCFC141B,9.29E-10 +CONC.HCFC142B,8.24E-10 +CONC.HCFC22,9.07E-09 +CONC.DMS,1.74E-10 +CONC.C2H5OH,2.75E-08 +CONC.HCL,8.17E-10 +CONC.BEPOMUC,5.40E-12 +CONC.CHBR3,3.87E-11 +CONC.H2402,1.52E-11 +CONC.CO2,1.56E-02 +CONC.BZALD,4.70E-10 +CONC.BENZENE,2.67E-09 +CONC.C3H7O2,2.31E-12 +CONC.CH3O2,3.34E-10 +CONC.BCARY,5.04E-12 +CONC.BIGALD,1.33E-11 +CONC.BIGALD2,6.58E-11 +CONC.BIGALD3,6.83E-11 +CONC.BIGALD4,7.34E-10 +CONC.BIGALK,1.90E-08 +CONC.H2O2,7.36E-08 +CONC.C2H5OOH,7.99E-11 +CONC.C2H6,2.93E-08 +CONC.C3H8,9.70E-09 +CONC.C3H6,2.42E-09 +CONC.CH2O,2.44E-07 +CONC.CH3CN,4.66E-09 +CONC.C2H2,2.13E-08 +CONC.CH3OH,2.74E-07 +CONC.CH3OOH,1.69E-08 +CONC.CRESOL,1.06E-10 +CONC.ENEO2,7.98E-12 +CONC.MACRO2,1.09E-10 +CONC.ISOPAO2,1.39E-10 +CONC.MALO2,1.07E-13 +CONC.ISOPBO2,9.08E-11 +CONC.MCO3,9.23E-12 +CONC.MDIALO2,1.61E-13 +CONC.MEKO2,2.81E-12 +CONC.EO2,4.04E-11 +CONC.EO,9.93E-18 +CONC.GLYOXAL,1.25E-08 +CONC.MPAN,3.11E-09 +CONC.NC4CH2OH,7.30E-13 +CONC.ISOPOOH,4.68E-09 +CONC.GLYALD,4.58E-08 +CONC.HO2,1.69E-09 +CONC.HOCH2OO,1.07E-13 +CONC.H2,1.93E-05 +CONC.HYDRALD,1.49E-08 +CONC.ISOP,1.23E-08 +CONC.NTERPO2,9.18E-13 +CONC.TOLO2,3.23E-12 +CONC.TERP2O2,2.15E-11 +CONC.XYLENO2,4.98E-12 +CONC.TERPO2,1.29E-11 +CONC.XYLOLO2,4.62E-13 +CONC.PBZNIT,1.78E-10 +CONC.XYLENES,2.82E-09 +CONC.PO2,1.46E-11 +CONC.TOLUENE,4.88E-09 +CONC.XO2,1.12E-10 +CONC.XOOH,7.93E-09 +CONC.TERPROD2,3.81E-09 +CONC.MEKOOH,6.15E-11 +CONC.MACR,1.06E-08 +CONC.HONITR,6.79E-09 +CONC.ISOPNITA,1.62E-09 +CONC.ISOPNOOH,9.16E-11 +CONC.IEPOX,5.84E-09 +CONC.ONITR,2.67E-09 +CONC.H2SO4,2.27E-11 +CONC.N2O,1.27E-05 +CONC.NO3,9.32E-12 +CONC.OH,2.52E-11 +CONC.PHENOOH,9.15E-12 +CONC.PHENOL,9.49E-11 +CONC.XYLOL,9.92E-11 +CONC.ROOH,4.40E-10 +CONC.O3,2.49E-06 +CONC.TERPOOH,2.46E-10 +CONC.TOLOOH,9.69E-11 +CONC.XYLENOOH,1.50E-10 +CONC.XYLOLOOH,1.31E-11 +CONC.CO,3.25E-06 +CONC.CH3COOH,8.20E-09 +CONC.CH3CHO,2.51E-08 +CONC.BIGALD1,6.47E-11 +CONC.ALKOOH,3.79E-10 +CONC.DICARBO2,1.55E-13 +CONC.BENZOOH,5.78E-12 +CONC.ALKO2,1.39E-11 +CONC.HO2NO2,7.99E-10 +CONC.C3H7OOH,7.87E-11 +CONC.NH3,7.59E-08 +CONC.TERP2OOH,5.96E-10 +CONC.POOH,9.24E-10 +CONC.NOA,5.46E-09 +CONC.NC4CHO,3.67E-11 +CONC.TEPOMUC,2.21E-10 +CONC.NH4,1.0E-15 +CONC.SF6,1.0E-15 +PHOTO.jacet,1.25E-06 +PHOTO.jalknit,5.95E-06 +PHOTO.jalkooh,5.95E-06 +PHOTO.jbenzooh,5.95E-06 +PHOTO.jbepomuc,1.01E-03 +PHOTO.jbigald,2.01E-03 +PHOTO.jbigald1,1.41E-03 +PHOTO.jbigald2,2.01E-03 +PHOTO.jbigald3,2.01E-03 +PHOTO.jbigald4,6.03E-05 +PHOTO.jbrcl,1.12E-02 +PHOTO.jbro,4.07E-02 +PHOTO.jbrono2_a,1.27E-03 +PHOTO.jbrono2_b,2.24E-04 +PHOTO.jbzooh,5.95E-06 +PHOTO.jc2h5ooh,5.95E-06 +PHOTO.jc3h7ooh,5.95E-06 +PHOTO.jc6h5ooh,5.95E-06 +PHOTO.jccl4,1.28E-23 +PHOTO.jcf2cl2,2.50E-24 +PHOTO.jcf2clbr,6.95E-09 +PHOTO.jcf3br,1.45E-12 +PHOTO.jcfc113,2.60E-24 +PHOTO.jcfc114,2.09E-25 +PHOTO.jcfc115,1.23E-26 +PHOTO.jcfcl3,1.38E-23 +PHOTO.jch2br2,1.15E-09 +PHOTO.jch2o_a,3.47E-05 +PHOTO.jch2o_b,4.92E-05 +PHOTO.jch3br,9.35E-16 +PHOTO.jch3ccl3,1.79E-23 +PHOTO.jch3cho,7.18E-06 +PHOTO.jch3cl,4.68E-25 +PHOTO.jch3co3h,2.23E-06 +PHOTO.jch3ooh,5.95E-06 +PHOTO.jch4_a,0.00E+00 +PHOTO.jch4_b,0.00E+00 +PHOTO.jchbr3,1.81E-06 +PHOTO.jcl2,2.52E-03 +PHOTO.jcl2o2,1.98E-03 +PHOTO.jclo,2.40E-04 +PHOTO.jclono2_a,4.59E-05 +PHOTO.jclono2_b,1.00E-05 +PHOTO.jco2,5.28E-30 +PHOTO.jcof2,3.30E-25 +PHOTO.jcofcl,2.30E-24 +PHOTO.jeooh,5.95E-06 +PHOTO.jglyald,6.18E-06 +PHOTO.jglyoxal,1.40E-04 +PHOTO.jh2402,3.68E-09 +PHOTO.jh2o2,7.98E-06 +PHOTO.jh2o_a,8.96E-31 +PHOTO.jh2o_b,0.00E+00 +PHOTO.jh2o_c,0.00E+00 +PHOTO.jh2so4,3.10E-10 +PHOTO.jhbr,2.55E-23 +PHOTO.jhcfc141b,4.36E-24 +PHOTO.jhcfc142b,3.98E-26 +PHOTO.jhcfc22,8.55E-27 +PHOTO.jhcl,6.59E-25 +PHOTO.jhf,0.00E+00 +PHOTO.jhno3,8.54E-07 +PHOTO.jho2no2_a,1.71E-06 +PHOTO.jho2no2_b,1.72E-05 +PHOTO.jhobr,2.37E-03 +PHOTO.jhocl,3.04E-04 +PHOTO.jhonitr,3.47E-05 +PHOTO.jhpald,6.03E-05 +PHOTO.jhyac,2.47E-06 +PHOTO.jisopnooh,5.95E-06 +PHOTO.jisopooh,5.95E-06 +PHOTO.jmacr_a,2.77E-06 +PHOTO.jmacr_b,2.77E-06 +PHOTO.jmek,1.25E-06 +PHOTO.jmekooh,5.95E-06 +PHOTO.jmgly,1.40E-04 +PHOTO.jmpan,9.69E-07 +PHOTO.jmvk,5.20E-06 +PHOTO.jn2o,9.25E-25 +PHOTO.jn2o5_a,5.54E-05 +PHOTO.jn2o5_b,3.76E-09 +PHOTO.jnc4cho,3.47E-05 +PHOTO.jno,1.0e-8 +PHOTO.jno2,1.01E-02 +PHOTO.jno3_a,1.99E-01 +PHOTO.jno3_b,1.24E-02 +PHOTO.jnoa,3.47E-05 +PHOTO.jnterpooh,5.95E-06 +PHOTO.jo2_a,0.00E+00 +PHOTO.jo2_b,1.40E-28 +PHOTO.jo3_a,5.13E-05 +PHOTO.jo3_b,4.49E-04 +PHOTO.joclo,7.26E-02 +PHOTO.jocs,6.18E-12 +PHOTO.jonitr,7.18E-06 +PHOTO.jpan,9.69E-07 +PHOTO.jphenooh,5.95E-06 +PHOTO.jpooh,5.95E-06 +PHOTO.jrooh,5.95E-06 +PHOTO.jso,2.58E-22 +PHOTO.jso2,1.18E-22 +PHOTO.jso3,2.36E-07 +PHOTO.jtepomuc,1.01E-03 +PHOTO.jterp2ooh,5.95E-06 +PHOTO.jterpnit,5.95E-06 +PHOTO.jterpooh,5.95E-06 +PHOTO.jterprd1,7.18E-06 +PHOTO.jterprd2,7.18E-06 +PHOTO.jtolooh,5.95E-06 +PHOTO.jxooh,5.95E-06 +PHOTO.jxylenooh,5.95E-06 +PHOTO.jxylolooh,5.95E-06 +PHOTO.jsf6,1.0e-20 +PHOTO.jsoa1_a1,1.0e-20 +PHOTO.jsoa1_a2,1.0e-20 +PHOTO.jsoa2_a1,1.0e-20 +PHOTO.jsoa2_a2,1.0e-20 +PHOTO.jsoa3_a1,1.0e-20 +PHOTO.jsoa3_a2,1.0e-20 +PHOTO.jsoa4_a1,1.0e-20 +PHOTO.jsoa4_a2,1.0e-20 +PHOTO.jsoa5_a1,1.0e-20 +PHOTO.jsoa5_a2,1.0e-20 +USER.het1,1.0E-20 +USER.het2,1.0E-20 +USER.het3,1.0E-20 +USER.het4,6.022140760000E-03 +USER.het5,6.022140760000E-03 +USER.het6,6.022140760000E-03 +USER.het7,1.0E-20 +USER.het8,1.0E-20 +USER.het9,6.022140760000E-03 +USER.het10,6.022140760000E-03 +USER.het11,1.0E-20 +USER.het12,1.0E-20 +USER.het13,1.0E-20 +USER.het14,1.0E-20 +USER.het15,6.022140760000E-03 +USER.het16,6.022140760000E-03 +USER.het17,6.022140760000E-03 +SURF.usr_NO2_aer,2.820947917738780E-08,1.0e11 +SURF.usr_HO2_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NO3_aer,2.820947917738780E-08,1.0e11 +SURF.usr_ISOPNITA_aer,2.820947917738780E-08,1.0e11 +SURF.usr_ISOPNITB_aer,2.820947917738780E-08,1.0e11 +SURF.usr_GLYOXAL_aer,2.820947917738780E-08,1.0e11 +SURF.usr_ONITR_aer,2.820947917738780E-08,1.0e11 +SURF.usr_N2O5_aer,2.820947917738780E-08,1.0e11 +SURF.usr_HONITR_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NC4CHO_aer,2.820947917738780E-08,1.0e11 +SURF.usr_TERPNIT_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NTERPOOH_aer,2.820947917738780E-08,1.0e11 +SURF.usr_NC4CH2OH_aer,2.820947917738780E-08,1.0e11 ENV.temperature,287.45 ENV.pressure,101319.9 -ENV.air_density,1e-6 -ENV.time_step,500 -EMIS.NO,1.44e-10 -EMIS.NO2,7.56e-12 -EMIS.CO,1.9600000000000003e-09 -EMIS.SO2,1.06e-09 -EMIS.FORM,1.02e-11 -EMIS.MEOH,5.920000000000001e-13 -EMIS.ALD2,4.25e-12 -EMIS.PAR,4.27e-10 -EMIS.ETH,4.62e-11 -EMIS.OLE,1.49e-11 -EMIS.IOLE,1.49e-11 -EMIS.TOL,1.53e-11 -EMIS.XYL,1.4e-11 -EMIS.ISOP,6.03e-12 -PHOTO.NO2,0.00477 -PHOTO.O3->O1D,2.26e-06 -PHOTO.O3->O3P,0.00025299999999999997 -PHOTO.NO3->NO2,0.11699999999999999 -PHOTO.NO3->NO,0.0144 -PHOTO.HONO,0.000918 -PHOTO.H2O2,2.59e-06 -PHOTO.PNA,1.89e-06 -PHOTO.HNO3,8.61e-08 -PHOTO.NTR,4.77e-07 -PHOTO.ROOH,1.81e-06 -PHOTO.MEPX,1.81e-06 -PHOTO.FORM->HO2,7.93e-06 -PHOTO.FORM->CO,2.2e-05 -PHOTO.ALD2,2.2e-06 -PHOTO.PACD,1.81e-06 -PHOTO.ALDX,2.2e-06 -PHOTO.OPEN,0.0006450000000000001 -PHOTO.MGLY,7.64e-05 -PHOTO.ISPD,1.98e-09 \ No newline at end of file +ENV.air_density,42.3934005 +ENV.time_step,60.0 \ No newline at end of file diff --git a/examples/configs/carbon_bond_5/species.json b/examples/configs/carbon_bond_5/species.json index d96ac8098..94c416755 100644 --- a/examples/configs/carbon_bond_5/species.json +++ b/examples/configs/carbon_bond_5/species.json @@ -1 +1,272 @@ -{"camp-data": [{"name": "AACD", "type": "CHEM_SPEC"}, {"name": "ALD2", "type": "CHEM_SPEC"}, {"name": "ALDX", "type": "CHEM_SPEC"}, {"name": "BENZENE", "type": "CHEM_SPEC"}, {"name": "BENZRO2", "type": "CHEM_SPEC"}, {"name": "C2O3", "type": "CHEM_SPEC"}, {"name": "CH4", "type": "CHEM_SPEC"}, {"name": "CL", "type": "CHEM_SPEC"}, {"name": "CL2", "type": "CHEM_SPEC"}, {"name": "CLO", "type": "CHEM_SPEC"}, {"name": "CO", "type": "CHEM_SPEC"}, {"name": "CRES", "type": "CHEM_SPEC"}, {"name": "CRO", "type": "CHEM_SPEC"}, {"name": "CXO3", "type": "CHEM_SPEC"}, {"name": "ETH", "type": "CHEM_SPEC"}, {"name": "ETHA", "type": "CHEM_SPEC"}, {"name": "ETOH", "type": "CHEM_SPEC"}, {"name": "FACD", "type": "CHEM_SPEC"}, {"name": "FMCL", "type": "CHEM_SPEC"}, {"name": "FORM", "type": "CHEM_SPEC"}, {"name": "H2", "type": "CHEM_SPEC"}, {"name": "H2O", "type": "CHEM_SPEC"}, {"name": "H2O2", "type": "CHEM_SPEC"}, {"name": "HCL", "type": "CHEM_SPEC"}, {"name": "HCO3", "type": "CHEM_SPEC"}, {"name": "HNO3", "type": "CHEM_SPEC"}, {"name": "HO2", "type": "CHEM_SPEC"}, {"name": "HOCL", "type": "CHEM_SPEC"}, {"name": "HONO", "type": "CHEM_SPEC"}, {"name": "IOLE", "type": "CHEM_SPEC"}, {"name": "ISOP", "type": "CHEM_SPEC"}, {"name": "ISPD", "type": "CHEM_SPEC"}, {"name": "M", "type": "CHEM_SPEC"}, {"name": "MEO2", "type": "CHEM_SPEC"}, {"name": "MEOH", "type": "CHEM_SPEC"}, {"name": "MEPX", "type": "CHEM_SPEC"}, {"name": "MGLY", "type": "CHEM_SPEC"}, {"name": "N2O5", "type": "CHEM_SPEC"}, {"name": "NO", "type": "CHEM_SPEC"}, {"name": "NO2", "type": "CHEM_SPEC"}, {"name": "NO3", "type": "CHEM_SPEC"}, {"name": "NTR", "type": "CHEM_SPEC"}, {"name": "O", "type": "CHEM_SPEC"}, {"name": "O1D", "type": "CHEM_SPEC"}, {"name": "O3", "type": "CHEM_SPEC"}, {"name": "OH", "type": "CHEM_SPEC"}, {"name": "O2", "type": "CHEM_SPEC"}, {"name": "OLE", "type": "CHEM_SPEC"}, {"name": "OPEN", "type": "CHEM_SPEC"}, {"name": "PACD", "type": "CHEM_SPEC"}, {"name": "PAN", "type": "CHEM_SPEC"}, {"name": "PANX", "type": "CHEM_SPEC"}, {"name": "PAR", "type": "CHEM_SPEC"}, {"name": "PNA", "type": "CHEM_SPEC"}, {"name": "ROOH", "type": "CHEM_SPEC"}, {"name": "ROR", "type": "CHEM_SPEC"}, {"name": "SESQ", "type": "CHEM_SPEC"}, {"name": "SO2", "type": "CHEM_SPEC"}, {"name": "SULF", "type": "CHEM_SPEC"}, {"name": "TERP", "type": "CHEM_SPEC"}, {"name": "TO2", "type": "CHEM_SPEC"}, {"name": "TOL", "type": "CHEM_SPEC"}, {"name": "TOLRO2", "type": "CHEM_SPEC"}, {"name": "XO2", "type": "CHEM_SPEC"}, {"name": "XO2N", "type": "CHEM_SPEC"}, {"name": "XYL", "type": "CHEM_SPEC"}, {"name": "XYLRO2", "type": "CHEM_SPEC"}]} \ No newline at end of file +{ + "camp-data": [ + { + "name": "AACD", + "type": "CHEM_SPEC" + }, + { + "name": "ALD2", + "type": "CHEM_SPEC" + }, + { + "name": "ALDX", + "type": "CHEM_SPEC" + }, + { + "name": "BENZENE", + "type": "CHEM_SPEC" + }, + { + "name": "BENZRO2", + "type": "CHEM_SPEC" + }, + { + "name": "C2O3", + "type": "CHEM_SPEC" + }, + { + "name": "CH4", + "type": "CHEM_SPEC" + }, + { + "name": "CL", + "type": "CHEM_SPEC" + }, + { + "name": "CL2", + "type": "CHEM_SPEC" + }, + { + "name": "CLO", + "type": "CHEM_SPEC" + }, + { + "name": "CO", + "type": "CHEM_SPEC" + }, + { + "name": "CRES", + "type": "CHEM_SPEC" + }, + { + "name": "CRO", + "type": "CHEM_SPEC" + }, + { + "name": "CXO3", + "type": "CHEM_SPEC" + }, + { + "name": "ETH", + "type": "CHEM_SPEC" + }, + { + "name": "ETHA", + "type": "CHEM_SPEC" + }, + { + "name": "ETOH", + "type": "CHEM_SPEC" + }, + { + "name": "FACD", + "type": "CHEM_SPEC" + }, + { + "name": "FMCL", + "type": "CHEM_SPEC" + }, + { + "name": "FORM", + "type": "CHEM_SPEC" + }, + { + "name": "H2", + "type": "CHEM_SPEC" + }, + { + "name": "H2O", + "type": "CHEM_SPEC" + }, + { + "name": "H2O2", + "type": "CHEM_SPEC" + }, + { + "name": "HCL", + "type": "CHEM_SPEC" + }, + { + "name": "HCO3", + "type": "CHEM_SPEC" + }, + { + "name": "HNO3", + "type": "CHEM_SPEC" + }, + { + "name": "HO2", + "type": "CHEM_SPEC" + }, + { + "name": "HOCL", + "type": "CHEM_SPEC" + }, + { + "name": "HONO", + "type": "CHEM_SPEC" + }, + { + "name": "IOLE", + "type": "CHEM_SPEC" + }, + { + "name": "ISOP", + "type": "CHEM_SPEC" + }, + { + "name": "ISPD", + "type": "CHEM_SPEC" + }, + { + "name": "M", + "type": "CHEM_SPEC" + }, + { + "name": "MEO2", + "type": "CHEM_SPEC" + }, + { + "name": "MEOH", + "type": "CHEM_SPEC" + }, + { + "name": "MEPX", + "type": "CHEM_SPEC" + }, + { + "name": "MGLY", + "type": "CHEM_SPEC" + }, + { + "name": "N2O5", + "type": "CHEM_SPEC" + }, + { + "name": "NO", + "type": "CHEM_SPEC" + }, + { + "name": "NO2", + "type": "CHEM_SPEC" + }, + { + "name": "NO3", + "type": "CHEM_SPEC" + }, + { + "name": "NTR", + "type": "CHEM_SPEC" + }, + { + "name": "O", + "type": "CHEM_SPEC" + }, + { + "name": "O1D", + "type": "CHEM_SPEC" + }, + { + "name": "O3", + "type": "CHEM_SPEC" + }, + { + "name": "OH", + "type": "CHEM_SPEC" + }, + { + "name": "O2", + "type": "CHEM_SPEC" + }, + { + "name": "OLE", + "type": "CHEM_SPEC" + }, + { + "name": "OPEN", + "type": "CHEM_SPEC" + }, + { + "name": "PACD", + "type": "CHEM_SPEC" + }, + { + "name": "PAN", + "type": "CHEM_SPEC" + }, + { + "name": "PANX", + "type": "CHEM_SPEC" + }, + { + "name": "PAR", + "type": "CHEM_SPEC" + }, + { + "name": "PNA", + "type": "CHEM_SPEC" + }, + { + "name": "ROOH", + "type": "CHEM_SPEC" + }, + { + "name": "ROR", + "type": "CHEM_SPEC" + }, + { + "name": "SESQ", + "type": "CHEM_SPEC" + }, + { + "name": "SO2", + "type": "CHEM_SPEC" + }, + { + "name": "SULF", + "type": "CHEM_SPEC" + }, + { + "name": "TERP", + "type": "CHEM_SPEC" + }, + { + "name": "TO2", + "type": "CHEM_SPEC" + }, + { + "name": "TOL", + "type": "CHEM_SPEC" + }, + { + "name": "TOLRO2", + "type": "CHEM_SPEC" + }, + { + "name": "XO2", + "type": "CHEM_SPEC" + }, + { + "name": "XO2N", + "type": "CHEM_SPEC" + }, + { + "name": "XYL", + "type": "CHEM_SPEC" + }, + { + "name": "XYLRO2", + "type": "CHEM_SPEC" + } + ] +} \ No newline at end of file diff --git a/examples/example.cpp b/examples/example.cpp index ae7c93cf1..6f5c31fb9 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -183,7 +183,7 @@ InitialConditions readCSVToMap(const std::string& filename) int main(const int argc, const char *argv[]) { - if (argc < 2) + if (argc < 3) { std::cout << " path Path to the folder holding the configuration data" << std::endl; std::cout << " initial condition csv file contanining initial condtions" << std::endl; @@ -219,7 +219,9 @@ int main(const int argc, const char *argv[]) auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; - RosenbrockSolver<> solver{ chemical_system, reactions, RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; + auto params = RosenbrockSolverParameters::three_stage_rosenbrock_parameters(); + params.relative_tolerance_ = 0.1; + RosenbrockSolver<> solver{ chemical_system, reactions, params}; State state = solver.GetState(); diff --git a/include/micm/solver/state.hpp b/include/micm/solver/state.hpp index 06a2e00e7..87d1d204f 100644 --- a/include/micm/solver/state.hpp +++ b/include/micm/solver/state.hpp @@ -87,4 +87,4 @@ namespace micm } // namespace micm -#include "state.inl" \ No newline at end of file +#include "state.inl" diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index 750b8baaf..f0b54d121 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -123,7 +123,7 @@ namespace micm std::cout << std::setw(5) << "time"; - for(auto& species : variable_names_) + for(const auto& [species, index] : variable_map_) { std::cout << "," << std::setw(width) << species; } @@ -144,9 +144,9 @@ namespace micm std::cout << std::setw(5) << time << std::flush; - for(auto& species : variable_names_) + for(const auto& [species, index] : variable_map_) { - std::cout << std::scientific << "," << std::setw(width) << std::setprecision(2) << variables_[0][variable_map_[species]]; + std::cout << std::scientific << "," << std::setw(width) << std::setprecision(2) << variables_[0][index]; } std::cout << std::endl; std::cout.copyfmt(oldState); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e24e29012..cfc56e8ef 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,7 +4,4 @@ set(CMAKE_CXX_CLANG_TIDY "") add_subdirectory(unit) add_subdirectory(regression) add_subdirectory(integration) -if(ENABLE_JSON) -add_subdirectory(examples) -endif() add_subdirectory(tutorial) diff --git a/test/examples/CMakeLists.txt b/test/examples/CMakeLists.txt deleted file mode 100644 index b3d56dd63..000000000 --- a/test/examples/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -################################################################################ -# Build example tests - -create_standard_test(NAME Chapman SOURCES test_chapman.cpp) -create_standard_test(NAME carbon_bond_5 SOURCES test_carbon_bond_5.cpp) -create_standard_test(NAME robertson SOURCES test_robertson.cpp) -create_standard_test(NAME TS1 SOURCES test_TS1.cpp) diff --git a/test/examples/test_TS1.cpp b/test/examples/test_TS1.cpp deleted file mode 100644 index 4ce2e141c..000000000 --- a/test/examples/test_TS1.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, -// -// SPDX-License-Identifier: Apache-2.0 -// -// Test of the TS1 example mechanism (MZ327_TS1.2_20230307 in the Chemistry Cafe) -// -// This only tests that a solver can be built for this mechanism and that it -// can be run for a given set of initial conditions without solver errors. -// -// Comparisons with other chemistry solvers are included in -// https://github.com/NCAR/MUSICA-Performance-Comparison - -#include - -#include -#include - -TEST(Examples, TS1) -{ - micm::SolverConfig config; - std::string config_path = "./examples/configs/TS1"; - auto status = config.ReadAndParse(config_path); - EXPECT_EQ(status, micm::ConfigParseStatus::Success); - auto solver_params = config.GetSolverParams(); - micm::RosenbrockSolver<> solver{ solver_params.system_, - solver_params.processes_, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - micm::State state = solver.GetState(); - - const double temperature = 287.45; // K - const double pressure = 101319.9; // Pa - const double air_density = pressure / (8.3145 * temperature); // mol m-3 - - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - // TODO: Replace with CAM-Chem conditions when Derecho is back up - std::unordered_map> initial_concentrations = { - { "O2", { 0.21 * air_density } }, // mol m-3 - { "N2", { 0.78 * air_density } }, // mol m-3 - { "HNO3", { 1.0e-9 * air_density } }, // mol m-3 - { "SO2", { 1.0e-9 * air_density } }, // mol m-3 - { "CH4", { 1.8e-6 * air_density } }, // mol m-3 - { "H2O", { 1.0e-3 * air_density } }, // mol m-3 - { "NO2", { 1.0e-9 * air_density } }, // mol m-3 - { "NO", { 1.0e-9 * air_density } }, // mol m-3 - { "OH", { 1.0e-12 * air_density } }, // mol m-3 - { "CL2", { 1.0e-9 * air_density } }, // mol m-3 - { "DMS", { 1.0e-9 * air_density } }, // mol m-3 - { "CO2", { 0.385e-3 * air_density } }, // mol m-3 - { "H2O2", { 1.0e-9 * air_density } }, // mol m-3 - { "ISOP", { 1.0e-9 * air_density } }, // mol m-3 - { "O3", { 50.0e-9 * air_density } }, // mol m-3 - { "CO", { 1.0e-9 * air_density } }, // mol m-3 - { "NH3", { 1.0e-9 * air_density } }, // mol m-3 - { "M", { air_density } } // mol m-3 - }; - state.SetConcentrations(initial_concentrations); - - // std::cout << "custom rate parameters" << std::endl; - // for(auto &rc : state.custom_rate_parameter_map_) - // std::cout << " { \"" << rc.first << "\", { 1.0e-8 } }, // s-1" << std::endl; - - // TODO: Replace with CAM-Chem rate constants when Derecho is back up - std::unordered_map> custom_rate_constants = { - { "PHOTO.jacet", { 1.0e-8 } }, // s-1 - { "PHOTO.jalknit->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jalkooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jbenzooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jbepomuc->,.10*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jbigald->,0.2*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jbigald1->,.14*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jbigald2->,.20*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jbigald3->,.20*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jbigald4->,.006*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jbrcl", { 1.0e-8 } }, // s-1 - { "PHOTO.jbro", { 1.0e-8 } }, // s-1 - { "PHOTO.jbrono2_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jbrono2_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jbzooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jc2h5ooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jc3h7ooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jc6h5ooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jccl4", { 1.0e-8 } }, // s-1 - { "PHOTO.jcf2cl2", { 1.0e-8 } }, // s-1 - { "PHOTO.jcf2clbr", { 1.0e-8 } }, // s-1 - { "PHOTO.jcf3br", { 1.0e-8 } }, // s-1 - { "PHOTO.jcfc113", { 1.0e-8 } }, // s-1 - { "PHOTO.jcfc114", { 1.0e-8 } }, // s-1 - { "PHOTO.jcfc115", { 1.0e-8 } }, // s-1 - { "PHOTO.jcfcl3", { 1.0e-8 } }, // s-1 - { "PHOTO.jch2br2", { 1.0e-8 } }, // s-1 - { "PHOTO.jch2o_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jch2o_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jch3br", { 1.0e-8 } }, // s-1 - { "PHOTO.jch3ccl3", { 1.0e-8 } }, // s-1 - { "PHOTO.jch3cho", { 1.0e-8 } }, // s-1 - { "PHOTO.jch3cl", { 1.0e-8 } }, // s-1 - { "PHOTO.jch3co3h->,0.28*jh2o2", { 1.0e-8 } }, // s-1 - { "PHOTO.jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jch4_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jch4_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jchbr3", { 1.0e-8 } }, // s-1 - { "PHOTO.jcl2", { 1.0e-8 } }, // s-1 - { "PHOTO.jcl2o2", { 1.0e-8 } }, // s-1 - { "PHOTO.jclo", { 1.0e-8 } }, // s-1 - { "PHOTO.jclono2_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jclono2_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jco2", { 1.0e-8 } }, // s-1 - { "PHOTO.jcof2", { 1.0e-8 } }, // s-1 - { "PHOTO.jcofcl", { 1.0e-8 } }, // s-1 - { "PHOTO.jeooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jglyald", { 1.0e-8 } }, // s-1 - { "PHOTO.jglyoxal->,jmgly", { 1.0e-8 } }, // s-1 - { "PHOTO.jh2402", { 1.0e-8 } }, // s-1 - { "PHOTO.jh2o2", { 1.0e-8 } }, // s-1 - { "PHOTO.jh2o_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jh2o_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jh2o_c", { 1.0e-8 } }, // s-1 - { "PHOTO.jh2so4", { 1.0e-8 } }, // s-1 - { "PHOTO.jhbr", { 1.0e-8 } }, // s-1 - { "PHOTO.jhcfc141b", { 1.0e-8 } }, // s-1 - { "PHOTO.jhcfc142b", { 1.0e-8 } }, // s-1 - { "PHOTO.jhcfc22", { 1.0e-8 } }, // s-1 - { "PHOTO.jhcl", { 1.0e-8 } }, // s-1 - { "PHOTO.jhf", { 1.0e-8 } }, // s-1 - { "PHOTO.jhno3", { 1.0e-8 } }, // s-1 - { "PHOTO.jho2no2_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jho2no2_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jhobr", { 1.0e-8 } }, // s-1 - { "PHOTO.jhocl", { 1.0e-8 } }, // s-1 - { "PHOTO.jhonitr->,jch2o_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jhpald->,.006*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jhyac", { 1.0e-8 } }, // s-1 - { "PHOTO.jisopnooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jisopooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jmacr_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jmacr_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jmek->,jacet", { 1.0e-8 } }, // s-1 - { "PHOTO.jmekooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jmgly", { 1.0e-8 } }, // s-1 - { "PHOTO.jmpan->,jpan", { 1.0e-8 } }, // s-1 - { "PHOTO.jmvk", { 1.0e-8 } }, // s-1 - { "PHOTO.jn2o", { 1.0e-8 } }, // s-1 - { "PHOTO.jn2o5_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jn2o5_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jnc4cho->,jch2o_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jno3_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jno3_b", { 1.0e-8 } }, // s-1 - { "PHOTO.jno=userdefined,", { 1.0e-8 } }, // s-1 - { "PHOTO.jnoa->,jch2o_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jnterpooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jo2_a=userdefined,", { 1.0e-8 } }, // s-1 - { "PHOTO.jo2_b=userdefined,", { 1.0e-8 } }, // s-1 - { "PHOTO.jo3_a", { 1.0e-8 } }, // s-1 - { "PHOTO.jo3_b", { 1.0e-8 } }, // s-1 - { "PHOTO.joclo", { 1.0e-8 } }, // s-1 - { "PHOTO.jocs", { 1.0e-8 } }, // s-1 - { "PHOTO.jonitr->,jch3cho", { 1.0e-8 } }, // s-1 - { "PHOTO.jpan", { 1.0e-8 } }, // s-1 - { "PHOTO.jphenooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jpooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jrooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jsf6", { 1.0e-8 } }, // s-1 - { "PHOTO.jso", { 1.0e-8 } }, // s-1 - { "PHOTO.jso2", { 1.0e-8 } }, // s-1 - { "PHOTO.jso3", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa1_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa1_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa2_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa2_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa3_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa3_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa4_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa4_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa5_a1->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jsoa5_a2->,.0004*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jtepomuc->,.10*jno2", { 1.0e-8 } }, // s-1 - { "PHOTO.jterp2ooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jterpnit->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jterpooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jterprd1->,jch3cho", { 1.0e-8 } }, // s-1 - { "PHOTO.jterprd2->,jch3cho", { 1.0e-8 } }, // s-1 - { "PHOTO.jtolooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jxooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jxylenooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "PHOTO.jxylolooh->,jch3ooh", { 1.0e-8 } }, // s-1 - { "USER.het1", { 1.0e-8 } }, // s-1 - { "USER.het2", { 1.0e-8 } }, // s-1 - { "USER.het3", { 1.0e-8 } }, // s-1 - { "USER.het4", { 1.0e-8 } }, // s-1 - { "USER.het5", { 1.0e-8 } }, // s-1 - { "USER.het6", { 1.0e-8 } }, // s-1 - { "USER.het7", { 1.0e-8 } }, // s-1 - { "USER.het8", { 1.0e-8 } }, // s-1 - { "USER.het9", { 1.0e-8 } }, // s-1 - { "USER.het10", { 1.0e-8 } }, // s-1 - { "USER.het11", { 1.0e-8 } }, // s-1 - { "USER.het12", { 1.0e-8 } }, // s-1 - { "USER.het13", { 1.0e-8 } }, // s-1 - { "USER.het14", { 1.0e-8 } }, // s-1 - { "USER.het15", { 1.0e-8 } }, // s-1 - { "USER.het16", { 1.0e-8 } }, // s-1 - { "USER.het17", { 1.0e-8 } }, // s-1 - { "USER.k_co_oh_jpl19", { 1.0e-8 } } // s-1 - }; - state.SetCustomRateParameters(custom_rate_constants); - - double time_step = 150.0; // s - - auto result = solver.Solve(time_step, state); - - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); -} \ No newline at end of file diff --git a/test/examples/test_carbon_bond_5.cpp b/test/examples/test_carbon_bond_5.cpp deleted file mode 100644 index 1d7a76999..000000000 --- a/test/examples/test_carbon_bond_5.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, -// -// SPDX-License-Identifier: Apache-2.0 -// -// Test of the TS1 example mechanism (MZ327_TS1.2_20230307 in the Chemistry Cafe) -// -// This only tests that a solver can be built for this mechanism and that it -// can be run for a given set of initial conditions without solver errors. -// -// Comparisons with other chemistry solvers are included in -// https://github.com/NCAR/MUSICA-Performance-Comparison - -#include - -#include -#include - -TEST(Examples, carbon_bond_5) -{ - micm::SolverConfig config; - std::string config_path = "./examples/configs/carbon_bond_5"; - auto status = config.ReadAndParse(config_path); - EXPECT_EQ(status, micm::ConfigParseStatus::Success); - auto solver_params = config.GetSolverParams(); - micm::RosenbrockSolver<> solver{ solver_params.system_, - solver_params.processes_, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - micm::State state = solver.GetState(); - - const double temperature = 287.45; // K - const double pressure = 101319.9; // Pa - const double air_density = 1e6; // mol m-3 - - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - std::unordered_map> custom_rate_constants = { - { "EMIS.NO", { 1.44e-10 } }, - { "EMIS.NO2", { 7.56e-12 } }, - { "EMIS.CO", { 1.9600000000000003e-09 } }, - { "EMIS.SO2", { 1.06e-09 } }, - { "EMIS.FORM", { 1.02e-11 } }, - { "EMIS.MEOH", { 5.920000000000001e-13 } }, - { "EMIS.ALD2", { 4.25e-12 } }, - { "EMIS.PAR", { 4.27e-10 } }, - { "EMIS.ETH", { 4.62e-11 } }, - { "EMIS.OLE", { 1.49e-11 } }, - { "EMIS.IOLE", { 1.49e-11 } }, - { "EMIS.TOL", { 1.53e-11 } }, - { "EMIS.XYL", { 1.4e-11 } }, - { "EMIS.ISOP", { 6.03e-12 } }, - { "PHOTO.NO2", { 0.00477 } }, - { "PHOTO.O3->O1D", { 2.26e-06 } }, - { "PHOTO.O3->O3P", { 0.00025299999999999997 } }, - { "PHOTO.NO3->NO2", { 0.11699999999999999 } }, - { "PHOTO.NO3->NO", { 0.0144 } }, - { "PHOTO.HONO", { 0.000918 } }, - { "PHOTO.H2O2", { 2.59e-06 } }, - { "PHOTO.PNA", { 1.89e-06 } }, - { "PHOTO.HNO3", { 8.61e-08 } }, - { "PHOTO.NTR", { 4.77e-07 } }, - { "PHOTO.ROOH", { 1.81e-06 } }, - { "PHOTO.MEPX", { 1.81e-06 } }, - { "PHOTO.FORM->HO2", { 7.93e-06 } }, - { "PHOTO.FORM->CO", { 2.2e-05 } }, - { "PHOTO.ALD2", { 2.2e-06 } }, - { "PHOTO.PACD", { 1.81e-06 } }, - { "PHOTO.ALDX", { 2.2e-06 } }, - { "PHOTO.OPEN", { 0.0006450000000000001 } }, - { "PHOTO.MGLY", { 7.64e-05 } }, - { "PHOTO.ISPD", { 1.98e-09 } } - }; - - state.SetCustomRateParameters(custom_rate_constants); - - double time_step = 150.0; // s - - auto result = solver.Solve(time_step, state); - - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); -} diff --git a/test/examples/test_chapman.cpp b/test/examples/test_chapman.cpp deleted file mode 100644 index 2f873f8dd..000000000 --- a/test/examples/test_chapman.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include - -template -using SparseMatrixPolicy = micm::SparseMatrix; - -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(10) << "M" - << "," << std::setw(11) << "O2" - << "," << std::setw(11) << "O3" - << "," << std::setw(11) << "O" - << "," << std::setw(11) << "O1D" << std::endl; -} - -template class T, template class D> -void print_state(double time, micm::State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << "," << std::flush; - - std::cout << std::scientific << std::setw(10) << std::setprecision(3) << state.variables_[0][state.variable_map_["M"]] - << "," << std::setw(11) << std::setprecision(3) << state.variables_[0][state.variable_map_["O2"]] << "," - << std::setw(11) << std::setprecision(3) << state.variables_[0][state.variable_map_["O3"]] << "," - << std::setw(11) << std::setprecision(3) << state.variables_[0][state.variable_map_["O"]] << "," << std::setw(11) - << std::setprecision(3) << state.variables_[0][state.variable_map_["O1D"]] << std::endl; - - std::cout.copyfmt(oldState); -} - -int main(const int argc, const char* argv[]) -{ - micm::SolverConfig solver_config; - - // Read and parse the configure files - micm::ConfigParseStatus status = solver_config.ReadAndParse("./examples/configs/chapman"); - - if (status != micm::ConfigParseStatus::Success) - { - throw "Parsing failed"; - } - - micm::SolverParameters solver_params = solver_config.GetSolverParams(); - - auto& process_vector = solver_params.processes_; - - // Print reactions from reactions configuration - for (int i = 0; i < process_vector.size(); i++) - { - int n_reactants = process_vector[i].reactants_.size(); - for (int j = 0; j < n_reactants - 1; j++) - { - std::cout << process_vector[i].reactants_[j].name_ << " + "; - } - std::cout << process_vector[i].reactants_[n_reactants - 1].name_; - - std::cout << " --> "; - - int n_products = process_vector[i].products_.size(); - for (int j = 0; j < n_products - 1; j++) - { - std::cout << process_vector[i].products_[j].first.name_ << " + "; - } - std::cout << process_vector[i].products_[n_products - 1].first.name_; - - std::vector param_labels = process_vector[i].rate_constant_->CustomParameters(); - for (int k = 0; k < param_labels.size(); k++) - { - std::cout << " " << param_labels[k]; - } - - std::cout << std::endl; - } - - auto chemical_system = solver_params.system_; - auto reactions = solver_params.processes_; - - micm::RosenbrockSolver solver{ - chemical_system, reactions, micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() - }; - - micm::State state = solver.GetState(); - - state.conditions_[0].temperature_ = 227.0; // K - state.conditions_[0].pressure_ = 1200.0; // Pa - - state.SetCustomRateParameter("PHOTO.R1", 6.0e-11); // s^-1 j_O2 - state.SetCustomRateParameter("PHOTO.R3", 1.0e-3); // s^-1 j_O3 - state.SetCustomRateParameter("PHOTO.R5", 1.0e-3); // s^-1 j_O3 - - // Define initial concentrations from Seinfeld & Pandis 3e - // and convert to SI units - double N_Avogadro = 6.02214076e23; - // molecules cm-3 -> mol m-3, z = 30 km, S&P3e table 5.1 p. 121 - double n_M = 3.1e17 * 1.0e6 / N_Avogadro; - double n_O2 = 0.21 * n_M; // [O2] ~ 0.21 [M] - // typical [O3] mid-latitude z ~ 30 km - double n_O3 = 2.0e12 * 1.0e6 / N_Avogadro; - // [O] / [O3] ~ 3e-5, S&P3e p. 124 - - micm::Species M("M"); - micm::Species O2("O2"); - micm::Species O3("O3"); - micm::Species O("O"); - micm::Species O1D("O1D"); - - state.SetConcentration(M, n_M); - state.SetConcentration(O2, n_O2); - state.SetConcentration(O3, n_O3); - state.SetConcentration(O, 0.0); - state.SetConcentration(O1D, 0.0); - - double time_step = 3600; // s - int nstep = 24; - - print_header(); - print_state(0, state); - - for (int i = 0; i < nstep; ++i) - { - double elapsed_solve_time = 0; - - while (elapsed_solve_time < time_step) - { - auto result = solver.Solve(time_step - elapsed_solve_time, state); - elapsed_solve_time = result.final_time_; - state.variables_[0] = result.result_.AsVector(); - } - print_state(time_step * (i + 1), state); - } - - return 0; -} diff --git a/test/examples/test_robertson.cpp b/test/examples/test_robertson.cpp deleted file mode 100644 index 7caf63d0c..000000000 --- a/test/examples/test_robertson.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, -// -// SPDX-License-Identifier: Apache-2.0 -// -// Test of the TS1 example mechanism (MZ327_TS1.2_20230307 in the Chemistry Cafe) -// -// This only tests that a solver can be built for this mechanism and that it -// can be run for a given set of initial conditions without solver errors. -// -// Comparisons with other chemistry solvers are included in -// https://github.com/NCAR/MUSICA-Performance-Comparison - -#include - -#include -#include - -TEST(Examples, robertson) -{ - micm::SolverConfig config; - std::string config_path = "./examples/configs/robertson"; - auto status = config.ReadAndParse(config_path); - EXPECT_EQ(status, micm::ConfigParseStatus::Success); - auto solver_params = config.GetSolverParams(); - micm::RosenbrockSolver<> solver{ solver_params.system_, - solver_params.processes_, - micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters() }; - micm::State state = solver.GetState(); - - const double temperature = 272.5; // K - const double pressure = 101253.3; // Pa - const double air_density = 1e6; // mol m-3 - - state.conditions_[0].temperature_ = temperature; - state.conditions_[0].pressure_ = pressure; - state.conditions_[0].air_density_ = air_density; - - std::unordered_map> initial_concentrations = { - { "A", { 1.0 } }, // mol m-3 - { "B", { 0.0 } }, // mol m-3 - { "C", { 0.0 } }, // mol m-3 - }; - state.SetConcentrations(initial_concentrations); - - std::unordered_map> custom_rate_constants = { { "PHOTO.r1", { 0.04 } }, - { "PHOTO.r2", { 3e7 } }, - { "PHOTO.r3", { 1e4 } } }; - - state.SetCustomRateParameters(custom_rate_constants); - - double time_step = 200.0; // s - - auto result = solver.Solve(time_step, state); - - EXPECT_EQ(result.state_, (micm::SolverState::Converged)); -} From 16ffc6d06bd553d59ae702dfa0175c4f826559d7 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 1 Dec 2023 12:22:27 -0600 Subject: [PATCH 306/318] using the new state in the tutorials, correcting line numbers, doxygen comments --- README.md | 34 +++----- docs/source/user_guide/jit.rst | 65 ++++++++------- .../source/user_guide/multiple_grid_cells.rst | 81 +++++++++---------- .../user_guide/rate_constant_tutorial.rst | 49 +++++------ .../user_guide/solver_configurations.rst | 4 +- include/micm/process/cuda_process_set.hpp | 2 +- include/micm/process/jit_process_set.hpp | 2 +- include/micm/process/process_set.hpp | 2 +- include/micm/process/rate_constant.hpp | 1 - .../micm/process/surface_rate_constant.hpp | 2 +- .../process/user_defined_rate_constant.hpp | 2 +- include/micm/solver/rosenbrock.hpp | 6 +- include/micm/solver/state.inl | 29 +++++-- include/micm/system/system.hpp | 2 +- test/tutorial/test_README_example.cpp | 8 +- test/tutorial/test_multiple_grid_cells.cpp | 45 +---------- ...rate_constants_no_user_defined_by_hand.cpp | 37 +-------- ..._constants_no_user_defined_with_config.cpp | 37 +-------- ...st_rate_constants_user_defined_by_hand.cpp | 37 +-------- ...ate_constants_user_defined_with_config.cpp | 37 +-------- test/tutorial/test_solver_configuration.cpp | 31 +------ 21 files changed, 166 insertions(+), 347 deletions(-) diff --git a/README.md b/README.md index 4836956df..9ae74e5e3 100644 --- a/README.md +++ b/README.md @@ -136,20 +136,12 @@ int main(const int argc, const char *argv[]) state.conditions_[0].pressure_ = 101319.9; // Pa state.SetConcentration(foo, 20.0); // mol m-3 - std::cout << std::setw(5) << "time [s]," - << std::setw(13) << "foo, " - << std::setw(12) << "bar, " - << std::setw(10) << "baz" << std::endl; + state.PrintHeader(); for (int i = 0; i < 10; ++i) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - std::cout << std::setfill(' ') << std::fixed - << std::setw(8) << std::setprecision(2) << i * 500.0 << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Foo"]] << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Bar"]] << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Baz"]] - << std::endl; + state.PrintState(i*500); } return 0; @@ -164,17 +156,17 @@ g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.2.0/include -std=c++20 Output: ``` -time [s], foo, bar, baz - 0.00, 11.8435, 5.9048, 1.9070 - 500.00, 6.7920, 9.0460, 3.3173 - 1000.00, 3.8287, 10.7406, 4.2105 - 1500.00, 2.1381, 11.6637, 4.7394 - 2000.00, 1.1879, 12.1695, 5.0425 - 2500.00, 0.6581, 12.4475, 5.2133 - 3000.00, 0.3640, 12.6007, 5.3086 - 3500.00, 0.2011, 12.6851, 5.3616 - 4000.00, 0.1110, 12.7317, 5.3909 - 4500.00, 0.0613, 12.7574, 5.4071 + time, Bar, Baz, Foo + 0, 5.90e+00, 1.91e+00, 1.18e+01 + 500, 9.05e+00, 3.32e+00, 6.79e+00 + 1000, 1.07e+01, 4.21e+00, 3.83e+00 + 1500, 1.17e+01, 4.74e+00, 2.14e+00 + 2000, 1.22e+01, 5.04e+00, 1.19e+00 + 2500, 1.24e+01, 5.21e+00, 6.58e-01 + 3000, 1.26e+01, 5.31e+00, 3.64e-01 + 3500, 1.27e+01, 5.36e+00, 2.01e-01 + 4000, 1.27e+01, 5.39e+00, 1.11e-01 + 4500, 1.28e+01, 5.41e+00, 6.13e-02 ``` # Citation diff --git a/docs/source/user_guide/jit.rst b/docs/source/user_guide/jit.rst index a0a380685..f9cd2f773 100644 --- a/docs/source/user_guide/jit.rst +++ b/docs/source/user_guide/jit.rst @@ -110,34 +110,37 @@ The output will be similar to this: .. code:: bash - Jit compile time: 207422864917 nanoseconds - Result time: 8593750 nanoseconds - JIT result time: 4904666 nanoseconds - Result stats: - accepted: 162 - function_calls: 334 - jacobian_updates: 162 - number_of_steps: 172 - accepted: 162 - rejected: 0 - decompositions: 172 - solves: 516 - singular: 0 - total_forcing_time: 339551 nanoseconds - total_jacobian_time: 1.60283e+06 nanoseconds - total_linear_factor_time: 4.88587e+06 nanoseconds - total_linear_solve_time: 928501 nanoseconds - JIT result stats: - accepted: 162 - function_calls: 334 - jacobian_updates: 162 - number_of_steps: 172 - accepted: 162 - rejected: 0 - decompositions: 172 - solves: 516 - singular: 0 - total_forcing_time: 317043 nanoseconds - total_jacobian_time: 1.57724e+06 nanoseconds - total_linear_factor_time: 1.56572e+06 nanoseconds - total_linear_solve_time: 630749 nanoseconds \ No newline at end of file + Jit compile time: 38305334 nanoseconds + Standard solve time: 122582 nanoseconds + JIT solve time: 96541 nanoseconds + Standard solve stats: + accepted: 130 + function_calls: 260 + jacobian_updates: 130 + number_of_steps: 130 + accepted: 130 + rejected: 0 + decompositions: 130 + solves: 390 + singular: 0 + total_update_state_time: 416 nanoseconds + total_forcing_time: 14163 nanoseconds + total_jacobian_time: 9965 nanoseconds + total_linear_factor_time: 24893 nanoseconds + total_linear_solve_time: 12842 nanoseconds + + JIT solve stats: + accepted: 130 + function_calls: 260 + jacobian_updates: 130 + number_of_steps: 130 + accepted: 130 + rejected: 0 + decompositions: 130 + solves: 390 + singular: 0 + total_update_state_time: 458 nanoseconds + total_forcing_time: 5036 nanoseconds + total_jacobian_time: 2168 nanoseconds + total_linear_factor_time: 17958 nanoseconds + total_linear_solve_time: 7586 nanoseconds \ No newline at end of file diff --git a/docs/source/user_guide/multiple_grid_cells.rst b/docs/source/user_guide/multiple_grid_cells.rst index 4840eca62..749ca5586 100644 --- a/docs/source/user_guide/multiple_grid_cells.rst +++ b/docs/source/user_guide/multiple_grid_cells.rst @@ -48,15 +48,14 @@ well as a :cpp:class:`micm::Phase`. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 49-53 + :lines: 11-15 With the species and gas phase, we can define all of our reactions .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 55-71 - + :lines: 17-33 Now we can define our RosenbrockSolver. This time we'll form the reactions and chemical system in place. Also, notice the ``false`` in our :cpp:class:`micm::RosenbrockSolverParameters`. This tells the solver @@ -66,7 +65,7 @@ the order the species are added to the gas phase. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 73-75 + :lines: 35-37 Now we need to get a state and set the concentations of each of the species. In the @@ -76,25 +75,25 @@ to set the concentrations. Here we will set the concentations by providing the : .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 77-82 + :lines: 39-44 Then we set the reaction rates by creating a vector that is 3 elements long, one for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 84-89 + :lines: 46-51 And lastly set the temperature, pressure, and air density for each grid cell. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 91-100 + :lines: 53-62 Now we are ready to run the simulation. .. literalinclude:: ../../../test/tutorial/test_multiple_grid_cells.cpp :language: cpp - :lines: 102-125 + :lines: 64-86 And these are the results. @@ -103,36 +102,36 @@ And these are the results. :header: "time", "grid", "A", "B", "C" :widths: 6, 6, 10, 10, 10 - 0, 1, 1.00e+00, 0.00e+00, 0.00e+00 - 0, 2, 2.00e+00, 0.00e+00, 0.00e+00 - 0, 3, 5.00e-01, 0.00e+00, 0.00e+00 - 200, 1, 5.35e-01, 4.49e-06, 4.65e-01 - 200, 2, 1.21e+00, 6.01e-06, 7.89e-01 - 200, 3, 2.30e-01, 3.31e-06, 2.70e-01 - 400, 1, 4.50e-01, 3.23e-06, 5.50e-01 - 400, 2, 1.05e+00, 4.42e-06, 9.45e-01 - 400, 3, 1.85e-01, 2.32e-06, 3.15e-01 - 600, 1, 4.00e-01, 2.63e-06, 6.00e-01 - 600, 2, 9.59e-01, 3.65e-06, 1.04e+00 - 600, 3, 1.60e-01, 1.85e-06, 3.40e-01 - 800, 1, 3.64e-01, 2.27e-06, 6.36e-01 - 800, 2, 8.90e-01, 3.18e-06, 1.11e+00 - 800, 3, 1.42e-01, 1.57e-06, 3.58e-01 - 1000, 1, 3.37e-01, 2.01e-06, 6.63e-01 - 1000, 2, 8.35e-01, 2.85e-06, 1.16e+00 - 1000, 3, 1.29e-01, 1.38e-06, 3.71e-01 - 1200, 1, 3.15e-01, 1.82e-06, 6.85e-01 - 1200, 2, 7.91e-01, 2.60e-06, 1.21e+00 - 1200, 3, 1.19e-01, 1.24e-06, 3.81e-01 - 1400, 1, 2.96e-01, 1.67e-06, 7.04e-01 - 1400, 2, 7.54e-01, 2.40e-06, 1.25e+00 - 1400, 3, 1.11e-01, 1.13e-06, 3.89e-01 - 1600, 1, 2.81e-01, 1.55e-06, 7.19e-01 - 1600, 2, 7.21e-01, 2.24e-06, 1.28e+00 - 1600, 3, 1.04e-01, 1.04e-06, 3.96e-01 - 1800, 1, 2.67e-01, 1.45e-06, 7.33e-01 - 1800, 2, 6.93e-01, 2.11e-06, 1.31e+00 - 1800, 3, 9.77e-02, 9.65e-07, 4.02e-01 - 2000, 1, 2.55e-01, 1.37e-06, 7.45e-01 - 2000, 2, 6.68e-01, 2.00e-06, 1.33e+00 - 2000, 3, 9.25e-02, 9.02e-07, 4.07e-01 \ No newline at end of file + 0, 0, 1.00e+00, 0.00e+00, 0.00e+00 + 0, 1, 2.00e+00, 0.00e+00, 0.00e+00 + 0, 2, 5.00e-01, 0.00e+00, 0.00e+00 + 200, 0, 5.35e-01, 4.49e-06, 4.65e-01 + 200, 1, 1.21e+00, 6.01e-06, 7.89e-01 + 200, 2, 2.30e-01, 3.31e-06, 2.70e-01 + 400, 0, 4.50e-01, 3.23e-06, 5.50e-01 + 400, 1, 1.05e+00, 4.42e-06, 9.45e-01 + 400, 2, 1.85e-01, 2.32e-06, 3.15e-01 + 600, 0, 4.00e-01, 2.63e-06, 6.00e-01 + 600, 1, 9.59e-01, 3.65e-06, 1.04e+00 + 600, 2, 1.60e-01, 1.85e-06, 3.40e-01 + 800, 0, 3.64e-01, 2.27e-06, 6.36e-01 + 800, 1, 8.90e-01, 3.18e-06, 1.11e+00 + 800, 2, 1.42e-01, 1.57e-06, 3.58e-01 + 1000, 0, 3.37e-01, 2.01e-06, 6.63e-01 + 1000, 1, 8.35e-01, 2.85e-06, 1.16e+00 + 1000, 2, 1.29e-01, 1.38e-06, 3.71e-01 + 1200, 0, 3.15e-01, 1.82e-06, 6.85e-01 + 1200, 1, 7.91e-01, 2.60e-06, 1.21e+00 + 1200, 2, 1.19e-01, 1.24e-06, 3.81e-01 + 1400, 0, 2.96e-01, 1.67e-06, 7.04e-01 + 1400, 1, 7.54e-01, 2.40e-06, 1.25e+00 + 1400, 2, 1.11e-01, 1.13e-06, 3.89e-01 + 1600, 0, 2.81e-01, 1.55e-06, 7.19e-01 + 1600, 1, 7.21e-01, 2.24e-06, 1.28e+00 + 1600, 2, 1.04e-01, 1.04e-06, 3.96e-01 + 1800, 0, 2.67e-01, 1.45e-06, 7.33e-01 + 1800, 1, 6.93e-01, 2.11e-06, 1.31e+00 + 1800, 2, 9.77e-02, 9.65e-07, 4.02e-01 + 2000, 0, 2.55e-01, 1.37e-06, 7.45e-01 + 2000, 1, 6.68e-01, 2.00e-06, 1.33e+00 + 2000, 2, 9.25e-02, 9.02e-07, 4.07e-01 \ No newline at end of file diff --git a/docs/source/user_guide/rate_constant_tutorial.rst b/docs/source/user_guide/rate_constant_tutorial.rst index 3ac2f6624..2f43c8a11 100644 --- a/docs/source/user_guide/rate_constant_tutorial.rst +++ b/docs/source/user_guide/rate_constant_tutorial.rst @@ -71,12 +71,11 @@ rosenbrock solver at the top of the file. :language: cpp :lines: 1-13 -After that, we'll use the ``micm`` namespace and setup a template alias so that we can instantiate the -rosenbrock solver. +After that, we'll use the ``micm`` namespace so that we don't have to repeat it everywhere we need it. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 15-22 + :lines: 14-15 To create a :cpp:class:`micm::RosenbrockSolver`, we have to define a chemical system (:cpp:class:`micm::System`) and our reactions, which will be a vector of :cpp:class:`micm::Process` We will use the species to define these. @@ -91,7 +90,7 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 56-67 + :lines: 19-30 Now that we have a gas phase and our species, we can start building the reactions. Two things to note are that stoichiemtric coefficients for reactants are represented by repeating that product as many times as you need. @@ -100,13 +99,13 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 69-133 + :lines: 32-96 And finally we define our chemical system and reactions .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 135-136 + :lines: 98-99 .. tab-item:: OpenAtmos Configuration reading @@ -115,18 +114,20 @@ and our reactions, which will be a vector of :cpp:class:`micm::Process` We will .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 57-69 + :lines: 20-32 Now that we have a chemical system and a list of reactions, we can create the RosenbrockSolver. There are several ways to configure the solver. Here we are using a three stage solver. More options -can be found in the :cpp:class:`micm::RosenbrockSolverParameters` +can be found in the :cpp:class:`micm::RosenbrockSolverParameters` and in the :ref:`Solver Configurations` tutorial. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 138-140 + :lines: 101 The rosenbrock solver will provide us a state, which we can use to set the concentrations, -custom rate parameters, and temperature and pressure +custom rate parameters, and temperature and pressure. Note that setting the custom rate paramters is different depending +on if you define the configuration by hand or read it in. The parser has defaults for the names of the custom parameters +and when defined by hand we choose these. .. _Rate constants set concentations: @@ -139,20 +140,20 @@ Initializing the state .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 141-155 + :lines: 102-116 .. tab-item:: OpenAtmos Configuration reading .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_with_config.cpp :language: cpp - :lines: 75-93 + :lines: 36-54 Finally, we are ready to pick a timestep and solve the system. .. literalinclude:: ../../../test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp :language: cpp - :lines: 149-173 + :lines: 118-142 This is the output: @@ -162,14 +163,14 @@ This is the output: :header: "time", "A", "B", "C", "D", "E", "F", "G" :widths: 10, 15, 15, 15, 15, 15, 15, 15 - "0", "1.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00", "0.00e+00" - "500", "3.18e-09", "3.66e-09", "9.83e-01", "3.88e-14", "1.41e-03", "2.02e-13", "7.92e-03" - "1000", "1.14e-14", "1.31e-14", "9.66e-01", "1.39e-19", "1.40e-03", "7.24e-19", "1.64e-02" - "1500", "4.09e-20", "4.71e-20", "9.49e-01", "4.98e-25", "1.39e-03", "2.59e-24", "2.48e-02" - "2000", "1.47e-25", "1.69e-25", "9.33e-01", "1.79e-30", "1.38e-03", "9.30e-30", "3.30e-02" - "2500", "5.26e-31", "6.05e-31", "9.17e-01", "6.40e-36", "1.37e-03", "3.33e-35", "4.11e-02" - "3000", "1.89e-36", "2.17e-36", "9.01e-01", "2.30e-41", "1.36e-03", "1.20e-40", "4.90e-02" - "3500", "6.77e-42", "7.78e-42", "8.85e-01", "8.23e-47", "1.34e-03", "4.29e-46", "5.68e-02" - "4000", "2.43e-47", "2.79e-47", "8.70e-01", "2.95e-52", "1.33e-03", "1.54e-51", "6.44e-02" - "4500", "8.70e-53", "1.00e-52", "8.55e-01", "1.06e-57", "1.32e-03", "5.51e-57", "7.20e-02" - "5000", "3.12e-58", "3.59e-58", "8.40e-01", "3.80e-63", "1.31e-03", "1.98e-62", "7.94e-02" + 0, 1.00e+00, 0.00e+00, 0.00e+00, 0.00e+00, 0.00e+00, 0.00e+00, 0.00e+00 + 500, 3.18e-09, 3.66e-09, 9.83e-01, 3.88e-14, 1.41e-03, 2.02e-13, 7.92e-03 + 1000, 1.14e-14, 1.31e-14, 9.66e-01, 1.39e-19, 1.40e-03, 7.24e-19, 1.64e-02 + 1500, 4.09e-20, 4.71e-20, 9.49e-01, 4.98e-25, 1.39e-03, 2.59e-24, 2.48e-02 + 2000, 1.47e-25, 1.69e-25, 9.33e-01, 1.79e-30, 1.38e-03, 9.30e-30, 3.30e-02 + 2500, 5.26e-31, 6.05e-31, 9.17e-01, 6.40e-36, 1.37e-03, 3.33e-35, 4.11e-02 + 3000, 1.89e-36, 2.17e-36, 9.01e-01, 2.30e-41, 1.36e-03, 1.20e-40, 4.90e-02 + 3500, 6.77e-42, 7.78e-42, 8.85e-01, 8.23e-47, 1.34e-03, 4.29e-46, 5.68e-02 + 4000, 2.43e-47, 2.79e-47, 8.70e-01, 2.95e-52, 1.33e-03, 1.54e-51, 6.44e-02 + 4500, 8.70e-53, 1.00e-52, 8.55e-01, 1.06e-57, 1.32e-03, 5.51e-57, 7.20e-02 + 5000, 3.12e-58, 3.59e-58, 8.40e-01, 3.80e-63, 1.31e-03, 1.98e-62, 7.94e-02 diff --git a/docs/source/user_guide/solver_configurations.rst b/docs/source/user_guide/solver_configurations.rst index ecde3e0b3..54842a7ef 100644 --- a/docs/source/user_guide/solver_configurations.rst +++ b/docs/source/user_guide/solver_configurations.rst @@ -45,14 +45,14 @@ Configuring the rosenbrock solver is as easy as providing the solver with a set .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp - :lines: 147-165 + :lines: 121-133 After that, the usage is the same as a regular solver. A templated method was used here to run the same solving code for each of the different solver configurations. .. literalinclude:: ../../../test/tutorial/test_solver_configuration.cpp :language: cpp - :lines: 34-116 + :lines: 10-90 Running this program should give an output similar to this: diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index 39e8e5c08..4217e7cdc 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -15,7 +15,7 @@ namespace micm public: /// @brief Create a process set calculator for a given set of processes /// @param processes Processes to create calculator for - /// @param state Solver state + /// @param variable_map A mapping of species names to concentration index CudaProcessSet(const std::vector& processes, const std::map& variable_map); template typename MatrixPolicy> diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index fdb21e226..5aac29b9c 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -35,7 +35,7 @@ namespace micm /// @brief Create a JITed process set calculator for a given set of processes /// @param compiler JIT compiler /// @param processes Processes to create calculator for - /// @param state Solver state + /// @param variable_map A mapping of species names to concentration index JitProcessSet( std::shared_ptr compiler, const std::vector &processes, diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index db1250a68..53bf1fbbf 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -29,7 +29,7 @@ namespace micm /// @brief Create a process set calculator for a given set of processes /// @param processes Processes to create calculator for - /// @param StateParameters Solver state + /// @param variable_map A mapping of species names to concentration index ProcessSet(const std::vector& processes, const std::map& variable_map); /// @brief Return the full set of non-zero Jacobian elements for the set of processes diff --git a/include/micm/process/rate_constant.hpp b/include/micm/process/rate_constant.hpp index 2dd82e9eb..77be53503 100644 --- a/include/micm/process/rate_constant.hpp +++ b/include/micm/process/rate_constant.hpp @@ -38,7 +38,6 @@ namespace micm /// @brief Calculate the rate constant for a set of conditions /// @param conditions The current environmental conditions of the chemical system - /// @param custom_parameters User defined rate constant parameters /// @return The reaction rate constant virtual double calculate(const Conditions& conditions) const { diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index a72786504..8049d3846 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -34,7 +34,7 @@ namespace micm double mean_free_speed_factor_; // 8 * gas_constant / ( pi * molecular_weight ) [K-1] /// @brief - /// @param name A name for this reaction + /// @param parameters The data required to build this class SurfaceRateConstant(const SurfaceRateConstantParameters& parameters); /// @brief Deep copy diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index fbaf1475f..480c6f407 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -26,7 +26,7 @@ namespace micm UserDefinedRateConstant(); /// @brief - /// @param name A name for this reaction + /// @param parameters The data needed to build this class UserDefinedRateConstant(const UserDefinedRateConstantParameters& parameters); /// @brief Deep copy diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 9322180aa..ab1bc21f9 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -198,7 +198,7 @@ namespace micm /// @param gamma time step factor for specific rosenbrock method /// @param singular indicates if the matrix is singular /// @param number_densities constituent concentration (molec/cm^3) - /// @param rate_constants Rate constants for each process (molecule/cm3)^(n-1) s-1 + /// @param state The current State void LinearFactor( double& H, const double gamma, @@ -209,8 +209,8 @@ namespace micm protected: /// @brief Computes the scaled norm of the vector errors - /// @param Y the original vector - /// @param Ynew the new vector + /// @param y the original vector + /// @param y_new the new vector /// @param errors The computed errors /// @return double diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index f0b54d121..96be1d2b0 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -121,7 +121,10 @@ namespace micm int largest_str_size = largest_str_iter->size(); int width = (largest_str_size < 10) ? 11 : largest_str_size + 2; - std::cout << std::setw(5) << "time"; + std::cout << std::setw(6) << "time"; + if (variables_.size() > 1) { + std::cout << "," << std::setw(6) << "grid"; + } for(const auto& [species, index] : variable_map_) { @@ -142,14 +145,26 @@ namespace micm int largest_str_size = largest_str_iter->size(); int width = (largest_str_size < 10) ? 11 : largest_str_size + 2; - std::cout << std::setw(5) << time << std::flush; - for(const auto& [species, index] : variable_map_) - { - std::cout << std::scientific << "," << std::setw(width) << std::setprecision(2) << variables_[0][index]; + for(size_t i = 0; i < variables_.size(); ++i) { + std::cout << std::setw(6) << time << ","; + + if (variables_.size() > 1) { + std::cout << std::setw(6) << i << ","; + } + + bool first = true; + for(const auto& [species, index] : variable_map_) + { + if (!first) { + std::cout << ","; + } + std::cout << std::scientific << std::setw(width) << std::setprecision(2) << variables_[i][index]; + first = false; + } + std::cout << std::endl; + std::cout.copyfmt(oldState); } - std::cout << std::endl; - std::cout.copyfmt(oldState); } } // namespace micm diff --git a/include/micm/system/system.hpp b/include/micm/system/system.hpp index c0b881b0f..e1461190f 100644 --- a/include/micm/system/system.hpp +++ b/include/micm/system/system.hpp @@ -84,7 +84,7 @@ namespace micm std::vector UniqueNames() const; /// @brief Returns a set of unique species names - /// @param reordering Function used to apply specific order to unique names + /// @param f Function used to apply specific order to unique names /// @return vector of unique state variable names std::vector UniqueNames( const std::function& variables, const std::size_t i)> f) const; diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index f84e9df59..bdc03ea1c 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -37,16 +37,12 @@ int main(const int argc, const char *argv[]) state.conditions_[0].pressure_ = 101319.9; // Pa state.SetConcentration(foo, 20.0); // mol m-3 - std::cout << std::setw(5) << "time [s]," << std::setw(13) << "foo, " << std::setw(12) << "bar, " << std::setw(10) << "baz" - << std::endl; + state.PrintHeader(); for (int i = 0; i < 10; ++i) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - std::cout << std::setfill(' ') << std::fixed << std::setw(8) << std::setprecision(2) << i * 500.0 << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Foo"]] << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Bar"]] << ", " - << std::setw(10) << std::setprecision(4) << state.variables_[0][state.variable_map_["Baz"]] << std::endl; + state.PrintState(i*500); } return 0; diff --git a/test/tutorial/test_multiple_grid_cells.cpp b/test/tutorial/test_multiple_grid_cells.cpp index a922e4caa..55a8cafd3 100644 --- a/test/tutorial/test_multiple_grid_cells.cpp +++ b/test/tutorial/test_multiple_grid_cells.cpp @@ -6,44 +6,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(5) << "grid" - << "," << std::setw(10) << "A" - << "," << std::setw(10) << "B" - << "," << std::setw(10) << "C" << std::endl; -} - -template class T, template class D> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "1," << std::setw(10) - << state.variables_[0][state.variable_map_["A"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "2," << std::setw(10) - << state.variables_[1][state.variable_map_["A"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[1][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(6) << "3," << std::setw(10) - << state.variables_[2][state.variable_map_["A"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[2][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); -} - int main() { auto a = micm::Species("A"); @@ -102,8 +64,8 @@ int main() // choose a timestep and print the initial state double time_step = 200; // s - print_header(); - print_state(0, state); + state.PrintHeader(); + state.PrintState(0); // solve for ten iterations for (int i = 0; i < 10; ++i) @@ -120,7 +82,6 @@ int main() elapsed_solve_time = result.final_time_; state.variables_ = result.result_; } - - print_state(time_step * (i + 1), state); + state.PrintState(time_step * (i + 1)); } } \ No newline at end of file diff --git a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp index 22aff99b3..c9e6ded20 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_by_hand.cpp @@ -14,37 +14,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(11) << "A" - << "," << std::setw(10) << "B" - << "," << std::setw(10) << "C" - << "," << std::setw(10) << "D" - << "," << std::setw(10) << "E" - << "," << std::setw(10) << "F" - << "," << std::setw(10) << "G" << std::endl; -} - -template class T> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ", " << std::flush; - - std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] - << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," - << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; - - std::cout.copyfmt(oldState); -} - int main(const int argc, const char* argv[]) { auto a = Species("A"); @@ -149,8 +118,8 @@ int main(const int argc, const char* argv[]) // choose a timestep and print the initial state double time_step = 500; // s - print_header(); - print_state(0, state); + state.PrintHeader(); + state.PrintState(0); // solve for ten iterations for (int i = 0; i < 10; ++i) @@ -169,7 +138,7 @@ int main(const int argc, const char* argv[]) state.variables_ = result.result_; } - print_state(time_step * (i + 1), state); + state.PrintState(time_step * (i + 1)); } return 0; diff --git a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp index b4842c179..41eed7591 100644 --- a/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_no_user_defined_with_config.cpp @@ -15,37 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(10) << "A" - << "," << std::setw(9) << "B" - << "," << std::setw(9) << "C" - << "," << std::setw(9) << "D" - << "," << std::setw(9) << "E" - << "," << std::setw(9) << "F" - << "," << std::setw(10) << "G" << std::endl; -} - -template class T> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ", " << std::flush; - - std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] - << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," - << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; - - std::cout.copyfmt(oldState); -} - int main(const int argc, const char* argv[]) { SolverConfig solverConfig; @@ -87,8 +56,8 @@ int main(const int argc, const char* argv[]) // choose a timestep and print the initial state double time_step = 500; // s - print_header(); - print_state(0, state); + state.PrintHeader(); + state.PrintState(0); // solve for ten iterations for (int i = 0; i < 10; ++i) @@ -107,7 +76,7 @@ int main(const int argc, const char* argv[]) state.variables_ = result.result_; } - print_state(time_step * (i + 1), state); + state.PrintState(time_step * (i + 1)); } return 0; diff --git a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp index 370a5acfd..2cb50810b 100644 --- a/test/tutorial/test_rate_constants_user_defined_by_hand.cpp +++ b/test/tutorial/test_rate_constants_user_defined_by_hand.cpp @@ -15,37 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(11) << "A" - << "," << std::setw(10) << "B" - << "," << std::setw(10) << "C" - << "," << std::setw(10) << "D" - << "," << std::setw(10) << "E" - << "," << std::setw(10) << "F" - << "," << std::setw(10) << "G" << std::endl; -} - -template class T, template class D> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ", " << std::flush; - - std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] - << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," - << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; - - std::cout.copyfmt(oldState); -} - int main(const int argc, const char* argv[]) { auto a = Species("A"); @@ -166,8 +135,8 @@ int main(const int argc, const char* argv[]) // choose and timestep a print the initial state double time_step = 500; // s - print_header(); - print_state(0, state); + state.PrintHeader(); + state.PrintState(0); double photo_rate = 1e-10; double emission_rate = 1e-20; @@ -194,7 +163,7 @@ int main(const int argc, const char* argv[]) state.variables_ = result.result_; } - print_state(time_step * (i + 1), state); + state.PrintState(time_step * (i + 1)); photo_rate *= 1.5; } diff --git a/test/tutorial/test_rate_constants_user_defined_with_config.cpp b/test/tutorial/test_rate_constants_user_defined_with_config.cpp index c9fd6bb53..804b68708 100644 --- a/test/tutorial/test_rate_constants_user_defined_with_config.cpp +++ b/test/tutorial/test_rate_constants_user_defined_with_config.cpp @@ -15,37 +15,6 @@ // Use our namespace so that this example is easier to read using namespace micm; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(10) << "A" - << "," << std::setw(9) << "B" - << "," << std::setw(9) << "C" - << "," << std::setw(9) << "D" - << "," << std::setw(9) << "E" - << "," << std::setw(9) << "F" - << "," << std::setw(10) << "G" << std::endl; -} - -template class T, template class D> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ", " << std::flush; - - std::cout << std::scientific << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["A"]] - << "," << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["B"]] << "," - << std::setw(10) << std::setprecision(2) << state.variables_[0][state.variable_map_["C"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["D"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["E"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["F"]] << "," << std::setw(10) - << std::setprecision(2) << state.variables_[0][state.variable_map_["G"]] << std::endl; - - std::cout.copyfmt(oldState); -} - int main(const int argc, const char* argv[]) { SolverConfig solverConfig; @@ -87,8 +56,8 @@ int main(const int argc, const char* argv[]) // choose a timestep a print the initial state double time_step = 500; // s - print_header(); - print_state(0, state); + state.PrintHeader(); + state.PrintState(0); double photo_rate = 1e-10; double emission_rate = 1e-20; @@ -115,7 +84,7 @@ int main(const int argc, const char* argv[]) state.variables_ = result.result_; } - print_state(time_step * (i + 1), state); + state.PrintState(time_step * (i + 1)); photo_rate *= 1.5; } diff --git a/test/tutorial/test_solver_configuration.cpp b/test/tutorial/test_solver_configuration.cpp index 5e45635d3..a628c5ff7 100644 --- a/test/tutorial/test_solver_configuration.cpp +++ b/test/tutorial/test_solver_configuration.cpp @@ -7,30 +7,7 @@ // Use our namespace so that this example is easier to read using namespace micm; -void print_header() -{ - std::cout << std::setw(5) << "time" - << "," << std::setw(10) << "A" - << "," << std::setw(10) << "B" - << "," << std::setw(10) << "C" << std::endl; -} - -template class T, template class D> -void print_state(double time, State& state) -{ - std::ios oldState(nullptr); - oldState.copyfmt(std::cout); - - std::cout << std::setw(5) << time << ","; - std::cout << std::scientific << std::setprecision(2) << std::setw(10) << state.variables_[0][state.variable_map_["A"]] - << "," << std::setw(10) << state.variables_[0][state.variable_map_["B"]] << "," << std::setw(10) - << state.variables_[0][state.variable_map_["C"]] << std::endl; - - std::cout.copyfmt(oldState); -} - -template -void test_solver_type(T solver) +void test_solver_type(auto& solver) { auto state = solver.GetState(); @@ -55,8 +32,8 @@ void test_solver_type(T solver) // choose a timestep and print the initial state double time_step = 200; // s - print_header(); - print_state(0, state); + state.PrintHeader(); + state.PrintState(0); SolverStats total_stats; std::chrono::duration total_solve_time = std::chrono::nanoseconds::zero(); @@ -94,7 +71,7 @@ void test_solver_type(T solver) state.variables_ = result.result_; } - print_state(time_step * (i + 1), state); + state.PrintState(time_step * (i + 1)); } std::cout << "Total solve time: " << total_solve_time.count() << " nanoseconds" << std::endl; std::cout << "accepted: " << total_stats.accepted << std::endl; From 897374219bc845e4fac20af69c394cf5f9c49ccb Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 1 Dec 2023 18:37:21 +0000 Subject: [PATCH 307/318] Auto-format code using Clang-Format --- test/tutorial/test_README_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tutorial/test_README_example.cpp b/test/tutorial/test_README_example.cpp index bdc03ea1c..f13ef08cb 100644 --- a/test/tutorial/test_README_example.cpp +++ b/test/tutorial/test_README_example.cpp @@ -42,7 +42,7 @@ int main(const int argc, const char *argv[]) { auto result = solver.Solve(500.0, state); state.variables_ = result.result_; - state.PrintState(i*500); + state.PrintState(i * 500); } return 0; From a6f96467fd02b2b01c766ae70011ed77680cc9cd Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Fri, 1 Dec 2023 13:37:05 -0600 Subject: [PATCH 308/318] trying a different filter (#382) --- .github/workflows/clang_format.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/clang_format.yml b/.github/workflows/clang_format.yml index bc75fa61d..d5e3131c6 100644 --- a/.github/workflows/clang_format.yml +++ b/.github/workflows/clang_format.yml @@ -24,7 +24,7 @@ jobs: 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 -not -path "./test/tutorial/*" -type f \( -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 From b5025ea523c4b0c94f535bba2fc37ba854903025 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Fri, 1 Dec 2023 11:37:54 -0800 Subject: [PATCH 309/318] Develop 374 examples (#381) * add examples to tests * update chapman example conditions and docs * fix quotes on cerr string --- README.md | 28 +- docs/source/getting_started.rst | 43 +- examples/CMakeLists.txt | 18 +- examples/configs/TS1/initial_conditions.csv | 2 + .../carbon_bond_5/initial_conditions.csv | 411 +++--------------- .../configs/chapman/initial_conditions.csv | 21 +- examples/example.cpp | 61 ++- 7 files changed, 198 insertions(+), 386 deletions(-) diff --git a/README.md b/README.md index 9ae74e5e3..09b9ff62a 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,33 @@ cd /build/ make test ``` -# Using MICM +# Using the MICM executable + +A simple driver for MICM is built with the library and can be used to solve a +chemical system for given initial conditions over one time step. + +Just pass the driver the path to the folder containing a valid JSON +mechanism configuration and the path to a CSV file holding the initial +conditions. + +Several example mechanisms and sets of conditions can be found in the +`/examples/configs/` folder. + +You can use them like this: + +``` +micm examples/configs/chapman examples/configs/chapman/initial_conditions.csv +``` + +The output should be: + +``` + time, O, O1D, O2, O3 + 0, 0.00e+00, 0.00e+00, 7.50e-01, 8.10e-06 + 60, 2.57e-12, 3.49e-22, 7.50e-01, 8.10e-06 +``` + +# Using the MICM API The following example solves the fictitious chemical system: diff --git a/docs/source/getting_started.rst b/docs/source/getting_started.rst index cf1ce1d07..103e9e3e3 100644 --- a/docs/source/getting_started.rst +++ b/docs/source/getting_started.rst @@ -64,35 +64,35 @@ Build and run the image:: If you would like, you can ssh into a running docker container and edit the files there. -GPU ---- +Run an Example +-------------- -NCAR Hardware -^^^^^^^^^^^^^ +MICM Executable Example +^^^^^^^^^^^^^^^^^^^^^^^ -On Cheyenne -^^^^^^^^^^^ +A simple driver for MICM is built with the library and can be used to solve a +chemical system for given initial conditions over one time step. -On Casper -^^^^^^^^^ +Just pass the driver the path to the folder containing a valid JSON +mechanism configuration and the path to a CSV file holding the initial +conditions. -On Gust and Derecho -^^^^^^^^^^^^^^^^^^^ +Several example mechanisms and sets of conditions can be found in the +``/examples/configs/`` folder. -To compile and test on gust:: +You can use them like this:: - $ qinteractive -A NTDD0005 --ngpus=1 - $ module load cmake/3.25.2 nvhpc/23.1 cuda/11.7.1 - $ mkdir build && cd build - $ cmake -DENABLE_OPENACC=OFF -DENABLE_CUDA=ON -D GPU_TYPE="a100" .. - $ make - $ make test + $ micm examples/configs/chapman examples/configs/chapman/initial_conditions.csv -NOAA Hardware -^^^^^^^^^^^^^ +The output should be:: -Run an Example --------------- + time, O, O1D, O2, O3 + 0, 0.00e+00, 0.00e+00, 7.50e-01, 8.10e-06 + 60, 2.57e-12, 3.49e-22, 7.50e-01, 8.10e-06 + + +MICM API Example +^^^^^^^^^^^^^^^^ The following example solves the fictitious chemical system:: foo --k1--> 0.8 bar + 0.2 baz @@ -128,3 +128,4 @@ You should see an output including this 4000.000000, 0.111028, 12.731727, 5.390884 4500.000000, 0.061290, 12.757422, 5.407096 + diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 73c481d2f..7fcbcb15e 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,4 +4,20 @@ set(CMAKE_CXX_CLANG_TIDY "") add_executable(micmDriver example.cpp) target_link_libraries(micmDriver PUBLIC musica::micm ) -set_target_properties(micmDriver PROPERTIES OUTPUT_NAME "micm") \ No newline at end of file +set_target_properties(micmDriver PROPERTIES OUTPUT_NAME "micm") + +################################################################################ +# Run each example configuration as a test + +add_test(NAME carbon_bond_5_example + COMMAND micm ${CMAKE_BINARY_DIR}/examples/configs/carbon_bond_5 ${CMAKE_BINARY_DIR}/examples/configs/carbon_bond_5/initial_conditions.csv + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME chapman_example + COMMAND micm ${CMAKE_BINARY_DIR}/examples/configs/chapman ${CMAKE_BINARY_DIR}/examples/configs/chapman/initial_conditions.csv + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME robertson_example + COMMAND micm ${CMAKE_BINARY_DIR}/examples/configs/robertson ${CMAKE_BINARY_DIR}/examples/configs/robertson/initial_conditions.csv + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) +add_test(NAME ts1_example + COMMAND micm ${CMAKE_BINARY_DIR}/examples/configs/TS1 ${CMAKE_BINARY_DIR}/examples/configs/TS1/initial_conditions.csv + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) \ No newline at end of file diff --git a/examples/configs/TS1/initial_conditions.csv b/examples/configs/TS1/initial_conditions.csv index 274fee21f..fa2a0ec1d 100644 --- a/examples/configs/TS1/initial_conditions.csv +++ b/examples/configs/TS1/initial_conditions.csv @@ -335,6 +335,8 @@ SURF.usr_NC4CHO_aer,2.820947917738780E-08,1.0e11 SURF.usr_TERPNIT_aer,2.820947917738780E-08,1.0e11 SURF.usr_NTERPOOH_aer,2.820947917738780E-08,1.0e11 SURF.usr_NC4CH2OH_aer,2.820947917738780E-08,1.0e11 +USER.usr_CO_OH,8.886e4 +USER.usr_DMS_OH,3.484e-12 ENV.temperature,287.45 ENV.pressure,101319.9 ENV.air_density,42.3934005 diff --git a/examples/configs/carbon_bond_5/initial_conditions.csv b/examples/configs/carbon_bond_5/initial_conditions.csv index 005df9960..3e5cc918f 100644 --- a/examples/configs/carbon_bond_5/initial_conditions.csv +++ b/examples/configs/carbon_bond_5/initial_conditions.csv @@ -1,352 +1,65 @@ -CONC.O2,8.90261411 -CONC.N2,33.1007671 -CONC.ALKNIT,8.72E-10 -CONC.BZOOH,2.68E-11 -CONC.C6H5OOH,4.20E-10 -CONC.COF2,5.24E-11 -CONC.COFCL,7.92E-12 -CONC.HF,1.81E-11 -CONC.F,3.89E-28 -CONC.BENZO2,2.09E-13 -CONC.BZOO,8.81E-13 -CONC.PAN,4.25E-08 -CONC.MVK,2.61E-08 -CONC.MACROOH,1.21E-09 -CONC.soa1_a1,1.0E-15 -CONC.soa1_a2,1.0E-15 -CONC.soa2_a1,1.0E-15 -CONC.soa2_a2,1.0E-15 -CONC.soa3_a1,1.0E-15 -CONC.soa3_a2,1.0E-15 -CONC.soa4_a1,1.0E-15 -CONC.soa4_a2,1.0E-15 -CONC.soa5_a1,1.0E-15 -CONC.soa5_a2,1.0E-15 -CONC.SOAG0,3.33E-11 -CONC.SOAG1,2.14E-10 -CONC.SOAG2,1.40E-09 -CONC.SOAG3,4.27E-09 -CONC.SOAG4,2.25E-08 -CONC.ISOPNITB,1.05E-09 -CONC.SO3,1.75E-17 -CONC.OCS,1.96E-08 -CONC.SO,1.22E-20 -CONC.S,1.10E-26 -CONC.H,9.67E-18 -CONC.MEK,1.04E-08 -CONC.MTERP,1.09E-09 -CONC.N2O5,1.00E-11 -CONC.HCN,1.90E-08 -CONC.SVOC,1.98E-10 -CONC.ISOPNO3,5.35E-13 -CONC.RO2,7.13E-12 -CONC.PHENO2,2.54E-13 -CONC.IVOC,2.06E-09 -CONC.TERPNIT,8.28E-10 -CONC.HNO3,1.04E-07 -CONC.ACBZO2,3.54E-13 -CONC.CH3COOOH,7.19E-09 -CONC.SO2,6.15E-08 -CONC.CH4,7.03E-05 -CONC.CH3CL,2.07E-08 -CONC.CH3CO3,8.32E-11 -CONC.C6H5O2,6.23E-12 -CONC.TERPROD1,1.68E-09 -CONC.HYAC,4.88E-08 -CONC.HPALD,3.56E-10 -CONC.H2O,7.46E-01 -CONC.NO2,6.37E-08 -CONC.EOOH,3.56E-09 -CONC.NTERPOOH,9.37E-11 -CONC.CCL4,2.96E-09 -CONC.CF2CLBR,1.24E-10 -CONC.CF3BR,1.26E-10 -CONC.CFC11,8.50E-09 -CONC.CFC113,2.66E-09 -CONC.CFC114,6.19E-10 -CONC.CFC115,3.29E-10 -CONC.CFC12,1.92E-08 -CONC.CH2BR2,4.32E-11 -CONC.CH3BR,2.37E-10 -CONC.CH3CCL3,5.55E-11 -CONC.NO,1.47E-08 -CONC.BR,3.67E-14 -CONC.BRCL,9.18E-19 -CONC.BRO,1.95E-13 -CONC.BRONO2,1.22E-11 -CONC.CL,1.07E-15 -CONC.CL2,1.01E-16 -CONC.CL2O2,1.89E-23 -CONC.CLO,8.06E-14 -CONC.CLONO2,4.09E-11 -CONC.HCOOH,8.79E-09 -CONC.HBR,4.48E-11 -CONC.HOBR,1.65E-12 -CONC.HOCL,2.11E-12 -CONC.BIGENE,6.59E-10 -CONC.C2H4,2.51E-08 -CONC.C2H5O2,1.55E-12 -CONC.CH3COCHO,1.95E-08 -CONC.CH3COCH3,7.37E-08 -CONC.O,2.85E-14 -CONC.OCLO,7.68E-19 -CONC.O1D,1.53E-19 -CONC.PHENO,1.00E-12 -CONC.HCFC141B,9.29E-10 -CONC.HCFC142B,8.24E-10 -CONC.HCFC22,9.07E-09 -CONC.DMS,1.74E-10 -CONC.C2H5OH,2.75E-08 -CONC.HCL,8.17E-10 -CONC.BEPOMUC,5.40E-12 -CONC.CHBR3,3.87E-11 -CONC.H2402,1.52E-11 -CONC.CO2,1.56E-02 -CONC.BZALD,4.70E-10 -CONC.BENZENE,2.67E-09 -CONC.C3H7O2,2.31E-12 -CONC.CH3O2,3.34E-10 -CONC.BCARY,5.04E-12 -CONC.BIGALD,1.33E-11 -CONC.BIGALD2,6.58E-11 -CONC.BIGALD3,6.83E-11 -CONC.BIGALD4,7.34E-10 -CONC.BIGALK,1.90E-08 -CONC.H2O2,7.36E-08 -CONC.C2H5OOH,7.99E-11 -CONC.C2H6,2.93E-08 -CONC.C3H8,9.70E-09 -CONC.C3H6,2.42E-09 -CONC.CH2O,2.44E-07 -CONC.CH3CN,4.66E-09 -CONC.C2H2,2.13E-08 -CONC.CH3OH,2.74E-07 -CONC.CH3OOH,1.69E-08 -CONC.CRESOL,1.06E-10 -CONC.ENEO2,7.98E-12 -CONC.MACRO2,1.09E-10 -CONC.ISOPAO2,1.39E-10 -CONC.MALO2,1.07E-13 -CONC.ISOPBO2,9.08E-11 -CONC.MCO3,9.23E-12 -CONC.MDIALO2,1.61E-13 -CONC.MEKO2,2.81E-12 -CONC.EO2,4.04E-11 -CONC.EO,9.93E-18 -CONC.GLYOXAL,1.25E-08 -CONC.MPAN,3.11E-09 -CONC.NC4CH2OH,7.30E-13 -CONC.ISOPOOH,4.68E-09 -CONC.GLYALD,4.58E-08 -CONC.HO2,1.69E-09 -CONC.HOCH2OO,1.07E-13 -CONC.H2,1.93E-05 -CONC.HYDRALD,1.49E-08 -CONC.ISOP,1.23E-08 -CONC.NTERPO2,9.18E-13 -CONC.TOLO2,3.23E-12 -CONC.TERP2O2,2.15E-11 -CONC.XYLENO2,4.98E-12 -CONC.TERPO2,1.29E-11 -CONC.XYLOLO2,4.62E-13 -CONC.PBZNIT,1.78E-10 -CONC.XYLENES,2.82E-09 -CONC.PO2,1.46E-11 -CONC.TOLUENE,4.88E-09 -CONC.XO2,1.12E-10 -CONC.XOOH,7.93E-09 -CONC.TERPROD2,3.81E-09 -CONC.MEKOOH,6.15E-11 -CONC.MACR,1.06E-08 -CONC.HONITR,6.79E-09 -CONC.ISOPNITA,1.62E-09 -CONC.ISOPNOOH,9.16E-11 -CONC.IEPOX,5.84E-09 -CONC.ONITR,2.67E-09 -CONC.H2SO4,2.27E-11 -CONC.N2O,1.27E-05 -CONC.NO3,9.32E-12 -CONC.OH,2.52E-11 -CONC.PHENOOH,9.15E-12 -CONC.PHENOL,9.49E-11 -CONC.XYLOL,9.92E-11 -CONC.ROOH,4.40E-10 -CONC.O3,2.49E-06 -CONC.TERPOOH,2.46E-10 -CONC.TOLOOH,9.69E-11 -CONC.XYLENOOH,1.50E-10 -CONC.XYLOLOOH,1.31E-11 -CONC.CO,3.25E-06 -CONC.CH3COOH,8.20E-09 -CONC.CH3CHO,2.51E-08 -CONC.BIGALD1,6.47E-11 -CONC.ALKOOH,3.79E-10 -CONC.DICARBO2,1.55E-13 -CONC.BENZOOH,5.78E-12 -CONC.ALKO2,1.39E-11 -CONC.HO2NO2,7.99E-10 -CONC.C3H7OOH,7.87E-11 -CONC.NH3,7.59E-08 -CONC.TERP2OOH,5.96E-10 -CONC.POOH,9.24E-10 -CONC.NOA,5.46E-09 -CONC.NC4CHO,3.67E-11 -CONC.TEPOMUC,2.21E-10 -CONC.NH4,1.0E-15 -CONC.SF6,1.0E-15 -PHOTO.jacet,1.25E-06 -PHOTO.jalknit,5.95E-06 -PHOTO.jalkooh,5.95E-06 -PHOTO.jbenzooh,5.95E-06 -PHOTO.jbepomuc,1.01E-03 -PHOTO.jbigald,2.01E-03 -PHOTO.jbigald1,1.41E-03 -PHOTO.jbigald2,2.01E-03 -PHOTO.jbigald3,2.01E-03 -PHOTO.jbigald4,6.03E-05 -PHOTO.jbrcl,1.12E-02 -PHOTO.jbro,4.07E-02 -PHOTO.jbrono2_a,1.27E-03 -PHOTO.jbrono2_b,2.24E-04 -PHOTO.jbzooh,5.95E-06 -PHOTO.jc2h5ooh,5.95E-06 -PHOTO.jc3h7ooh,5.95E-06 -PHOTO.jc6h5ooh,5.95E-06 -PHOTO.jccl4,1.28E-23 -PHOTO.jcf2cl2,2.50E-24 -PHOTO.jcf2clbr,6.95E-09 -PHOTO.jcf3br,1.45E-12 -PHOTO.jcfc113,2.60E-24 -PHOTO.jcfc114,2.09E-25 -PHOTO.jcfc115,1.23E-26 -PHOTO.jcfcl3,1.38E-23 -PHOTO.jch2br2,1.15E-09 -PHOTO.jch2o_a,3.47E-05 -PHOTO.jch2o_b,4.92E-05 -PHOTO.jch3br,9.35E-16 -PHOTO.jch3ccl3,1.79E-23 -PHOTO.jch3cho,7.18E-06 -PHOTO.jch3cl,4.68E-25 -PHOTO.jch3co3h,2.23E-06 -PHOTO.jch3ooh,5.95E-06 -PHOTO.jch4_a,0.00E+00 -PHOTO.jch4_b,0.00E+00 -PHOTO.jchbr3,1.81E-06 -PHOTO.jcl2,2.52E-03 -PHOTO.jcl2o2,1.98E-03 -PHOTO.jclo,2.40E-04 -PHOTO.jclono2_a,4.59E-05 -PHOTO.jclono2_b,1.00E-05 -PHOTO.jco2,5.28E-30 -PHOTO.jcof2,3.30E-25 -PHOTO.jcofcl,2.30E-24 -PHOTO.jeooh,5.95E-06 -PHOTO.jglyald,6.18E-06 -PHOTO.jglyoxal,1.40E-04 -PHOTO.jh2402,3.68E-09 -PHOTO.jh2o2,7.98E-06 -PHOTO.jh2o_a,8.96E-31 -PHOTO.jh2o_b,0.00E+00 -PHOTO.jh2o_c,0.00E+00 -PHOTO.jh2so4,3.10E-10 -PHOTO.jhbr,2.55E-23 -PHOTO.jhcfc141b,4.36E-24 -PHOTO.jhcfc142b,3.98E-26 -PHOTO.jhcfc22,8.55E-27 -PHOTO.jhcl,6.59E-25 -PHOTO.jhf,0.00E+00 -PHOTO.jhno3,8.54E-07 -PHOTO.jho2no2_a,1.71E-06 -PHOTO.jho2no2_b,1.72E-05 -PHOTO.jhobr,2.37E-03 -PHOTO.jhocl,3.04E-04 -PHOTO.jhonitr,3.47E-05 -PHOTO.jhpald,6.03E-05 -PHOTO.jhyac,2.47E-06 -PHOTO.jisopnooh,5.95E-06 -PHOTO.jisopooh,5.95E-06 -PHOTO.jmacr_a,2.77E-06 -PHOTO.jmacr_b,2.77E-06 -PHOTO.jmek,1.25E-06 -PHOTO.jmekooh,5.95E-06 -PHOTO.jmgly,1.40E-04 -PHOTO.jmpan,9.69E-07 -PHOTO.jmvk,5.20E-06 -PHOTO.jn2o,9.25E-25 -PHOTO.jn2o5_a,5.54E-05 -PHOTO.jn2o5_b,3.76E-09 -PHOTO.jnc4cho,3.47E-05 -PHOTO.jno,1.0e-8 -PHOTO.jno2,1.01E-02 -PHOTO.jno3_a,1.99E-01 -PHOTO.jno3_b,1.24E-02 -PHOTO.jnoa,3.47E-05 -PHOTO.jnterpooh,5.95E-06 -PHOTO.jo2_a,0.00E+00 -PHOTO.jo2_b,1.40E-28 -PHOTO.jo3_a,5.13E-05 -PHOTO.jo3_b,4.49E-04 -PHOTO.joclo,7.26E-02 -PHOTO.jocs,6.18E-12 -PHOTO.jonitr,7.18E-06 -PHOTO.jpan,9.69E-07 -PHOTO.jphenooh,5.95E-06 -PHOTO.jpooh,5.95E-06 -PHOTO.jrooh,5.95E-06 -PHOTO.jso,2.58E-22 -PHOTO.jso2,1.18E-22 -PHOTO.jso3,2.36E-07 -PHOTO.jtepomuc,1.01E-03 -PHOTO.jterp2ooh,5.95E-06 -PHOTO.jterpnit,5.95E-06 -PHOTO.jterpooh,5.95E-06 -PHOTO.jterprd1,7.18E-06 -PHOTO.jterprd2,7.18E-06 -PHOTO.jtolooh,5.95E-06 -PHOTO.jxooh,5.95E-06 -PHOTO.jxylenooh,5.95E-06 -PHOTO.jxylolooh,5.95E-06 -PHOTO.jsf6,1.0e-20 -PHOTO.jsoa1_a1,1.0e-20 -PHOTO.jsoa1_a2,1.0e-20 -PHOTO.jsoa2_a1,1.0e-20 -PHOTO.jsoa2_a2,1.0e-20 -PHOTO.jsoa3_a1,1.0e-20 -PHOTO.jsoa3_a2,1.0e-20 -PHOTO.jsoa4_a1,1.0e-20 -PHOTO.jsoa4_a2,1.0e-20 -PHOTO.jsoa5_a1,1.0e-20 -PHOTO.jsoa5_a2,1.0e-20 -USER.het1,1.0E-20 -USER.het2,1.0E-20 -USER.het3,1.0E-20 -USER.het4,6.022140760000E-03 -USER.het5,6.022140760000E-03 -USER.het6,6.022140760000E-03 -USER.het7,1.0E-20 -USER.het8,1.0E-20 -USER.het9,6.022140760000E-03 -USER.het10,6.022140760000E-03 -USER.het11,1.0E-20 -USER.het12,1.0E-20 -USER.het13,1.0E-20 -USER.het14,1.0E-20 -USER.het15,6.022140760000E-03 -USER.het16,6.022140760000E-03 -USER.het17,6.022140760000E-03 -SURF.usr_NO2_aer,2.820947917738780E-08,1.0e11 -SURF.usr_HO2_aer,2.820947917738780E-08,1.0e11 -SURF.usr_NO3_aer,2.820947917738780E-08,1.0e11 -SURF.usr_ISOPNITA_aer,2.820947917738780E-08,1.0e11 -SURF.usr_ISOPNITB_aer,2.820947917738780E-08,1.0e11 -SURF.usr_GLYOXAL_aer,2.820947917738780E-08,1.0e11 -SURF.usr_ONITR_aer,2.820947917738780E-08,1.0e11 -SURF.usr_N2O5_aer,2.820947917738780E-08,1.0e11 -SURF.usr_HONITR_aer,2.820947917738780E-08,1.0e11 -SURF.usr_NC4CHO_aer,2.820947917738780E-08,1.0e11 -SURF.usr_TERPNIT_aer,2.820947917738780E-08,1.0e11 -SURF.usr_NTERPOOH_aer,2.820947917738780E-08,1.0e11 -SURF.usr_NC4CH2OH_aer,2.820947917738780E-08,1.0e11 +CONC.NO,0.000000001 +CONC.NO2,0.00000001 +CONC.HNO3,0.00000001 +CONC.O3,0.000002 +CONC.H2O2,0.00000005 +CONC.CO,0.000006 +CONC.SO2,0.00000003 +CONC.HCL,0.00000009 +CONC.CH4,0.00009 +CONC.ETHA,0.00000001 +CONC.FORM,0.00000009 +CONC.MEOH,0.000000009 +CONC.MEPX,0.00000002 +CONC.ALD2,0.00000001 +CONC.PAR,0.00000002 +CONC.ETH,0.000000002 +CONC.OLE,0.0000000004 +CONC.IOLE,0.00000000003 +CONC.TOL,0.000000001 +CONC.XYL,0.000000001 +CONC.NTR,0.000000001 +CONC.PAN,0.00000003 +CONC.AACD,0.000000002 +CONC.ROOH,0.000000001 +CONC.ISOP,0.0000002 +CONC.O2,56 +CONC.H2,0.00003 +CONC.H2O,128 +EMIS.NO,0.000000000144 +EMIS.NO2,0.00000000000756 +EMIS.CO,0.00000000196 +EMIS.SO2,0.00000000106 +EMIS.FORM,0.0000000000102 +EMIS.MEOH,0.000000000000592 +EMIS.ALD2,0.00000000000425 +EMIS.PAR,0.000000000427 +EMIS.ETH,0.0000000000462 +EMIS.OLE,0.0000000000149 +EMIS.IOLE,0.0000000000149 +EMIS.TOL,0.0000000000153 +EMIS.XYL,0.000000000014 +EMIS.ISOP,0.00000000000603 +PHOTO.NO2,0.00477 +PHOTO.O3->O1D,0.00000226 +PHOTO.O3->O3P,0.000252999999999999 +PHOTO.NO3->NO2,0.116999999999999 +PHOTO.NO3->NO,0.0144 +PHOTO.HONO,0.000918 +PHOTO.H2O2,0.00000259 +PHOTO.PNA,0.00000189 +PHOTO.HNO3,0.0000000861 +PHOTO.NTR,0.000000477 +PHOTO.ROOH,0.00000181 +PHOTO.MEPX,0.00000181 +PHOTO.FORM->HO2,0.00000793 +PHOTO.FORM->CO,0.000022 +PHOTO.ALD2,0.0000022 +PHOTO.PACD,0.00000181 +PHOTO.ALDX,0.0000022 +PHOTO.OPEN,0.000645 +PHOTO.MGLY,0.0000764 +PHOTO.ISPD,0.00000000198 ENV.temperature,287.45 ENV.pressure,101319.9 ENV.air_density,42.3934005 diff --git a/examples/configs/chapman/initial_conditions.csv b/examples/configs/chapman/initial_conditions.csv index 026fecfb7..eb291c269 100644 --- a/examples/configs/chapman/initial_conditions.csv +++ b/examples/configs/chapman/initial_conditions.csv @@ -1,12 +1,9 @@ -CONC.M,0.779127760953859 -CONC.O1D,0.925827943758015 -CONC.O,0.566253459937848 -CONC.O2,0.566253459937848 -CONC.O3,0.566253459937848 -ENV.temperature,227.0 -ENV.pressure,1200.0 -ENV.air_density,1e6 -ENV.time_step,3600 -PHOTO.R1,6.0e-11 -PHOTO.R3,1.0e-3 -PHOTO.R5,1.0e-3 \ No newline at end of file +CONC.O2,0.75 +CONC.O3,8.1e-6 +ENV.temperature,206.6374207 +ENV.pressure,6152.049805 +ENV.air_density,3.580772136E+00 +ENV.time_step,60 +PHOTO.R1,2.42e-17 +PHOTO.R3,1.15e-5 +PHOTO.R5,6.61e-9 \ No newline at end of file diff --git a/examples/example.cpp b/examples/example.cpp index 6f5c31fb9..33e5f9ef0 100644 --- a/examples/example.cpp +++ b/examples/example.cpp @@ -66,6 +66,7 @@ InitialConditions readCSVToMap(const std::string& filename) const std::string EMIS_PREFIX = "EMIS."; const std::string LOSS_PREFIX = "LOSS."; const std::string USER_PREFIX = "USER."; + const std::string SURF_PREFIX = "SURF."; constexpr int CONC_POS = 5; constexpr int ENV_POS = 4; @@ -140,7 +141,7 @@ InitialConditions readCSVToMap(const std::string& filename) return dataMap; } } - // Find custom rate constants + // Find custom rate constants that use UserDefinedRateConstant class else if (line.find(PHOTO_PREFIX) != std::string::npos || line.find(EMIS_PREFIX) != std::string::npos || line.find(LOSS_PREFIX) != std::string::npos @@ -149,7 +150,7 @@ InitialConditions readCSVToMap(const std::string& filename) delimiter_pos = line.find_last_of(','); if (delimiter_pos == std::string::npos) { - std::cerr << "Error: Unable to find the delimiter \',' in \"" << line << "\"" << std::endl; + std::cerr << "Error: Unable to find the delimiter ',' in \"" << line << "\"" << std::endl; dataMap.incomplete_parsing(); return dataMap; } @@ -169,6 +170,42 @@ InitialConditions readCSVToMap(const std::string& filename) return dataMap; } } + else if (line.find(SURF_PREFIX) != std::string::npos) + { + auto last_delimiter_pos = line.find_last_of(','); + auto second_last_delimiter_pos = line.substr(0,last_delimiter_pos-1).find_last_of(','); + if (last_delimiter_pos == std::string::npos || second_last_delimiter_pos == std::string::npos) + { + std::cerr << "Error: Unable to find both delimiters ',' in \"" << line << "\"" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + key = line.substr(0, second_last_delimiter_pos); + value_string = line.substr(second_last_delimiter_pos + 1, last_delimiter_pos - second_last_delimiter_pos); + try + { + value = std::stod(value_string); + dataMap.custom_rate_params[key + ".effective radius [m]"].emplace_back(value); + } + catch(const std::exception& e) + { + std::cerr << e.what() << ": Parsing Error: Unable to convert string to double for the value of '" << value_string << "'" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + value_string = line.substr(last_delimiter_pos + 1); + try + { + value = std::stod(value_string); + dataMap.custom_rate_params[key + ".particle number concentration [# m-3]"].emplace_back(value); + } + catch(const std::exception& e) + { + std::cerr << e.what() << ": Parsing Error: Unable to convert string to double for the value of '" << value_string << "'" << std::endl; + dataMap.incomplete_parsing(); + return dataMap; + } + } else { std::cerr << "Error: Unable to parse string \"" << line << "\"" << std::endl; @@ -216,6 +253,21 @@ int main(const int argc, const char *argv[]) } SolverParameters solver_params = solverConfig.GetSolverParams(); + + // add third-body species parameterizaton on air density + for (auto& species : solver_params.system_.gas_phase_.species_) + if (species.name_ == "M") + species.parameterize_ = [](const Conditions& c) { return c.air_density_; }; + for (auto& process : solver_params.processes_) + { + for (auto& reactant : process.reactants_) + if (reactant.name_ == "M") + reactant.parameterize_ = [](const Conditions& c) { return c.air_density_; }; + for (auto& product : process.products_) + if (product.first.name_ == "M") + product.first.parameterize_ = [](const Conditions& c) { return c.air_density_; }; + } + auto chemical_system = solver_params.system_; auto reactions = solver_params.processes_; @@ -256,6 +308,11 @@ int main(const int argc, const char *argv[]) auto result = solver.Solve(time_step - elapsed_solve_time, state); elapsed_solve_time = result.final_time_; state.variables_ = result.result_; + if (result.state_ != SolverState::Converged) + { + std::cout << "solver failed to converge" << std::endl; + return 1; + } } state.PrintState(time_step); From 794553cd4ffb51fe33fb393221cf12730e641013 Mon Sep 17 00:00:00 2001 From: Kyle Shores Date: Thu, 7 Dec 2023 11:22:00 -0600 Subject: [PATCH 310/318] adding FAIR badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 09b9ff62a..41645ae0d 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Model Independent Chemical Module. MICM can be used to configure and solve atmos [![Ubuntu](https://github.com/NCAR/micm/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/NCAR/micm/actions/workflows/ubuntu.yml) [![codecov](https://codecov.io/gh/NCAR/micm/branch/main/graph/badge.svg?token=ATGO4DKTMY)](https://codecov.io/gh/NCAR/micm) [![DOI](https://zenodo.org/badge/294492778.svg)](https://zenodo.org/badge/latestdoi/294492778) +[![FAIR checklist badge](https://fairsoftwarechecklist.net/badge.svg)](https://fairsoftwarechecklist.net/v0.2?f=31&a=32113&i=22322&r=123) + Copyright (C) 2018-2023 National Center for Atmospheric Research From cbb3e1c6bae17c52b291ff4db772a0c0cad0e793 Mon Sep 17 00:00:00 2001 From: Qina Tan <72781532+qinatan@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:33:59 -0700 Subject: [PATCH 311/318] Cuda alpha (#384) * ready to test * modified test * modified test * modified headers * added templates * minor bug * template issue * test failed * still failing * test passed * tests passed * cuda_process_set modified * added liscend * change made * function name change * added template to getSolver() function * test passed with EXPECT_EQ --------- Co-authored-by: Qina Tan Co-authored-by: Qina Tan Co-authored-by: Qina Tan Co-authored-by: Qina Tan Co-authored-by: Qina Tan Co-authored-by: Qina Tan Co-authored-by: Qina Tan Co-authored-by: Qina Tan --- include/micm/process/cuda_process_set.hpp | 1 + include/micm/solver/cuda_rosenbrock.cuh | 15 +++ include/micm/solver/cuda_rosenbrock.hpp | 70 +++++++++++ include/micm/solver/rosenbrock.hpp | 2 +- include/micm/solver/rosenbrock.inl | 1 + src/solver/CMakeLists.txt | 1 + src/solver/rosenbrock.cu | 63 ++++++++++ test/unit/solver/CMakeLists.txt | 1 + test/unit/solver/test_cuda_rosenbrock.cpp | 138 ++++++++++++++++++++++ test/unit/solver/test_rosenbrock.cpp | 2 +- 10 files changed, 292 insertions(+), 2 deletions(-) create mode 100644 include/micm/solver/cuda_rosenbrock.cuh create mode 100644 include/micm/solver/cuda_rosenbrock.hpp create mode 100644 src/solver/rosenbrock.cu create mode 100644 test/unit/solver/test_cuda_rosenbrock.cpp diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index 4217e7cdc..08e1a5f6e 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -13,6 +13,7 @@ namespace micm class CudaProcessSet : public ProcessSet { public: + CudaProcessSet() = default; /// @brief Create a process set calculator for a given set of processes /// @param processes Processes to create calculator for /// @param variable_map A mapping of species names to concentration index diff --git a/include/micm/solver/cuda_rosenbrock.cuh b/include/micm/solver/cuda_rosenbrock.cuh new file mode 100644 index 000000000..591960967 --- /dev/null +++ b/include/micm/solver/cuda_rosenbrock.cuh @@ -0,0 +1,15 @@ +// Copyright (C) 2023 National Center for Atmospheric Research +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +#include +namespace micm{ + namespace cuda{ + std::chrono::nanoseconds AlphaMinusJacobianDriver( + CudaSparseMatrixParam& sparseMatrix, + const std::vector jacobian_diagonal_elements, + double alpha); + + }//end cuda +}//end micm \ No newline at end of file diff --git a/include/micm/solver/cuda_rosenbrock.hpp b/include/micm/solver/cuda_rosenbrock.hpp new file mode 100644 index 000000000..6c46db1b4 --- /dev/null +++ b/include/micm/solver/cuda_rosenbrock.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace micm{ + + template< + template class MatrixPolicy = Matrix, + template class SparseMatrixPolicy = StandardSparseMatrix, + class LinearSolverPolicy = CudaLinearSolver, + class ProcessSetPolicy = CudaProcessSet> + +class CudaRosenbrockSolver : public RosenbrockSolver{ +///@brief Default constructor +public: +CudaRosenbrockSolver(); + + +CudaRosenbrockSolver(const System& system, + const std::vector& processes, + const RosenbrockSolverParameters& parameters) +: RosenbrockSolver(system, processes, parameters){}; + + +CudaRosenbrockSolver(const System& system, + const std::vector processes, + const RosenbrockSolverParameters& parameters, + const std::function, double)> create_linear_solver, + const std::function& , const std::map&)> create_process_set) +: RosenbrockSolver(system, processes, parameters, create_linear_solver, create_process_set){}; + +std::chrono::nanoseconds AlphaMinusJacobian(SparseMatrixPolicy& jacobian, double alpha) const +requires VectorizableSparse> +{ + for (auto& element : jacobian.AsVector()) + element = -element; + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.jacobian_ = jacobian.AsVector().data(); + sparseMatrix.jacobian_size_ = jacobian.AsVector().size(); + sparseMatrix.n_grids_ = jacobian.size(); + + return micm::cuda::AlphaMinusJacobianDriver(sparseMatrix, + this->state_parameters_.jacobian_diagonal_elements_, alpha); + + } + }; //end CudaRosenbrockSolver +}//end micm \ No newline at end of file diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index ab1bc21f9..8eb1e2bf4 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -115,7 +115,7 @@ namespace micm /// @brief The final time the solver iterated to double final_time_{}; }; - + std::vector processes_; RosenbrockSolverParameters parameters_; StateParameters state_parameters_; diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index dcf801959..bbc2077b8 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -388,6 +388,7 @@ namespace micm const std::size_t n_cells = jacobian.GroupVectorSize(); for (auto& elem : jacobian.AsVector()) elem = -elem; + for (std::size_t i_group = 0; i_group < jacobian.NumberOfGroups(jacobian.size()); ++i_group) { auto jacobian_vector = std::next(jacobian.AsVector().begin(), i_group * jacobian.GroupSize(jacobian.FlatBlockSize())); diff --git a/src/solver/CMakeLists.txt b/src/solver/CMakeLists.txt index 2c5ca20ee..7bf198c06 100644 --- a/src/solver/CMakeLists.txt +++ b/src/solver/CMakeLists.txt @@ -3,5 +3,6 @@ if(ENABLE_CUDA) PRIVATE lu_decomposition.cu linear_solver.cu + rosenbrock.cu ) endif() \ No newline at end of file diff --git a/src/solver/rosenbrock.cu b/src/solver/rosenbrock.cu new file mode 100644 index 000000000..41fb6b831 --- /dev/null +++ b/src/solver/rosenbrock.cu @@ -0,0 +1,63 @@ +// Copyright (C) 2023 National Center for Atmospheric Research +// SPDX-License-Identifier: Apache-2.0 +#pragma once +#include +#include +#include +#include + +namespace micm{ + namespace cuda{ + __global__ void AlphaMinusJacobianKernel(size_t n_grids, + double* d_jacobian, + size_t* d_jacobian_diagonal_elements, + size_t jacobian_diagonal_elements_size, + double alpha) +{ + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + if (tid < n_grids) + { + for (int j = 0; j < jacobian_diagonal_elements_size; j++) + { + size_t jacobian_index = d_jacobian_diagonal_elements[j]; + d_jacobian[jacobian_index + tid] += alpha; + } + } +} + + std::chrono::nanoseconds AlphaMinusJacobianDriver( + CudaSparseMatrixParam& sparseMatrix, + const std::vector jacobian_diagonal_elements, + double alpha) + { + //device pointers + double* d_jacobian; + size_t* d_jacobian_diagonal_elements; + cudaMalloc(&d_jacobian, sizeof(double)* sparseMatrix.jacobian_size_); + cudaMalloc(&d_jacobian_diagonal_elements, sizeof(size_t)* jacobian_diagonal_elements.size()); + cudaMemcpy(d_jacobian, sparseMatrix.jacobian_, sizeof(double)* sparseMatrix.jacobian_size_, cudaMemcpyHostToDevice); + cudaMemcpy(d_jacobian_diagonal_elements, jacobian_diagonal_elements.data(), sizeof(size_t)* jacobian_diagonal_elements.size(), cudaMemcpyHostToDevice); + + //kernel call + size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + auto startTime = std::chrono::high_resolution_clock::now(); + AlphaMinusJacobianKernel<<>>(sparseMatrix.n_grids_, + d_jacobian, + d_jacobian_diagonal_elements, + jacobian_diagonal_elements.size(), + alpha); + + + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(sparseMatrix.jacobian_, d_jacobian, sizeof(double)* sparseMatrix.jacobian_size_, cudaMemcpyDeviceToHost); + cudaFree(d_jacobian); + cudaFree(d_jacobian_diagonal_elements); + + return kernel_duration; + + } + + }// end cuda +}// end mimc diff --git a/test/unit/solver/CMakeLists.txt b/test/unit/solver/CMakeLists.txt index f2302671e..f29189603 100644 --- a/test/unit/solver/CMakeLists.txt +++ b/test/unit/solver/CMakeLists.txt @@ -16,6 +16,7 @@ create_standard_test(NAME state SOURCES test_state.cpp) if(ENABLE_CUDA) create_standard_test(NAME cuda_lu_decomposition SOURCES test_cuda_lu_decomposition.cpp LIBRARIES musica::micm_cuda) create_standard_test(NAME cuda_linear_solver SOURCES test_cuda_linear_solver.cpp LIBRARIES musica::micm_cuda) + create_standard_test(NAME cuda_rosenbrock SOURCES test_cuda_rosenbrock.cpp LIBRARIES musica::micm_cuda) endif() if(ENABLE_LLVM) diff --git a/test/unit/solver/test_cuda_rosenbrock.cpp b/test/unit/solver/test_cuda_rosenbrock.cpp new file mode 100644 index 000000000..8756d6473 --- /dev/null +++ b/test/unit/solver/test_cuda_rosenbrock.cpp @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +template +using Group1VectorMatrix = micm::VectorMatrix; +template +using Group2VectorMatrix = micm::VectorMatrix; +template +using Group3VectorMatrix = micm::VectorMatrix; +template +using Group4VectorMatrix = micm::VectorMatrix; + +template +using Group1SparseVectorMatrix = micm::SparseMatrix>; +template +using Group2SparseVectorMatrix = micm::SparseMatrix>; +template +using Group3SparseVectorMatrix = micm::SparseMatrix>; +template +using Group4SparseVectorMatrix = micm::SparseMatrix>; + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class RosenbrockPolicy> +RosenbrockPolicy getSolver(std::size_t number_of_grid_cells) +{ + // ---- foo bar baz quz quuz + // foo 0 1 2 - - + // bar 3 4 5 - - + // baz 6 - 7 - - + // quz - 8 - 9 - + // quuz 10 - 11 - 12 + + auto foo = micm::Species("foo"); + auto bar = micm::Species("bar"); + auto baz = micm::Species("baz"); + auto quz = micm::Species("quz"); + auto quuz = micm::Species("quuz"); + + micm::Phase gas_phase{ std::vector{ foo, bar, baz, quz, quuz } }; + + micm::Process r1 = micm::Process::create() + .reactants({ foo, baz }) + .products({ yields(bar, 1), yields(quuz, 2.4) }) + .phase(gas_phase) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 2.0e-11, .B_ = 0, .C_ = 110 })); + + micm::Process r2 = micm::Process::create() + .reactants({ bar }) + .products({ yields(foo, 1), yields(quz, 1.4) }) + .phase(gas_phase) + .rate_constant(micm::ArrheniusRateConstant({ .A_ = 1.0e-6 })); + + micm::Process r3 = micm::Process::create().reactants({ quz }).products({}).phase(gas_phase).rate_constant( + micm::ArrheniusRateConstant({ .A_ = 3.5e-6 })); + + return RosenbrockPolicy( + micm::System(micm::SystemParameters{ .gas_phase_ = gas_phase }), + std::vector{ r1, r2, r3 }, + micm::RosenbrockSolverParameters::three_stage_rosenbrock_parameters(number_of_grid_cells, false)); +} + +template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> +void testAlphaMinusJacobian(std::size_t number_of_grid_cells) +{ + auto gpu_solver = getSolver>(number_of_grid_cells); + auto jacobian = gpu_solver.GetState().jacobian_; + EXPECT_EQ(jacobian.size(), number_of_grid_cells); + EXPECT_EQ(jacobian[0].size(), 5); + EXPECT_EQ(jacobian[0][0].size(), 5); + EXPECT_GE(jacobian.AsVector().size(), 13 * number_of_grid_cells); + for (auto& elem : jacobian.AsVector()) + elem = 100.0; + for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) + { + jacobian[i_cell][0][0] = 12.2; + jacobian[i_cell][0][1] = 24.3 * (i_cell + 2); + jacobian[i_cell][0][2] = 42.3; + jacobian[i_cell][1][0] = 0.43; + jacobian[i_cell][1][1] = 23.4; + jacobian[i_cell][1][2] = 83.4 / (i_cell + 3); + jacobian[i_cell][2][0] = 4.74; + jacobian[i_cell][2][2] = 6.91; + jacobian[i_cell][3][1] = 59.1; + jacobian[i_cell][3][3] = 83.4; + jacobian[i_cell][4][0] = 78.5; + jacobian[i_cell][4][2] = 53.6; + jacobian[i_cell][4][4] = 1.0; + } + auto cpu_jacobian = jacobian; + + gpu_solver.AlphaMinusJacobian(jacobian, 42.042); + for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) + { + EXPECT_EQ(jacobian[i_cell][0][0], 42.042 - 12.2); + EXPECT_EQ(jacobian[i_cell][0][1], -24.3 * (i_cell + 2)); + EXPECT_EQ(jacobian[i_cell][0][2], -42.3); + EXPECT_EQ(jacobian[i_cell][1][0], -0.43); + EXPECT_EQ(jacobian[i_cell][1][1], 42.042 - 23.4); + EXPECT_EQ(jacobian[i_cell][1][2], -83.4 / (i_cell + 3)); + EXPECT_EQ(jacobian[i_cell][2][0], -4.74); + EXPECT_EQ(jacobian[i_cell][2][2], 42.042 - 6.91); + EXPECT_EQ(jacobian[i_cell][3][1], -59.1); + EXPECT_EQ(jacobian[i_cell][3][3], 42.042 - 83.4); + EXPECT_EQ(jacobian[i_cell][4][0], -78.5); + EXPECT_EQ(jacobian[i_cell][4][2], -53.6); + EXPECT_EQ(jacobian[i_cell][4][4], 42.042 - 1.0); + } + + auto cpu_solver = getSolver, micm::RosenbrockSolver>>(number_of_grid_cells); + cpu_solver.AlphaMinusJacobian(cpu_jacobian, 42.042); + std::vector jacobian_gpu_vector = jacobian.AsVector(); + std::vector jacobian_cpu_vector = cpu_jacobian.AsVector(); + for (int i = 0; i < jacobian_cpu_vector.size(); i++){ + EXPECT_EQ(jacobian_cpu_vector[i], jacobian_gpu_vector[i]); + } + +} + +TEST(RosenbrockSolver, DenseAlphaMinusJacobian) +{ + testAlphaMinusJacobian>( + 1); + testAlphaMinusJacobian>( + 2); + testAlphaMinusJacobian>( + 3); + testAlphaMinusJacobian>( + 4); +} + diff --git a/test/unit/solver/test_rosenbrock.cpp b/test/unit/solver/test_rosenbrock.cpp index c54fb0f1d..cb3731d39 100644 --- a/test/unit/solver/test_rosenbrock.cpp +++ b/test/unit/solver/test_rosenbrock.cpp @@ -1,5 +1,4 @@ #include - #include #include #include @@ -96,6 +95,7 @@ void testAlphaMinusJacobian(std::size_t number_of_grid_cells) } } + TEST(RosenbrockSolver, StandardAlphaMinusJacobian) { testAlphaMinusJacobian>(1); From 76f6ce220ac14b3a52651b10ecc7bf360acfc88d Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Wed, 20 Dec 2023 22:34:21 +0000 Subject: [PATCH 312/318] Auto-format code using Clang-Format --- include/micm/solver/cuda_rosenbrock.hpp | 88 ++++++++++++---------- include/micm/solver/rosenbrock.hpp | 2 +- src/solver/rosenbrock.cu | 92 ++++++++++++----------- test/unit/solver/test_cuda_rosenbrock.cpp | 63 ++++++++++------ test/unit/solver/test_rosenbrock.cpp | 2 +- 5 files changed, 140 insertions(+), 107 deletions(-) diff --git a/include/micm/solver/cuda_rosenbrock.hpp b/include/micm/solver/cuda_rosenbrock.hpp index 6c46db1b4..89158e628 100644 --- a/include/micm/solver/cuda_rosenbrock.hpp +++ b/include/micm/solver/cuda_rosenbrock.hpp @@ -1,8 +1,5 @@ -#pragma once +#pragma once -#include -#include -#include #include #include #include @@ -11,60 +8,73 @@ #include #include #include +#include #include #include +#include +#include #include +#include #include #include #include +#include #include #include #include #include #include -#include -#include -namespace micm{ +namespace micm +{ - template< + template< template class MatrixPolicy = Matrix, template class SparseMatrixPolicy = StandardSparseMatrix, class LinearSolverPolicy = CudaLinearSolver, class ProcessSetPolicy = CudaProcessSet> -class CudaRosenbrockSolver : public RosenbrockSolver{ -///@brief Default constructor -public: -CudaRosenbrockSolver(); + class CudaRosenbrockSolver + : public RosenbrockSolver + { + ///@brief Default constructor + public: + CudaRosenbrockSolver(); + CudaRosenbrockSolver( + const System& system, + const std::vector& processes, + const RosenbrockSolverParameters& parameters) + : RosenbrockSolver( + system, + processes, + parameters){}; -CudaRosenbrockSolver(const System& system, - const std::vector& processes, - const RosenbrockSolverParameters& parameters) -: RosenbrockSolver(system, processes, parameters){}; + CudaRosenbrockSolver( + const System& system, + const std::vector processes, + const RosenbrockSolverParameters& parameters, + const std::function, double)> create_linear_solver, + const std::function&, const std::map&)> + create_process_set) + : RosenbrockSolver( + system, + processes, + parameters, + create_linear_solver, + create_process_set){}; + std::chrono::nanoseconds AlphaMinusJacobian(SparseMatrixPolicy& jacobian, double alpha) const requires + VectorizableSparse> + { + for (auto& element : jacobian.AsVector()) + element = -element; + CudaSparseMatrixParam sparseMatrix; + sparseMatrix.jacobian_ = jacobian.AsVector().data(); + sparseMatrix.jacobian_size_ = jacobian.AsVector().size(); + sparseMatrix.n_grids_ = jacobian.size(); -CudaRosenbrockSolver(const System& system, - const std::vector processes, - const RosenbrockSolverParameters& parameters, - const std::function, double)> create_linear_solver, - const std::function& , const std::map&)> create_process_set) -: RosenbrockSolver(system, processes, parameters, create_linear_solver, create_process_set){}; - -std::chrono::nanoseconds AlphaMinusJacobian(SparseMatrixPolicy& jacobian, double alpha) const -requires VectorizableSparse> -{ - for (auto& element : jacobian.AsVector()) - element = -element; - CudaSparseMatrixParam sparseMatrix; - sparseMatrix.jacobian_ = jacobian.AsVector().data(); - sparseMatrix.jacobian_size_ = jacobian.AsVector().size(); - sparseMatrix.n_grids_ = jacobian.size(); - - return micm::cuda::AlphaMinusJacobianDriver(sparseMatrix, - this->state_parameters_.jacobian_diagonal_elements_, alpha); - - } - }; //end CudaRosenbrockSolver -}//end micm \ No newline at end of file + return micm::cuda::AlphaMinusJacobianDriver(sparseMatrix, this->state_parameters_.jacobian_diagonal_elements_, alpha); + } + }; // end CudaRosenbrockSolver +} // namespace micm \ No newline at end of file diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 8eb1e2bf4..ab1bc21f9 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -115,7 +115,7 @@ namespace micm /// @brief The final time the solver iterated to double final_time_{}; }; - + std::vector processes_; RosenbrockSolverParameters parameters_; StateParameters state_parameters_; diff --git a/src/solver/rosenbrock.cu b/src/solver/rosenbrock.cu index 41fb6b831..8992221b6 100644 --- a/src/solver/rosenbrock.cu +++ b/src/solver/rosenbrock.cu @@ -2,62 +2,64 @@ // SPDX-License-Identifier: Apache-2.0 #pragma once #include -#include #include #include +#include -namespace micm{ - namespace cuda{ - __global__ void AlphaMinusJacobianKernel(size_t n_grids, - double* d_jacobian, - size_t* d_jacobian_diagonal_elements, - size_t jacobian_diagonal_elements_size, - double alpha) +namespace micm { - size_t tid = blockIdx.x * blockDim.x + threadIdx.x; - if (tid < n_grids) + namespace cuda + { + __global__ void AlphaMinusJacobianKernel( + size_t n_grids, + double* d_jacobian, + size_t* d_jacobian_diagonal_elements, + size_t jacobian_diagonal_elements_size, + double alpha) { + size_t tid = blockIdx.x * blockDim.x + threadIdx.x; + if (tid < n_grids) + { for (int j = 0; j < jacobian_diagonal_elements_size; j++) { - size_t jacobian_index = d_jacobian_diagonal_elements[j]; - d_jacobian[jacobian_index + tid] += alpha; + size_t jacobian_index = d_jacobian_diagonal_elements[j]; + d_jacobian[jacobian_index + tid] += alpha; } - } -} - - std::chrono::nanoseconds AlphaMinusJacobianDriver( - CudaSparseMatrixParam& sparseMatrix, - const std::vector jacobian_diagonal_elements, - double alpha) - { - //device pointers - double* d_jacobian; - size_t* d_jacobian_diagonal_elements; - cudaMalloc(&d_jacobian, sizeof(double)* sparseMatrix.jacobian_size_); - cudaMalloc(&d_jacobian_diagonal_elements, sizeof(size_t)* jacobian_diagonal_elements.size()); - cudaMemcpy(d_jacobian, sparseMatrix.jacobian_, sizeof(double)* sparseMatrix.jacobian_size_, cudaMemcpyHostToDevice); - cudaMemcpy(d_jacobian_diagonal_elements, jacobian_diagonal_elements.data(), sizeof(size_t)* jacobian_diagonal_elements.size(), cudaMemcpyHostToDevice); + } + } - //kernel call - size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; - auto startTime = std::chrono::high_resolution_clock::now(); - AlphaMinusJacobianKernel<<>>(sparseMatrix.n_grids_, - d_jacobian, - d_jacobian_diagonal_elements, - jacobian_diagonal_elements.size(), - alpha); + std::chrono::nanoseconds AlphaMinusJacobianDriver( + CudaSparseMatrixParam& sparseMatrix, + const std::vector jacobian_diagonal_elements, + double alpha) + { + // device pointers + double* d_jacobian; + size_t* d_jacobian_diagonal_elements; + cudaMalloc(&d_jacobian, sizeof(double) * sparseMatrix.jacobian_size_); + cudaMalloc(&d_jacobian_diagonal_elements, sizeof(size_t) * jacobian_diagonal_elements.size()); + cudaMemcpy(d_jacobian, sparseMatrix.jacobian_, sizeof(double) * sparseMatrix.jacobian_size_, cudaMemcpyHostToDevice); + cudaMemcpy( + d_jacobian_diagonal_elements, + jacobian_diagonal_elements.data(), + sizeof(size_t) * jacobian_diagonal_elements.size(), + cudaMemcpyHostToDevice); + // kernel call + size_t num_block = (sparseMatrix.n_grids_ + BLOCK_SIZE - 1) / BLOCK_SIZE; + auto startTime = std::chrono::high_resolution_clock::now(); + AlphaMinusJacobianKernel<<>>( + sparseMatrix.n_grids_, d_jacobian, d_jacobian_diagonal_elements, jacobian_diagonal_elements.size(), alpha); - cudaDeviceSynchronize(); - auto endTime = std::chrono::high_resolution_clock::now(); - auto kernel_duration = std::chrono::duration_cast(endTime - startTime); - cudaMemcpy(sparseMatrix.jacobian_, d_jacobian, sizeof(double)* sparseMatrix.jacobian_size_, cudaMemcpyDeviceToHost); - cudaFree(d_jacobian); - cudaFree(d_jacobian_diagonal_elements); - - return kernel_duration; + cudaDeviceSynchronize(); + auto endTime = std::chrono::high_resolution_clock::now(); + auto kernel_duration = std::chrono::duration_cast(endTime - startTime); + cudaMemcpy(sparseMatrix.jacobian_, d_jacobian, sizeof(double) * sparseMatrix.jacobian_size_, cudaMemcpyDeviceToHost); + cudaFree(d_jacobian); + cudaFree(d_jacobian_diagonal_elements); + return kernel_duration; } - }// end cuda -}// end mimc + } // namespace cuda +} // namespace micm diff --git a/test/unit/solver/test_cuda_rosenbrock.cpp b/test/unit/solver/test_cuda_rosenbrock.cpp index 8756d6473..7c897157e 100644 --- a/test/unit/solver/test_cuda_rosenbrock.cpp +++ b/test/unit/solver/test_cuda_rosenbrock.cpp @@ -1,14 +1,13 @@ #include + #include +#include #include #include -#include #include #include #include #include -#include - template using Group1VectorMatrix = micm::VectorMatrix; @@ -28,7 +27,13 @@ using Group3SparseVectorMatrix = micm::SparseMatrix using Group4SparseVectorMatrix = micm::SparseMatrix>; -template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy, class RosenbrockPolicy> +template< + template + class MatrixPolicy, + template + class SparseMatrixPolicy, + class LinearSolverPolicy, + class RosenbrockPolicy> RosenbrockPolicy getSolver(std::size_t number_of_grid_cells) { // ---- foo bar baz quz quuz @@ -70,7 +75,11 @@ RosenbrockPolicy getSolver(std::size_t number_of_grid_cells) template class MatrixPolicy, template class SparseMatrixPolicy, class LinearSolverPolicy> void testAlphaMinusJacobian(std::size_t number_of_grid_cells) { - auto gpu_solver = getSolver>(number_of_grid_cells); + auto gpu_solver = getSolver< + MatrixPolicy, + SparseMatrixPolicy, + LinearSolverPolicy, + micm::CudaRosenbrockSolver>(number_of_grid_cells); auto jacobian = gpu_solver.GetState().jacobian_; EXPECT_EQ(jacobian.size(), number_of_grid_cells); EXPECT_EQ(jacobian[0].size(), 5); @@ -94,8 +103,8 @@ void testAlphaMinusJacobian(std::size_t number_of_grid_cells) jacobian[i_cell][4][2] = 53.6; jacobian[i_cell][4][4] = 1.0; } - auto cpu_jacobian = jacobian; - + auto cpu_jacobian = jacobian; + gpu_solver.AlphaMinusJacobian(jacobian, 42.042); for (std::size_t i_cell = 0; i_cell < number_of_grid_cells; ++i_cell) { @@ -114,25 +123,37 @@ void testAlphaMinusJacobian(std::size_t number_of_grid_cells) EXPECT_EQ(jacobian[i_cell][4][4], 42.042 - 1.0); } - auto cpu_solver = getSolver, micm::RosenbrockSolver>>(number_of_grid_cells); + auto cpu_solver = getSolver< + MatrixPolicy, + SparseMatrixPolicy, + micm::LinearSolver, + micm::RosenbrockSolver>>( + number_of_grid_cells); cpu_solver.AlphaMinusJacobian(cpu_jacobian, 42.042); - std::vector jacobian_gpu_vector = jacobian.AsVector(); - std::vector jacobian_cpu_vector = cpu_jacobian.AsVector(); - for (int i = 0; i < jacobian_cpu_vector.size(); i++){ + std::vector jacobian_gpu_vector = jacobian.AsVector(); + std::vector jacobian_cpu_vector = cpu_jacobian.AsVector(); + for (int i = 0; i < jacobian_cpu_vector.size(); i++) + { EXPECT_EQ(jacobian_cpu_vector[i], jacobian_gpu_vector[i]); } - } TEST(RosenbrockSolver, DenseAlphaMinusJacobian) { - testAlphaMinusJacobian>( - 1); - testAlphaMinusJacobian>( - 2); - testAlphaMinusJacobian>( - 3); - testAlphaMinusJacobian>( - 4); + testAlphaMinusJacobian< + Group1VectorMatrix, + Group1SparseVectorMatrix, + micm::CudaLinearSolver>(1); + testAlphaMinusJacobian< + Group2VectorMatrix, + Group2SparseVectorMatrix, + micm::CudaLinearSolver>(2); + testAlphaMinusJacobian< + Group3VectorMatrix, + Group3SparseVectorMatrix, + micm::CudaLinearSolver>(3); + testAlphaMinusJacobian< + Group4VectorMatrix, + Group4SparseVectorMatrix, + micm::CudaLinearSolver>(4); } - diff --git a/test/unit/solver/test_rosenbrock.cpp b/test/unit/solver/test_rosenbrock.cpp index cb3731d39..c54fb0f1d 100644 --- a/test/unit/solver/test_rosenbrock.cpp +++ b/test/unit/solver/test_rosenbrock.cpp @@ -1,4 +1,5 @@ #include + #include #include #include @@ -95,7 +96,6 @@ void testAlphaMinusJacobian(std::size_t number_of_grid_cells) } } - TEST(RosenbrockSolver, StandardAlphaMinusJacobian) { testAlphaMinusJacobian>(1); From 14f71ab31b0b3bd76848929495250d751791b1bb Mon Sep 17 00:00:00 2001 From: Jiwon Gim Date: Tue, 2 Jan 2024 16:04:39 -0700 Subject: [PATCH 313/318] Happy new year\! --- README.md | 4 ++-- include/micm/configure/solver_config.hpp | 2 +- include/micm/jit/jit_compiler.hpp | 2 +- include/micm/jit/jit_function.hpp | 2 +- include/micm/process/arrhenius_rate_constant.hpp | 2 +- include/micm/process/branched_rate_constant.hpp | 2 +- include/micm/process/cuda_process_set.cuh | 2 +- include/micm/process/cuda_process_set.hpp | 2 +- include/micm/process/jit_process_set.hpp | 2 +- include/micm/process/process.hpp | 2 +- include/micm/process/process_set.hpp | 2 +- include/micm/process/rate_constant.hpp | 2 +- include/micm/process/surface_rate_constant.hpp | 2 +- .../process/ternary_chemical_activation_rate_constant.hpp | 2 +- include/micm/process/troe_rate_constant.hpp | 2 +- include/micm/process/tunneling_rate_constant.hpp | 2 +- include/micm/process/user_defined_rate_constant.hpp | 2 +- include/micm/solver/cuda_linear_solver.cuh | 2 +- include/micm/solver/cuda_linear_solver.hpp | 2 +- include/micm/solver/cuda_lu_decomposition.cuh | 2 +- include/micm/solver/cuda_lu_decomposition.hpp | 2 +- include/micm/solver/cuda_rosenbrock.cuh | 2 +- include/micm/solver/jit_linear_solver.hpp | 2 +- include/micm/solver/jit_linear_solver.inl | 2 +- include/micm/solver/jit_lu_decomposition.hpp | 2 +- include/micm/solver/jit_lu_decomposition.inl | 2 +- include/micm/solver/jit_rosenbrock.hpp | 2 +- include/micm/solver/linear_solver.hpp | 2 +- include/micm/solver/linear_solver.inl | 2 +- include/micm/solver/lu_decomposition.hpp | 2 +- include/micm/solver/lu_decomposition.inl | 2 +- include/micm/solver/rosenbrock.hpp | 2 +- include/micm/solver/rosenbrock.inl | 2 +- include/micm/solver/rosenbrock_solver_parameters.hpp | 2 +- include/micm/solver/state.inl | 2 +- include/micm/system/conditions.hpp | 2 +- include/micm/system/phase.hpp | 2 +- include/micm/system/species.hpp | 2 +- include/micm/system/system.hpp | 2 +- include/micm/util/constants.hpp | 2 +- include/micm/util/cuda_param.hpp | 2 +- include/micm/util/jacobian.hpp | 2 +- include/micm/util/matrix.hpp | 2 +- include/micm/util/property_keys.hpp | 2 +- include/micm/util/random_string.hpp | 2 +- include/micm/util/sparse_matrix.hpp | 2 +- include/micm/util/sparse_matrix_standard_ordering.hpp | 2 +- include/micm/util/sparse_matrix_vector_ordering.hpp | 2 +- include/micm/util/vector_matrix.hpp | 2 +- src/process/process_set.cu | 2 +- src/solver/linear_solver.cu | 2 +- src/solver/lu_decomposition.cu | 2 +- src/solver/rosenbrock.cu | 2 +- test/regression/RosenbrockChapman/chapman_ode_solver.hpp | 2 +- test/unit/process/forcing_calculation.cpp | 2 +- test/unit/process/forcing_calculation.hpp | 2 +- test/unit/process/test_jit_forcing_calculation.cpp | 2 +- test/unit/process/test_process.cpp | 2 +- 58 files changed, 59 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 41645ae0d..db14c1c9e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Model Independent Chemical Module. MICM can be used to configure and solve atmos [![FAIR checklist badge](https://fairsoftwarechecklist.net/badge.svg)](https://fairsoftwarechecklist.net/v0.2?f=31&a=32113&i=22322&r=123) -Copyright (C) 2018-2023 National Center for Atmospheric Research +Copyright (C) 2018-2024 National Center for Atmospheric Research

      @@ -240,4 +240,4 @@ installation and usage instructions. - [Apache 2.0](/LICENSE) -Copyright (C) 2018-2023 National Center for Atmospheric Research +Copyright (C) 2018-2024 National Center for Atmospheric Research diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 3d5621278..28c439bbd 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index 5c2180879..d085bbee3 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 // diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp index 1a71a965e..880187510 100644 --- a/include/micm/jit/jit_function.hpp +++ b/include/micm/jit/jit_function.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/arrhenius_rate_constant.hpp b/include/micm/process/arrhenius_rate_constant.hpp index ca9f7b667..aae5937ae 100644 --- a/include/micm/process/arrhenius_rate_constant.hpp +++ b/include/micm/process/arrhenius_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/branched_rate_constant.hpp b/include/micm/process/branched_rate_constant.hpp index bbb0738b8..457206942 100644 --- a/include/micm/process/branched_rate_constant.hpp +++ b/include/micm/process/branched_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/cuda_process_set.cuh b/include/micm/process/cuda_process_set.cuh index 38cb81113..524a57d5d 100644 --- a/include/micm/process/cuda_process_set.cuh +++ b/include/micm/process/cuda_process_set.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index 08e1a5f6e..9eb8303e6 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 5aac29b9c..504ab4af2 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index cf86a4692..7976e486e 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 53bf1fbbf..612dba6b6 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/rate_constant.hpp b/include/micm/process/rate_constant.hpp index 77be53503..153f4a979 100644 --- a/include/micm/process/rate_constant.hpp +++ b/include/micm/process/rate_constant.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index 8049d3846..ed7fb271c 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index 7a2ea4623..918ae1956 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/troe_rate_constant.hpp b/include/micm/process/troe_rate_constant.hpp index ff2b08715..9094483a2 100644 --- a/include/micm/process/troe_rate_constant.hpp +++ b/include/micm/process/troe_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/tunneling_rate_constant.hpp b/include/micm/process/tunneling_rate_constant.hpp index 0b07bae52..371230229 100644 --- a/include/micm/process/tunneling_rate_constant.hpp +++ b/include/micm/process/tunneling_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index 480c6f407..5aac41195 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_linear_solver.cuh b/include/micm/solver/cuda_linear_solver.cuh index f2014dbb0..6ba053c40 100644 --- a/include/micm/solver/cuda_linear_solver.cuh +++ b/include/micm/solver/cuda_linear_solver.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/include/micm/solver/cuda_linear_solver.hpp b/include/micm/solver/cuda_linear_solver.hpp index fe80cd954..b85357f7d 100644 --- a/include/micm/solver/cuda_linear_solver.hpp +++ b/include/micm/solver/cuda_linear_solver.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_lu_decomposition.cuh b/include/micm/solver/cuda_lu_decomposition.cuh index 70ff61fbe..f4f5b94da 100644 --- a/include/micm/solver/cuda_lu_decomposition.cuh +++ b/include/micm/solver/cuda_lu_decomposition.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index 6315b3d44..386b67c04 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_rosenbrock.cuh b/include/micm/solver/cuda_rosenbrock.cuh index 591960967..906efe06c 100644 --- a/include/micm/solver/cuda_rosenbrock.cuh +++ b/include/micm/solver/cuda_rosenbrock.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once #include diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index 017b053ca..8a76f3f0b 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index 01da3852e..5480a95c2 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index e5b2959ab..8680c1e5e 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/jit_lu_decomposition.inl b/include/micm/solver/jit_lu_decomposition.inl index 71677ac28..b07f0077d 100644 --- a/include/micm/solver/jit_lu_decomposition.inl +++ b/include/micm/solver/jit_lu_decomposition.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 633fca4d8..53dfa635f 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 * diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index 9af334502..b3064feb5 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index 74d2724c1..942d271dd 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/lu_decomposition.hpp b/include/micm/solver/lu_decomposition.hpp index 79681e2e4..10b35bb4b 100644 --- a/include/micm/solver/lu_decomposition.hpp +++ b/include/micm/solver/lu_decomposition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/lu_decomposition.inl b/include/micm/solver/lu_decomposition.inl index 0483970ef..6517b37af 100644 --- a/include/micm/solver/lu_decomposition.inl +++ b/include/micm/solver/lu_decomposition.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index ab1bc21f9..91e0c5b31 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 * diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index bbc2077b8..ab2c01ab5 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #define TIMED_METHOD(assigned_increment, time_it, method, ...) \ diff --git a/include/micm/solver/rosenbrock_solver_parameters.hpp b/include/micm/solver/rosenbrock_solver_parameters.hpp index 2b00af10f..3912c6d40 100644 --- a/include/micm/solver/rosenbrock_solver_parameters.hpp +++ b/include/micm/solver/rosenbrock_solver_parameters.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index 96be1d2b0..1ce7a0359 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/system/conditions.hpp b/include/micm/system/conditions.hpp index e077b73cf..1e3c19ce4 100644 --- a/include/micm/system/conditions.hpp +++ b/include/micm/system/conditions.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/system/phase.hpp b/include/micm/system/phase.hpp index f81add256..bdf5fd6d3 100644 --- a/include/micm/system/phase.hpp +++ b/include/micm/system/phase.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/system/species.hpp b/include/micm/system/species.hpp index 6dd194383..0c0b5f653 100644 --- a/include/micm/system/species.hpp +++ b/include/micm/system/species.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/system/system.hpp b/include/micm/system/system.hpp index e1461190f..a2287c160 100644 --- a/include/micm/system/system.hpp +++ b/include/micm/system/system.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/util/constants.hpp b/include/micm/util/constants.hpp index e8315b64a..5fc8abd6c 100644 --- a/include/micm/util/constants.hpp +++ b/include/micm/util/constants.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index 2f680e915..4962a9302 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/include/micm/util/jacobian.hpp b/include/micm/util/jacobian.hpp index ad18dc3d4..b3af0e338 100644 --- a/include/micm/util/jacobian.hpp +++ b/include/micm/util/jacobian.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index 1d2f4afc1..957f216dc 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/property_keys.hpp b/include/micm/util/property_keys.hpp index ee494d2ad..8e93bb78a 100644 --- a/include/micm/util/property_keys.hpp +++ b/include/micm/util/property_keys.hpp @@ -1,5 +1,5 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/random_string.hpp b/include/micm/util/random_string.hpp index b3f011107..01cdb38ae 100644 --- a/include/micm/util/random_string.hpp +++ b/include/micm/util/random_string.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/sparse_matrix.hpp b/include/micm/util/sparse_matrix.hpp index a23f7e109..40730fbf1 100644 --- a/include/micm/util/sparse_matrix.hpp +++ b/include/micm/util/sparse_matrix.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/sparse_matrix_standard_ordering.hpp b/include/micm/util/sparse_matrix_standard_ordering.hpp index f1457bafe..132101d2f 100644 --- a/include/micm/util/sparse_matrix_standard_ordering.hpp +++ b/include/micm/util/sparse_matrix_standard_ordering.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/sparse_matrix_vector_ordering.hpp b/include/micm/util/sparse_matrix_vector_ordering.hpp index 19f76e668..ea5fdafbd 100644 --- a/include/micm/util/sparse_matrix_vector_ordering.hpp +++ b/include/micm/util/sparse_matrix_vector_ordering.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index edae9ed14..1de36a410 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/src/process/process_set.cu b/src/process/process_set.cu index b515a6d02..6d9fed404 100644 --- a/src/process/process_set.cu +++ b/src/process/process_set.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/src/solver/linear_solver.cu b/src/solver/linear_solver.cu index 947572d27..fc2d77aef 100644 --- a/src/solver/linear_solver.cu +++ b/src/solver/linear_solver.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/src/solver/lu_decomposition.cu b/src/solver/lu_decomposition.cu index 6c1826e88..e5e659f4b 100644 --- a/src/solver/lu_decomposition.cu +++ b/src/solver/lu_decomposition.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #include diff --git a/src/solver/rosenbrock.cu b/src/solver/rosenbrock.cu index 8992221b6..399034f81 100644 --- a/src/solver/rosenbrock.cu +++ b/src/solver/rosenbrock.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research +// Copyright (C) 2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once #include diff --git a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp index cd2f20eda..1fc646e50 100644 --- a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp +++ b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 National Center for Atmospheric Research, +/* Copyright (C) 2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/test/unit/process/forcing_calculation.cpp b/test/unit/process/forcing_calculation.cpp index c402b1c0d..456203412 100644 --- a/test/unit/process/forcing_calculation.cpp +++ b/test/unit/process/forcing_calculation.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 // // A function hard-coded to calculate forcing for a toy chemistry system. diff --git a/test/unit/process/forcing_calculation.hpp b/test/unit/process/forcing_calculation.hpp index 116d2ba75..1b9fbc954 100644 --- a/test/unit/process/forcing_calculation.hpp +++ b/test/unit/process/forcing_calculation.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 // // A function hard-coded to calculate forcing for a toy chemistry system. diff --git a/test/unit/process/test_jit_forcing_calculation.cpp b/test/unit/process/test_jit_forcing_calculation.cpp index 4b7d93261..cae69fe76 100644 --- a/test/unit/process/test_jit_forcing_calculation.cpp +++ b/test/unit/process/test_jit_forcing_calculation.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 // // A comparison of the JITed forcing function with a hard-coded equivalent diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index 2088a47ea..8d7f29e5b 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2023 National Center for Atmospheric Research, +// Copyright (C) 2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 #include From b2d140f5740216c36a3b29a5400e6a75d086717f Mon Sep 17 00:00:00 2001 From: Jiwon Gim Date: Tue, 2 Jan 2024 16:25:14 -0700 Subject: [PATCH 314/318] update the range --- include/micm/configure/solver_config.hpp | 2 +- include/micm/jit/jit_compiler.hpp | 2 +- include/micm/jit/jit_function.hpp | 2 +- include/micm/process/arrhenius_rate_constant.hpp | 2 +- include/micm/process/branched_rate_constant.hpp | 2 +- include/micm/process/cuda_process_set.cuh | 2 +- include/micm/process/cuda_process_set.hpp | 2 +- include/micm/process/jit_process_set.hpp | 2 +- include/micm/process/process.hpp | 2 +- include/micm/process/process_set.hpp | 2 +- include/micm/process/rate_constant.hpp | 2 +- include/micm/process/surface_rate_constant.hpp | 2 +- .../micm/process/ternary_chemical_activation_rate_constant.hpp | 2 +- include/micm/process/troe_rate_constant.hpp | 2 +- include/micm/process/tunneling_rate_constant.hpp | 2 +- include/micm/process/user_defined_rate_constant.hpp | 2 +- include/micm/solver/cuda_linear_solver.cuh | 2 +- include/micm/solver/cuda_linear_solver.hpp | 2 +- include/micm/solver/cuda_lu_decomposition.cuh | 2 +- include/micm/solver/cuda_lu_decomposition.hpp | 2 +- include/micm/solver/cuda_rosenbrock.cuh | 2 +- include/micm/solver/jit_linear_solver.hpp | 2 +- include/micm/solver/jit_linear_solver.inl | 2 +- include/micm/solver/jit_lu_decomposition.hpp | 2 +- include/micm/solver/jit_lu_decomposition.inl | 2 +- include/micm/solver/jit_rosenbrock.hpp | 2 +- include/micm/solver/linear_solver.hpp | 2 +- include/micm/solver/linear_solver.inl | 2 +- include/micm/solver/lu_decomposition.hpp | 2 +- include/micm/solver/lu_decomposition.inl | 2 +- include/micm/solver/rosenbrock.hpp | 2 +- include/micm/solver/rosenbrock.inl | 2 +- include/micm/solver/rosenbrock_solver_parameters.hpp | 2 +- include/micm/solver/state.inl | 2 +- include/micm/system/conditions.hpp | 2 +- include/micm/system/phase.hpp | 2 +- include/micm/system/species.hpp | 2 +- include/micm/system/system.hpp | 2 +- include/micm/util/constants.hpp | 2 +- include/micm/util/cuda_param.hpp | 2 +- include/micm/util/jacobian.hpp | 2 +- include/micm/util/matrix.hpp | 2 +- include/micm/util/property_keys.hpp | 2 +- include/micm/util/random_string.hpp | 2 +- include/micm/util/sparse_matrix.hpp | 2 +- include/micm/util/sparse_matrix_standard_ordering.hpp | 2 +- include/micm/util/sparse_matrix_vector_ordering.hpp | 2 +- include/micm/util/vector_matrix.hpp | 2 +- src/process/process_set.cu | 2 +- src/solver/linear_solver.cu | 2 +- src/solver/lu_decomposition.cu | 2 +- src/solver/rosenbrock.cu | 2 +- test/regression/RosenbrockChapman/chapman_ode_solver.hpp | 2 +- test/unit/process/forcing_calculation.cpp | 2 +- test/unit/process/forcing_calculation.hpp | 2 +- test/unit/process/test_jit_forcing_calculation.cpp | 2 +- test/unit/process/test_process.cpp | 2 +- 57 files changed, 57 insertions(+), 57 deletions(-) diff --git a/include/micm/configure/solver_config.hpp b/include/micm/configure/solver_config.hpp index 28c439bbd..5a8fbbf18 100644 --- a/include/micm/configure/solver_config.hpp +++ b/include/micm/configure/solver_config.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/include/micm/jit/jit_compiler.hpp b/include/micm/jit/jit_compiler.hpp index d085bbee3..8e789a86a 100644 --- a/include/micm/jit/jit_compiler.hpp +++ b/include/micm/jit/jit_compiler.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 // diff --git a/include/micm/jit/jit_function.hpp b/include/micm/jit/jit_function.hpp index 880187510..e8a98fee7 100644 --- a/include/micm/jit/jit_function.hpp +++ b/include/micm/jit/jit_function.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/arrhenius_rate_constant.hpp b/include/micm/process/arrhenius_rate_constant.hpp index aae5937ae..9b47e54bb 100644 --- a/include/micm/process/arrhenius_rate_constant.hpp +++ b/include/micm/process/arrhenius_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/branched_rate_constant.hpp b/include/micm/process/branched_rate_constant.hpp index 457206942..52a7a7bb7 100644 --- a/include/micm/process/branched_rate_constant.hpp +++ b/include/micm/process/branched_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/cuda_process_set.cuh b/include/micm/process/cuda_process_set.cuh index 524a57d5d..9f99f85f1 100644 --- a/include/micm/process/cuda_process_set.cuh +++ b/include/micm/process/cuda_process_set.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/cuda_process_set.hpp b/include/micm/process/cuda_process_set.hpp index 9eb8303e6..feb6e3626 100644 --- a/include/micm/process/cuda_process_set.hpp +++ b/include/micm/process/cuda_process_set.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/jit_process_set.hpp b/include/micm/process/jit_process_set.hpp index 504ab4af2..7190b18ee 100644 --- a/include/micm/process/jit_process_set.hpp +++ b/include/micm/process/jit_process_set.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/process.hpp b/include/micm/process/process.hpp index 7976e486e..dff1f7bd5 100644 --- a/include/micm/process/process.hpp +++ b/include/micm/process/process.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/process/process_set.hpp b/include/micm/process/process_set.hpp index 612dba6b6..e4ef4296c 100644 --- a/include/micm/process/process_set.hpp +++ b/include/micm/process/process_set.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/rate_constant.hpp b/include/micm/process/rate_constant.hpp index 153f4a979..d23eedc20 100644 --- a/include/micm/process/rate_constant.hpp +++ b/include/micm/process/rate_constant.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/process/surface_rate_constant.hpp b/include/micm/process/surface_rate_constant.hpp index ed7fb271c..d4a58b103 100644 --- a/include/micm/process/surface_rate_constant.hpp +++ b/include/micm/process/surface_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/ternary_chemical_activation_rate_constant.hpp b/include/micm/process/ternary_chemical_activation_rate_constant.hpp index 918ae1956..f6923c7a6 100644 --- a/include/micm/process/ternary_chemical_activation_rate_constant.hpp +++ b/include/micm/process/ternary_chemical_activation_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/troe_rate_constant.hpp b/include/micm/process/troe_rate_constant.hpp index 9094483a2..e6a5fc91e 100644 --- a/include/micm/process/troe_rate_constant.hpp +++ b/include/micm/process/troe_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/tunneling_rate_constant.hpp b/include/micm/process/tunneling_rate_constant.hpp index 371230229..cd66c7e34 100644 --- a/include/micm/process/tunneling_rate_constant.hpp +++ b/include/micm/process/tunneling_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/process/user_defined_rate_constant.hpp b/include/micm/process/user_defined_rate_constant.hpp index 5aac41195..c7a882085 100644 --- a/include/micm/process/user_defined_rate_constant.hpp +++ b/include/micm/process/user_defined_rate_constant.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_linear_solver.cuh b/include/micm/solver/cuda_linear_solver.cuh index 6ba053c40..0fe90db06 100644 --- a/include/micm/solver/cuda_linear_solver.cuh +++ b/include/micm/solver/cuda_linear_solver.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/include/micm/solver/cuda_linear_solver.hpp b/include/micm/solver/cuda_linear_solver.hpp index b85357f7d..c73af0fb9 100644 --- a/include/micm/solver/cuda_linear_solver.hpp +++ b/include/micm/solver/cuda_linear_solver.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_lu_decomposition.cuh b/include/micm/solver/cuda_lu_decomposition.cuh index f4f5b94da..2d7c44f8c 100644 --- a/include/micm/solver/cuda_lu_decomposition.cuh +++ b/include/micm/solver/cuda_lu_decomposition.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_lu_decomposition.hpp b/include/micm/solver/cuda_lu_decomposition.hpp index 386b67c04..21b469dc4 100644 --- a/include/micm/solver/cuda_lu_decomposition.hpp +++ b/include/micm/solver/cuda_lu_decomposition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/cuda_rosenbrock.cuh b/include/micm/solver/cuda_rosenbrock.cuh index 906efe06c..9992fe815 100644 --- a/include/micm/solver/cuda_rosenbrock.cuh +++ b/include/micm/solver/cuda_rosenbrock.cuh @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once #include diff --git a/include/micm/solver/jit_linear_solver.hpp b/include/micm/solver/jit_linear_solver.hpp index 8a76f3f0b..3263feb3d 100644 --- a/include/micm/solver/jit_linear_solver.hpp +++ b/include/micm/solver/jit_linear_solver.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/jit_linear_solver.inl b/include/micm/solver/jit_linear_solver.inl index 5480a95c2..c6ad36fb6 100644 --- a/include/micm/solver/jit_linear_solver.inl +++ b/include/micm/solver/jit_linear_solver.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/jit_lu_decomposition.hpp b/include/micm/solver/jit_lu_decomposition.hpp index 8680c1e5e..c54277a59 100644 --- a/include/micm/solver/jit_lu_decomposition.hpp +++ b/include/micm/solver/jit_lu_decomposition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/jit_lu_decomposition.inl b/include/micm/solver/jit_lu_decomposition.inl index b07f0077d..9a63c4fcf 100644 --- a/include/micm/solver/jit_lu_decomposition.inl +++ b/include/micm/solver/jit_lu_decomposition.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/jit_rosenbrock.hpp b/include/micm/solver/jit_rosenbrock.hpp index 53dfa635f..42b614c34 100644 --- a/include/micm/solver/jit_rosenbrock.hpp +++ b/include/micm/solver/jit_rosenbrock.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 * diff --git a/include/micm/solver/linear_solver.hpp b/include/micm/solver/linear_solver.hpp index b3064feb5..8f4437579 100644 --- a/include/micm/solver/linear_solver.hpp +++ b/include/micm/solver/linear_solver.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/linear_solver.inl b/include/micm/solver/linear_solver.inl index 942d271dd..d51f74eba 100644 --- a/include/micm/solver/linear_solver.inl +++ b/include/micm/solver/linear_solver.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/lu_decomposition.hpp b/include/micm/solver/lu_decomposition.hpp index 10b35bb4b..8c204184b 100644 --- a/include/micm/solver/lu_decomposition.hpp +++ b/include/micm/solver/lu_decomposition.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/lu_decomposition.inl b/include/micm/solver/lu_decomposition.inl index 6517b37af..08ba9e816 100644 --- a/include/micm/solver/lu_decomposition.inl +++ b/include/micm/solver/lu_decomposition.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/solver/rosenbrock.hpp b/include/micm/solver/rosenbrock.hpp index 91e0c5b31..999d4d499 100644 --- a/include/micm/solver/rosenbrock.hpp +++ b/include/micm/solver/rosenbrock.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 * diff --git a/include/micm/solver/rosenbrock.inl b/include/micm/solver/rosenbrock.inl index ab2c01ab5..b89d10ace 100644 --- a/include/micm/solver/rosenbrock.inl +++ b/include/micm/solver/rosenbrock.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #define TIMED_METHOD(assigned_increment, time_it, method, ...) \ diff --git a/include/micm/solver/rosenbrock_solver_parameters.hpp b/include/micm/solver/rosenbrock_solver_parameters.hpp index 3912c6d40..9806fce97 100644 --- a/include/micm/solver/rosenbrock_solver_parameters.hpp +++ b/include/micm/solver/rosenbrock_solver_parameters.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/solver/state.inl b/include/micm/solver/state.inl index 1ce7a0359..6bb2b5143 100644 --- a/include/micm/solver/state.inl +++ b/include/micm/solver/state.inl @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 namespace micm diff --git a/include/micm/system/conditions.hpp b/include/micm/system/conditions.hpp index 1e3c19ce4..c8bf8b410 100644 --- a/include/micm/system/conditions.hpp +++ b/include/micm/system/conditions.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/system/phase.hpp b/include/micm/system/phase.hpp index bdf5fd6d3..87a465f00 100644 --- a/include/micm/system/phase.hpp +++ b/include/micm/system/phase.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/system/species.hpp b/include/micm/system/species.hpp index 0c0b5f653..869f3d13b 100644 --- a/include/micm/system/species.hpp +++ b/include/micm/system/species.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/system/system.hpp b/include/micm/system/system.hpp index a2287c160..ddf02b56b 100644 --- a/include/micm/system/system.hpp +++ b/include/micm/system/system.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/include/micm/util/constants.hpp b/include/micm/util/constants.hpp index 5fc8abd6c..84511b459 100644 --- a/include/micm/util/constants.hpp +++ b/include/micm/util/constants.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/cuda_param.hpp b/include/micm/util/cuda_param.hpp index 4962a9302..a440df3dd 100644 --- a/include/micm/util/cuda_param.hpp +++ b/include/micm/util/cuda_param.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/include/micm/util/jacobian.hpp b/include/micm/util/jacobian.hpp index b3af0e338..32822ba20 100644 --- a/include/micm/util/jacobian.hpp +++ b/include/micm/util/jacobian.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/matrix.hpp b/include/micm/util/matrix.hpp index 957f216dc..a1742f30a 100644 --- a/include/micm/util/matrix.hpp +++ b/include/micm/util/matrix.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/property_keys.hpp b/include/micm/util/property_keys.hpp index 8e93bb78a..b09caa6a5 100644 --- a/include/micm/util/property_keys.hpp +++ b/include/micm/util/property_keys.hpp @@ -1,5 +1,5 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/random_string.hpp b/include/micm/util/random_string.hpp index 01cdb38ae..2b31eb3bf 100644 --- a/include/micm/util/random_string.hpp +++ b/include/micm/util/random_string.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/sparse_matrix.hpp b/include/micm/util/sparse_matrix.hpp index 40730fbf1..b9ed7859d 100644 --- a/include/micm/util/sparse_matrix.hpp +++ b/include/micm/util/sparse_matrix.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/sparse_matrix_standard_ordering.hpp b/include/micm/util/sparse_matrix_standard_ordering.hpp index 132101d2f..1f19b14ab 100644 --- a/include/micm/util/sparse_matrix_standard_ordering.hpp +++ b/include/micm/util/sparse_matrix_standard_ordering.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/sparse_matrix_vector_ordering.hpp b/include/micm/util/sparse_matrix_vector_ordering.hpp index ea5fdafbd..bfbadd582 100644 --- a/include/micm/util/sparse_matrix_vector_ordering.hpp +++ b/include/micm/util/sparse_matrix_vector_ordering.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/include/micm/util/vector_matrix.hpp b/include/micm/util/vector_matrix.hpp index 1de36a410..92bb24508 100644 --- a/include/micm/util/vector_matrix.hpp +++ b/include/micm/util/vector_matrix.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #pragma once diff --git a/src/process/process_set.cu b/src/process/process_set.cu index 6d9fed404..cc15e8490 100644 --- a/src/process/process_set.cu +++ b/src/process/process_set.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/src/solver/linear_solver.cu b/src/solver/linear_solver.cu index fc2d77aef..f4bae1470 100644 --- a/src/solver/linear_solver.cu +++ b/src/solver/linear_solver.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 diff --git a/src/solver/lu_decomposition.cu b/src/solver/lu_decomposition.cu index e5e659f4b..18596844a 100644 --- a/src/solver/lu_decomposition.cu +++ b/src/solver/lu_decomposition.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // // SPDX-License-Identifier: Apache-2.0 #include diff --git a/src/solver/rosenbrock.cu b/src/solver/rosenbrock.cu index 399034f81..e6db8f067 100644 --- a/src/solver/rosenbrock.cu +++ b/src/solver/rosenbrock.cu @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research +// Copyright (C) 2023-2024 National Center for Atmospheric Research // SPDX-License-Identifier: Apache-2.0 #pragma once #include diff --git a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp index 1fc646e50..ffaea14b9 100644 --- a/test/regression/RosenbrockChapman/chapman_ode_solver.hpp +++ b/test/regression/RosenbrockChapman/chapman_ode_solver.hpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2024 National Center for Atmospheric Research, +/* Copyright (C) 2023-2024 National Center for Atmospheric Research, * * SPDX-License-Identifier: Apache-2.0 */ diff --git a/test/unit/process/forcing_calculation.cpp b/test/unit/process/forcing_calculation.cpp index 456203412..11a520b71 100644 --- a/test/unit/process/forcing_calculation.cpp +++ b/test/unit/process/forcing_calculation.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 // // A function hard-coded to calculate forcing for a toy chemistry system. diff --git a/test/unit/process/forcing_calculation.hpp b/test/unit/process/forcing_calculation.hpp index 1b9fbc954..f7b14a6aa 100644 --- a/test/unit/process/forcing_calculation.hpp +++ b/test/unit/process/forcing_calculation.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 // // A function hard-coded to calculate forcing for a toy chemistry system. diff --git a/test/unit/process/test_jit_forcing_calculation.cpp b/test/unit/process/test_jit_forcing_calculation.cpp index cae69fe76..40451fc58 100644 --- a/test/unit/process/test_jit_forcing_calculation.cpp +++ b/test/unit/process/test_jit_forcing_calculation.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 // // A comparison of the JITed forcing function with a hard-coded equivalent diff --git a/test/unit/process/test_process.cpp b/test/unit/process/test_process.cpp index 8d7f29e5b..d06fd1cd3 100644 --- a/test/unit/process/test_process.cpp +++ b/test/unit/process/test_process.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2024 National Center for Atmospheric Research, +// Copyright (C) 2023-2024 National Center for Atmospheric Research, // SPDX-License-Identifier: Apache-2.0 #include From b502d74d18497d886ed99b46c95417417d0e5614 Mon Sep 17 00:00:00 2001 From: Jiwon Gim Date: Fri, 5 Jan 2024 12:33:44 -0700 Subject: [PATCH 315/318] update the version --- README.md | 2 +- docs/source/_static/switcher.json | 9 +++++++-- include/micm/version.hpp | 2 +- test/integration/cmake/find_package/CMakeLists.txt | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index db14c1c9e..d16f16a17 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ int main(const int argc, const char *argv[]) To build and run the example using GNU (assuming the default install location): ``` -g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.2.0/include -std=c++20 +g++ -o foo_chem foo_chem.cpp -I/usr/local/micm-3.3.0/include -std=c++20 ./foo_chem ``` diff --git a/docs/source/_static/switcher.json b/docs/source/_static/switcher.json index 290f60623..dd717f9b4 100644 --- a/docs/source/_static/switcher.json +++ b/docs/source/_static/switcher.json @@ -1,7 +1,7 @@ [ { - "name": "v3.2.0 (stable)", - "version": "v3.2.0 (stable)", + "name": "v3.3.0 (stable)", + "version": "v3.3.0 (stable)", "url": "https://ncar.github.io/micm" }, { @@ -9,6 +9,11 @@ "version": "3.2.0", "url": "https://ncar.github.io/micm/versions/3.2.0" }, + { + "name": "v3.3.0", + "version": "3.3.0", + "url": "https://ncar.github.io/micm/versions/3.3.0" + }, { "name": "dev", "version": "dev", diff --git a/include/micm/version.hpp b/include/micm/version.hpp index 6a5992c97..2daf99e57 100644 --- a/include/micm/version.hpp +++ b/include/micm/version.hpp @@ -8,7 +8,7 @@ extern "C" { const char* getMicmVersion() { - return "3.2.0"; + return "3.3.0"; } unsigned getMicmVersionMajor() { diff --git a/test/integration/cmake/find_package/CMakeLists.txt b/test/integration/cmake/find_package/CMakeLists.txt index c27ec0d37..2ee12591f 100644 --- a/test/integration/cmake/find_package/CMakeLists.txt +++ b/test/integration/cmake/find_package/CMakeLists.txt @@ -8,7 +8,7 @@ project( set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -find_package(micm 3.2.0 REQUIRED) +find_package(micm 3.3.0 REQUIRED) ################################################################################ # Tests From 2763f30155ab4c29b22c5057b45f3d3fe1bc6605 Mon Sep 17 00:00:00 2001 From: Jiwon Gim Date: Fri, 5 Jan 2024 12:37:40 -0700 Subject: [PATCH 316/318] update the version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 71d4faffa..0adb6858b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.21) project( micm - VERSION 3.2.0 + VERSION 3.3.0 LANGUAGES CXX ) From d41e03167e7f6a012c7eb3f80f2d6c8a584cba65 Mon Sep 17 00:00:00 2001 From: Jiwon Gim Date: Fri, 5 Jan 2024 12:43:14 -0700 Subject: [PATCH 317/318] update the version --- include/micm/version.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/micm/version.hpp b/include/micm/version.hpp index 2daf99e57..7b1c81bcc 100644 --- a/include/micm/version.hpp +++ b/include/micm/version.hpp @@ -16,7 +16,7 @@ extern "C" { } unsigned getMicmVersionMinor() { - return 2+0; + return 3+0; } unsigned getMicmVersionPatch() { From c92db77e3a382afa01fabdd354282a63f17c8d42 Mon Sep 17 00:00:00 2001 From: Jiwon Gim Date: Fri, 5 Jan 2024 12:47:07 -0700 Subject: [PATCH 318/318] update the version --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 68a05905a..27a59984d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -23,7 +23,7 @@ suffix = os.getenv("SWITCHER_SUFFIX", "") # the suffix is required. This is controlled by the dockerfile that builds the docs -release = f'v3.2.0{suffix}' +release = f'v3.3.0{suffix}' # -- General configuration ---------------------------------------------------

kP1P*NG^KIh^7)1LjJlk?O3UzT#!LQ|*WYpZe| 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