Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
38aeb38
refactoring v1
K20shores Jun 11, 2026
1775dab
v0 refactor
K20shores Jun 11, 2026
cbf0b9d
development
K20shores Jun 11, 2026
5976a6d
Accept `name` or `species name` for reaction components (v1 alias)
K20shores Jun 12, 2026
0b29ca3
Rename development engine to v1; delete the old combined v1 parser
K20shores Jun 12, 2026
494b220
Wire top-level parse() dispatch (file + string)
K20shores Jun 12, 2026
86f5ad7
Stage 4: unify v1 tests on the engine; add file-list config support
K20shores Jun 12, 2026
db6a108
Accept bare-string phase species in the v1 engine (restore v1 behavior)
K20shores Jun 12, 2026
fcfd98b
Restore v1 leniency in the engine: optional/scalar components, FOL pr…
K20shores Jun 12, 2026
ae36040
Drop the 6 CAMP-only reactions from v1; delete development tests
K20shores Jun 12, 2026
7604b19
Update v1 test expectations to the engine's error reporting; fix file…
K20shores Jun 12, 2026
5647aaa
Exempt reaction products from the phase-membership check
K20shores Jun 12, 2026
db19633
Add version-agnostic validate(const Mechanism&)
K20shores Jun 12, 2026
b559ffc
Refactor validate() onto a single semantics checker over a located in…
K20shores Jun 12, 2026
a11b3b6
Add v1 BuildSemanticInput (YAML -> located semantics::Input)
K20shores Jun 12, 2026
67a2500
Route parse() semantic validation through ValidateSemantics; strip v1…
K20shores Jun 12, 2026
8945487
Modernize integration tests to the parse()/validate() API
K20shores Jun 12, 2026
5148472
Fix examples/v1/full_configuration to be semantically valid
K20shores Jun 12, 2026
513c5fe
Remove dead semantic helpers from v1/utils
K20shores Jun 12, 2026
62e19a6
Tidy v1 type_validators: structural-only docs, drop dead ValidatePart…
K20shores Jun 12, 2026
af0a23d
Rename validation.hpp -> validation_keys.hpp
K20shores Jun 12, 2026
603c1c0
docs: add README usage example (parse a file or validate an in-code m…
K20shores Jun 12, 2026
468b3ea
private headers
K20shores Jun 12, 2026
c65624d
Flatten v1 detail headers to nested namespace; fix ::v typo
K20shores Jun 13, 2026
3bdd87c
docs: document lambda_rate_constant, add it to examples; drop plannin…
K20shores Jun 13, 2026
3b863e3
Release 2.0.0: bump version and add docs changelog
K20shores Jun 13, 2026
bbf93d5
ci: fix Ubuntu workflow matrix exclude indentation
K20shores Jun 13, 2026
974af2a
Fix Linux clang and MSVC CI builds
K20shores Jun 13, 2026
f3c524d
Rename structural checks Validate -> CheckSchema; reserve validate() …
K20shores Jun 15, 2026
9b136ca
Tidy v1 schema checkers: drop dead is_valid, rename validation_errors
K20shores Jun 15, 2026
81f18a1
Rename type_validators -> type_schema, reactions/validators/ -> react…
K20shores Jun 15, 2026
dcd9a06
Flatten reactions/{parsers,schema}/ into one file per reaction
K20shores Jun 15, 2026
5977643
v1::Parser::Parse owns validation; return std::expected, hide CheckSc…
K20shores Jun 15, 2026
32479dd
v1::Parser::Parse: add path/string/node overloads; mirror v0 in parse()
K20shores Jun 15, 2026
bde7475
docs: use a static Apache-2.0 license badge; fix LICENSE link to main
K20shores Jun 15, 2026
9e73057
Drop read_from_config_file flag from v1::Parser
K20shores Jun 15, 2026
5ba6325
test: fix stale version_mismatch comment
K20shores Jun 15, 2026
a53878c
Hoist semantic validation out of CheckSchema into ValidateAndBuild
K20shores Jun 15, 2026
550fb80
Rename validation key-vocabulary to keys
K20shores Jun 15, 2026
e8c830f
Move the v1 key vocabulary under v1 (keys -> v1::keys)
K20shores Jun 15, 2026
431d18e
Prefix the unsupported-version error with the config file path
K20shores Jun 15, 2026
398a8f7
Include line:col in the unsupported-version error
K20shores Jun 15, 2026
80e9759
adding readme example parsing
K20shores Jun 15, 2026
9e168d7
update to camelcase
boulderdaze Jun 17, 2026
18598b0
remove old aqueous and henrys law configs
boulderdaze Jun 17, 2026
e1ffa8a
update the copyright header
boulderdaze Jun 17, 2026
3ec403c
rearrage the types
boulderdaze Jun 17, 2026
1b7f7d5
replace phase location with location
boulderdaze Jun 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 17 additions & 1 deletion .github/workflows/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,36 @@ jobs:
strategy:
matrix:
compiler:
- { cpp: g++-12, c: gcc-12 }
- { cpp: g++-13, c: gcc-13 }
- { cpp: g++-14, c: gcc-14 }
- { cpp: clang++, c: clang }
build_type: [Release]
use_fmt: [OFF, ON]
exclude:
- compiler: { cpp: g++-12, c: gcc-12 }
use_fmt: OFF
env:
CC: ${{ matrix.compiler.c }}
CXX: ${{ matrix.compiler.cpp }}
steps:
- name: Checkout code
uses: actions/checkout@v4

# Clang on Ubuntu defaults to libstdc++, whose std::expected is not visible to
# clang-18 (no template named 'expected'). Build the clang job against libc++,
# which provides std::expected and std::format.
- name: Install libc++ (clang)
if: matrix.compiler.cpp == 'clang++'
run: sudo apt-get update && sudo apt-get install -y libc++-dev libc++abi-dev

- name: Configure CMake
run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MECH_CONFIG_USE_FMT=${{ matrix.use_fmt }} -D MECH_CONFIG_COMPILE_WARNING_AS_ERROR=ON
run: |
EXTRA_FLAGS=""
if [ "${{ matrix.compiler.cpp }}" = "clang++" ]; then
EXTRA_FLAGS="-D CMAKE_CXX_FLAGS=-stdlib=libc++"
fi
cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MECH_CONFIG_USE_FMT=${{ matrix.use_fmt }} -D MECH_CONFIG_COMPILE_WARNING_AS_ERROR=ON $EXTRA_FLAGS

- name: Build
run: cmake --build build --config ${{ matrix.build_type }} --parallel 10
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# must be on the same line so that pyproject.toml can correctly identify the version
project(mechanism_configuration VERSION 1.1.2 LANGUAGES CXX)
project(mechanism_configuration VERSION 2.0.0 LANGUAGES CXX)

if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING
Expand Down
69 changes: 67 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
An attempt at defining a model-independent configuration schema for atmospheric chemical systems in JSON and YAML.

[![GitHub Releases](https://img.shields.io/github/release/ncar/MechanismConfiguration.svg)](https://github.com/ncar/MechanismConfiguration/releases)
[![License](https://img.shields.io/github/license/ncar/MechanismConfiguration.svg)](https://github.com/ncar/MechanismConfiguration/blob/master/LICENSE)
[![Docs build](https://github.com/ncar/MechanismConfiguration/actions/workflows/gh-pages.yml/badge.svg)](https://github.com/ncar/MechanismConfiguration/actions/workflows/gh-pages.yml)
[![License](https://img.shields.io/badge/license-Apache_2.0-blue.svg)](https://github.com/NCAR/MechanismConfiguration/blob/main/LICENSE)
[![Windows](https://github.com/ncar/MechanismConfiguration/actions/workflows/windows.yml/badge.svg)](https://github.com/ncar/MechanismConfiguration/actions/workflows/windows.yml)
[![Mac](https://github.com/ncar/MechanismConfiguration/actions/workflows/mac.yml/badge.svg)](https://github.com/ncar/MechanismConfiguration/actions/workflows/mac.yml)
[![Ubuntu](https://github.com/ncar/MechanismConfiguration/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/ncar/MechanismConfiguration/actions/workflows/ubuntu.yml)
Expand All @@ -15,6 +14,72 @@ Copyright (C) 2017–2026 University Corporation for Atmospheric Research, Unive

The configuration documentation can be found [here](https://ncar.github.io/MechanismConfiguration/).

## Usage

Everything goes through the canonical `mechanism_configuration::Mechanism`. You can either
**parse** a configuration file (the version is detected and dispatched automatically), or build
a `Mechanism` in code and **validate** it. `Validate()` runs the same semantic checks the parser
uses, so it applies to any mechanism regardless of where it came from.

```cpp
#include <mechanism_configuration/mechanism_configuration.hpp>

#include <iostream>

using namespace mechanism_configuration;

void print_errors(const Errors& errors)
{
for (const auto& [code, message] : errors)
std::cerr << " [" << ErrorCodeToString(code) << "] " << message << '\n';
}

int main()
{
int status = 0;

// 1) Parse from a file (YAML or JSON; v0 or v1). Returns std::expected<Mechanism, Errors>
// with both structural and semantic errors reported.
if (auto parsed = Parse("examples/v1/full_configuration.yaml"))
{
const Mechanism& mechanism = *parsed;
std::cout << "Parsed '" << mechanism.name << "': " << mechanism.species.size()
<< " species, " << mechanism.reactions.arrhenius.size() << " Arrhenius reactions\n";
}
else
{
std::cerr << "Failed to parse file:\n";
print_errors(parsed.error());
status = 1;
}

// 2) Build a Mechanism in code and validate it (species exist, reactants are in their
// phase, no duplicate names, ...).
Mechanism mechanism;
mechanism.name = "example";
mechanism.species = { { .name = "A" }, { .name = "B" } };
mechanism.phases = { { .name = "gas", .species = { { .name = "A" }, { .name = "B" } } } };

types::Arrhenius reaction;
reaction.name = "A -> B";
reaction.gas_phase = "gas";
reaction.reactants = { { .name = "A" } }; // reactants must be registered in the phase
reaction.products = { { .name = "B" } }; // products may reference any phase
mechanism.reactions.arrhenius = { reaction };

if (Errors errors = Validate(mechanism); errors.empty())
std::cout << "In-code mechanism is valid\n";
else
{
std::cerr << "In-code mechanism is invalid:\n";
print_errors(errors);
status = 1;
}

return status;
}
```

## Building the Documentation

With python and pip installed, go to the `docs/` folder and run:
Expand Down
4 changes: 4 additions & 0 deletions cmake/test_util.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ function(create_standard_test)
add_executable(test_${TEST_NAME} ${TEST_SOURCES})

target_link_libraries(test_${TEST_NAME} PUBLIC musica::mechanism_configuration GTest::gtest_main)
target_include_directories(test_${TEST_NAME}
PUBLIC
${PROJECT_SOURCE_DIR}/src
)


# link additional libraries
Expand Down
102 changes: 102 additions & 0 deletions docs/source/changelog.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
Changelog
=========

Version 2.0.0
-------------

Version 2.0.0 introduces breaking changes to the C++ API compared to the 1.x series.
The configuration file formats (``v0`` and ``v1``) are unchanged; the breaking changes
are in how the library is consumed from C++.

API Changes
^^^^^^^^^^^

- Parsing is now a free function that returns ``std::expected``:
``std::expected<Mechanism, Errors> Parse(const std::filesystem::path&)``.
The previous ``UniversalParser`` / ``ParserResult<GlobalMechanism>`` interface has been removed.
- A version-neutral ``Errors Validate(const Mechanism&)`` has been added so an in-code
``Mechanism`` can be checked with the same semantic rules the parser uses.
- A single canonical ``mechanism_configuration::Mechanism`` (with ``mechanism_configuration::types::*``)
replaces the version-specific ``v0::types::Mechanism`` and ``v1::types::Mechanism``.
- Errors are reported through ``Errors`` (a list of ``{ErrorCode, std::string}``) and
``ErrorCodeToString``; the previous parse-status helpers have been removed.

Header Reorganization
^^^^^^^^^^^^^^^^^^^^^^

- A single public umbrella header is now provided. Including
``<mechanism_configuration/mechanism_configuration.hpp>`` is sufficient to use the library.
- The public surface is limited to ``parse.hpp``, ``validate.hpp``, ``mechanism.hpp``,
``types.hpp``, ``errors.hpp``, and ``version.hpp`` (plus the umbrella).
- Internal headers (the ``v0`` / ``v1`` parsers, schema and validation helpers) have moved
into a private ``detail`` tree and are no longer installed. Code that included headers such
as ``parser.hpp``, ``parser_result.hpp``, ``parse_status.hpp``, ``error_location.hpp``, or the
versioned ``v1/types.hpp`` / ``v1/mechanism.hpp`` must migrate to the public headers above.

Removed Reactions
^^^^^^^^^^^^^^^^^

The following reaction types were removed from the ``v1`` mechanism:

- ``AQUEOUS_EQUILIBRIUM``
- ``CONDENSED_PHASE_ARRHENIUS``
- ``CONDENSED_PHASE_PHOTOLYSIS``
- ``HENRYS_LAW``
- ``SIMPOL_PHASE_TRANSFER``
- ``WET_DEPOSITION``

Other Changes
^^^^^^^^^^^^^

- Reaction components accept the legacy ``species name`` key as an alias for ``name``.
- A ``v1`` configuration may split ``species``, ``phases``, and ``reactions`` across multiple
files using the ``files`` list form (minor version 1.1 or later).
- Products may reference a species in any phase; only reactants must belong to the
reaction's phase.

Migration
^^^^^^^^^

Old approach (1.x):

.. code-block:: cpp

#include <mechanism_configuration/parser.hpp>

using namespace mechanism_configuration;

UniversalParser parser;
ParserResult<GlobalMechanism> result = parser.Parse("config.yaml");
if (result)
{
GlobalMechanism& mechanism = *result;
// ...
}
else
{
for (const auto& error : result.errors)
{
// ...
}
}

New approach (2.0):

.. code-block:: cpp

#include <mechanism_configuration/mechanism_configuration.hpp>

using namespace mechanism_configuration;

if (auto parsed = Parse("config.yaml"))
{
const Mechanism& mechanism = *parsed;
// ...
}
else
{
for (const auto& [code, message] : parsed.error())
{
// ...
}
}
3 changes: 2 additions & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
.. ~ for subsubsubsections
.. " for paragraphs

.. |project_version| replace:: 1.1.2
.. |project_version| replace:: 2.0.0

###############################################################
Welcome to the NSF NCAR Mechanism Configuration documentation!
Expand All @@ -24,6 +24,7 @@ Welcome to the NSF NCAR Mechanism Configuration documentation!

v0/index
v1/index
changelog
bibliography


Expand Down
2 changes: 2 additions & 0 deletions docs/source/v1/reactions/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Their configuration is defined in this repository, as are the algorithms that mi
* :doc:`branched` - :cpp:class:`micm::BranchedRateConstant`
* :doc:`emission` - :cpp:class:`micm::UserDefinedRateConstant`
* :doc:`first_order_loss` - :cpp:class:`micm::UserDefinedRateConstant`
* :doc:`lambda_rate_constant` - :cpp:class:`micm::UserDefinedRateConstant`
* :doc:`photolysis` - :cpp:class:`micm::UserDefinedRateConstant`
* :doc:`surface` - :cpp:class:`micm::SurfaceRateConstant`
* :doc:`taylor_series` - :cpp:class:`micm::TaylorSeriesRateConstant`
Expand All @@ -29,6 +30,7 @@ Their configuration is defined in this repository, as are the algorithms that mi
branched
emission
first_order_loss
lambda_rate_constant
photolysis
surface
taylor_series
Expand Down
76 changes: 76 additions & 0 deletions docs/source/v1/reactions/lambda_rate_constant.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
Lambda Rate Constant
====================

Lambda rate constant reactions compute the rate constant from a user-supplied lambda
function of temperature (and optionally pressure):

.. math::

\ce{X_1 ( + X_2 \dots ) -> Y_1 ( + Y_2 \dots )}

where :math:`\ce{X_n}` are the reactants, and :math:`\ce{Y_n}` are the products of the reaction.

The rate constant is given by the value returned from the supplied ``lambda function`` when
evaluated at the current temperature :math:`T` (in K) and, optionally, pressure :math:`P` (in Pa).

Input data for lambda rate constant reactions have the following format:

.. tab-set::

.. tab-item:: YAML

.. code-block:: yaml

type: LAMBDA_RATE_CONSTANT
name: foo-lambda
gas phase: gas
lambda function: "[](double T, double P) { return 1.2e-5 * exp(-500.0 / T); }"
reactants:
- species name: foo
coefficient: 1.0
products:
- species name: bar
coefficient: 0.5
- species name: baz
coefficient: 0.3


.. tab-item:: JSON

.. code-block:: json

{
"type": "LAMBDA_RATE_CONSTANT",
"name": "foo-lambda",
"gas phase": "gas",
"lambda function": "[](double T, double P) { return 1.2e-5 * exp(-500.0 / T); }",
"reactants": [
{
"species name": "foo",
"coefficient": 1.0
}
],
"products": [
{
"species name": "bar",
"coefficient": 0.5
},
{
"species name": "baz",
"coefficient": 0.3
}
]
}

The key-value pairs ``reactants``, ``products``, and ``lambda function`` are required.
Any number of reactants and products may be present. Reactants and products without a specified ``coefficient`` are
assumed to have a ``coefficient`` of 1.0.

The ``lambda function`` is a string holding a function that takes temperature :math:`T` and,
optionally, pressure :math:`P` and returns the rate constant. It is expected to be compiled
by a conforming implementation.

The ``gas phase`` key is required and must be set to the name of the phase the
reaction takes place in. Each reactant must be present in the specified phase.

Rate constants are in units of :math:`\mathrm{(m^{3}\ mol^{-1})^{(n-1)}\ s^{-1}}` where :math:`n` is the total number of reactants.
Loading
Loading