Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
aa62200
Save cantera files that were made from chemkin files to a separate fo…
rwest Jun 6, 2024
b60ceab
Rename yml to yaml_rms, because it was specific to RMS
Nora-Khalil Jun 15, 2022
96d55c8
Enable Cantera-YAML writing.
Nora-Khalil Jun 13, 2022
f3d0f7f
Reworking the Elements blocks in cantera yaml writer.
rwest Aug 2, 2024
1a306cf
add yaml writer test to compare yaml file generated by RMG and the ya…
Mar 3, 2025
34f8b62
Simplify species_to_dict for writing cantera yaml.
rwest Mar 21, 2024
7e54db5
Rename generate_cantera_files to generate_cantera_files_from_chemkin
rwest Feb 5, 2026
8af1cec
Add (or restore?) a TestCanteraOutputConversion test
rwest Feb 12, 2026
f2b4725
Functional test comparing Cantera YAML outputs to Chemkin-converted YAML
rwest Feb 6, 2026
d1c0487
YAML: Reaction comparison can do irreversible reactions
rwest Feb 6, 2026
2892941
Enhance reaction comparison by adding checks for reaction counts and …
rwest Feb 6, 2026
1a285e9
Simplify CompareYaml and YamlAnalyst arguments
rwest Feb 6, 2026
ccdc668
Move test_make_cantera_input_file into ..._from_ck and make a ..._dir…
rwest Feb 12, 2026
7d528f1
Tweak test_yaml
rwest Feb 6, 2026
c9f172f
Update test_yaml.py to use new YAML test files for comparison
rwest Feb 6, 2026
a8fd07d
A stub cantera_yamlTest.py file for testing the cantera yaml features.
rwest Feb 7, 2026
323f5d4
Work on the yaml_canteraTest.
rwest Feb 7, 2026
2ba5bc1
Work on yaml_canteraTest.py. Check phases.
rwest Feb 8, 2026
d67077c
TEMP? mainTest functional test now copies the yaml files into the tes…
rwest Feb 8, 2026
f1f4907
[yaml_cantera] Make phase names mach ck2yaml version.
rwest Feb 8, 2026
610b23a
[yaml_cantera] Fix where transport data comments are put
rwest Feb 8, 2026
9300613
[yaml_canteraTest] More testing of phase definitions.
rwest Feb 8, 2026
fc6bd3a
[yaml_canteraTest] Test the Elements block
rwest Feb 8, 2026
2da5bc1
[yaml_cantera] Outputs more condensed, with flow_style for some lists.
rwest Feb 8, 2026
b41871d
Update the cantera yaml test data files.
rwest Feb 8, 2026
79a66ea
[yaml_cantera] Add test for species definition matching.
rwest Feb 10, 2026
5c0a887
[yaml_canteraTest] fix species test for NASA polynomial collapse.
rwest Feb 10, 2026
bd523c1
Converted cantera SI units to the units specified in write_cantera
LekiaAnonim Feb 11, 2026
e213efc
yaml comparer: Added a normalization method for equation with third-b…
LekiaAnonim Feb 11, 2026
3cb20dd
yaml comparer: use cantera to compare rate
LekiaAnonim Feb 11, 2026
2ad7d8a
Fix YAML serialization of Cantera objects by converting AnyMap to dict
rwest Feb 12, 2026
15f1fac
TEMP? Newly created yaml files for testing.
rwest Feb 12, 2026
60283ec
Enhance compare_yaml_outputs
rwest Feb 13, 2026
982d82d
yaml_canteraTest compares yaml files made in mainTest
rwest Feb 13, 2026
93fa1e9
Making a new tool compare_cantera_yaml
rwest Feb 15, 2026
782aab7
Refactor Cantera YAML comparison tool with enhanced features
rwest Feb 16, 2026
972e55b
The compare_cantera_yaml script now uses logging.
rwest Feb 16, 2026
afd52c0
At end of model generation, compare direct yaml and ck2yaml outputs.
rwest Feb 16, 2026
6398484
Added a Cantera writer module
alongd Dec 13, 2025
aa89b30
Move Alon's cantera writer to yaml_cantera2
rwest Feb 16, 2026
9de152d
Rename Alon's CanteraWriter class to CanteraWriter2
rwest Feb 16, 2026
cfd2e3a
Tests: Cantera writer
alongd Dec 13, 2025
4d4f87b
Rename (file and contents) Alon's yaml_cantera2Test
rwest Feb 16, 2026
8d9146b
Add surface chemistry support to Cantera YAML export
alongd Jan 10, 2026
e17e2b9
Updates output subdirectory name to 'cantera2'
rwest Feb 16, 2026
5a6b709
[yaml_cantera] makes a copy of the latest chem.yaml each iteration
rwest Feb 16, 2026
47be27b
Attach the CanteraWriter2
rwest Feb 16, 2026
15ff103
compare_cantera_yaml copes with different reaction block specification
rwest Feb 16, 2026
643b87f
Correct units for transport properties in CanteraWriter2
rwest Feb 16, 2026
060b940
Fix transport properties in CanteraWriter2
rwest Feb 16, 2026
c1ad220
[CanteraWriter2] Write transport and thermo notes where they belong.
rwest Feb 16, 2026
1897bff
[CanteraWriter2] fix conversion of dipole monte into Debye
rwest Feb 16, 2026
7d872b2
Fix attempt for 'E' in atom dictionary
alongd Feb 19, 2026
3659097
Revert "Fix attempt for 'E' in atom dictionary"
rwest Feb 20, 2026
9ae67ff
[yaml_cantera2] Fixing 'E' electron count for charged species.
rwest Feb 20, 2026
ff961c9
Rename 'yaml_cantera' to 'yaml_cantera1'.
rwest Feb 20, 2026
a48e3ef
[RMG/main] get_git_commit now returns strings not bytes
rwest Feb 20, 2026
507bfdf
[yaml_cantera2] Detailed logging of the yaml generator
rwest Feb 20, 2026
1800a9d
[yaml_cantera1] Detailed logging of the yaml generator
rwest Feb 20, 2026
4859d3a
Revert "Converted cantera SI units to the units specified in write_ca…
rwest Feb 21, 2026
4c438fc
[yaml_cantera1] Report things in cantera default internal units.
rwest Feb 21, 2026
7e81509
[yaml_cantera1] Add coverage-dependent thermo to CanteraWriter1
rwest Apr 8, 2026
f29f32c
[yaml_cantera2] Add coverage-dependent thermo to CanteraWriter2
rwest Apr 8, 2026
2f6bdfb
Move coverage-dependent thermo into Species.to_cantera()
rwest Apr 8, 2026
e581f7e
[yaml_cantera2] use label if it's a Species (should save time)
rwest Apr 9, 2026
d3ee617
[yaml_cantera2] write species list in order of index
rwest Apr 9, 2026
0b407eb
[yaml_cantera2] fix unit test (detecting the generator field)
rwest Apr 9, 2026
d21336d
Fix PDepArrhenius.set_cantera_kinetics for duplicate-pressure PLOG
rwest May 1, 2026
df8110b
[yaml_cantera2] Unbox numpy P scalars in PDepArrhenius rate-constants
rwest May 1, 2026
3e8f0da
[yaml_cantera2] Emit duplicate-pressure PLOG as one reaction, not spl…
rwest May 1, 2026
525cd14
[yaml_cantera1] Add verboseComments support
rwest May 1, 2026
2fb75b8
[yaml_cantera2] Add verboseComments support
rwest May 1, 2026
e215a74
[yaml_cantera1] Add saveEdgeSpecies support
rwest May 1, 2026
8e9ed8b
[yaml_cantera2] cov_params is a dict with keys not a structure with a…
rwest May 1, 2026
4335be1
[yaml_cantera1] Add coverage dependent kinetics.
rwest May 1, 2026
dc385d7
[yaml_cantera1] Move the coverage-dependence code into to_cantera
rwest May 1, 2026
687a42d
Fixes to coverage-dependent kinetics. [requires rebuild of cython]
rwest May 1, 2026
be945ad
[yaml_cantera1] Add unit tests for species_to_dict and reaction_to_dicts
rwest May 1, 2026
c062fc5
[yaml_cantera2] Add unit tests for surface species and reactions
rwest May 1, 2026
5951eb0
Use CDumper (C-accelerated) in both Cantera YAML writers for faster o…
rwest May 2, 2026
2e6c1d1
Use CDumper (C-accelerated) in RMS YAML writer for faster output
rwest May 2, 2026
e2bd6f7
Remove outdated Cantera files from testing directory.
rwest May 3, 2026
a2b0583
Add Cantera files from main test, to testing folder.
rwest May 3, 2026
d5eb1ee
mainTest creates Cantera yaml files for testing.
rwest May 4, 2026
415836b
Replaced cantera1 yaml test file with the verbose annotated one
rwest May 4, 2026
fdade27
fixup! Add Cantera files from main test, to testing folder.
rwest May 4, 2026
b3728b1
Fix yaml_cantera1Test test (generator name)
rwest May 4, 2026
f08e9ce
Fix yaml_cantera2Test mock RMG object missing verbose_comments attrib…
rwest May 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions rmgpy/kinetics/arrhenius.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -946,17 +946,24 @@ cdef class PDepArrhenius(PDepKineticsModel):
def set_cantera_kinetics(self, ct_reaction, species_list):
"""
Sets a Cantera PlogReaction()'s `rates` attribute with
A list of tuples containing [(pressure in Pa, cantera arrhenius object), (..)]
a list of tuples containing [(pressure in Pa, cantera arrhenius object), ...].

A ``MultiArrhenius`` entry (from chemkin PLOG blocks with duplicate
pressures) is expanded into one tuple per inner Arrhenius; Cantera's
PlogRate sums duplicate-pressure entries at evaluation.
"""
import cantera as ct
import copy
assert isinstance(ct_reaction.rate, ct.PlogRate), "Must have a Cantera PlogRate attribute"

pressures = copy.deepcopy(self._pressures.value_si)
ctArrhenius = [arr.to_cantera_kinetics(arrhenius_class=True) for arr in self.arrhenius]
rate_pairs = []
for P, arr in zip(self._pressures.value_si, self.arrhenius):
if isinstance(arr, MultiArrhenius):
for sub in arr.arrhenius:
rate_pairs.append((P, sub.to_cantera_kinetics(arrhenius_class=True)))
else:
rate_pairs.append((P, arr.to_cantera_kinetics(arrhenius_class=True)))

new_rates = ct.PlogRate(list(zip(pressures, ctArrhenius)))
ct_reaction.rate = new_rates
ct_reaction.rate = ct.PlogRate(rate_pairs)

################################################################################

Expand Down
13 changes: 13 additions & 0 deletions rmgpy/reaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,19 @@ def to_cantera(self, species_list=None, use_chemkin_identifier=False):
# Now we set the kinetics.
self.kinetics.set_cantera_kinetics(ct_reaction, species_list)

# Coverage dependencies are not handled by set_cantera_kinetics; set them here.
# Cantera's coverage_dependencies E is in J/kmol; RMG's value_si is J/mol.
if hasattr(self.kinetics, 'coverage_dependence') and self.kinetics.coverage_dependence:
cov_deps = {}
for sp, params in self.kinetics.coverage_dependence.items():
sp_label = sp.to_chemkin() if use_chemkin_identifier else sp.label
cov_deps[sp_label] = {
'a': params['a'].value_si,
'm': params['m'].value_si,
'E': params['E'].value_si * 1000, # J/mol → J/kmol
}
ct_reaction.rate.coverage_dependencies = cov_deps

return ct_reaction

def get_url(self):
Expand Down
47 changes: 35 additions & 12 deletions rmgpy/rmg/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,11 @@
from rmgpy.stats import ExecutionStatsWriter
from rmgpy.thermo.thermoengine import submit
from rmgpy.tools.plot import plot_sensitivity
from rmgpy.tools.compare_cantera_yaml import compare_yaml_files, compare_yaml_files_and_report
from rmgpy.tools.uncertainty import Uncertainty, process_local_results
from rmgpy.yml import RMSWriter
from rmgpy.yaml_rms import RMSWriter
from rmgpy.yaml_cantera1 import CanteraWriter1
from rmgpy.yaml_cantera2 import CanteraWriter2

################################################################################

Expand Down Expand Up @@ -784,7 +787,8 @@ def register_listeners(self, requires_rms=False):
self.attach(ChemkinWriter(self.output_directory))

self.attach(RMSWriter(self.output_directory))

self.attach(CanteraWriter1(self.output_directory))
self.attach(CanteraWriter2(self.output_directory))
if self.generate_output_html:
self.attach(OutputHTMLWriter(self.output_directory))

Expand Down Expand Up @@ -1232,15 +1236,17 @@ def execute(self, initialize=True, **kwargs):

self.run_model_analysis()

# generate Cantera files chem.yaml & chem_annotated.yaml in a designated `cantera` output folder
# generate Cantera files chem.yaml & chem_annotated.yaml in designated Cantera output folders
try:
logging.info("Translating final chemkin file into Cantera yaml.")
translated_cantera_file = None
if any([s.contains_surface_site() for s in self.reaction_model.core.species]):
# Surface (catalytic) chemistry
self.generate_cantera_files(
translated_cantera_file = self.generate_cantera_files_from_chemkin(
os.path.join(self.output_directory, "chemkin", "chem-gas.inp"),
surface_file=(os.path.join(self.output_directory, "chemkin", "chem-surface.inp")),
)
self.generate_cantera_files(
self.generate_cantera_files_from_chemkin(
os.path.join(self.output_directory, "chemkin", "chem_annotated-gas.inp"),
surface_file=(os.path.join(self.output_directory, "chemkin", "chem_annotated-surface.inp")),
)
Expand Down Expand Up @@ -1273,8 +1279,22 @@ def execute(self, initialize=True, **kwargs):
_add_coverage_dependence_to_cantera_yaml(yaml_path, coverage_deps)

else: # gas phase only
self.generate_cantera_files(os.path.join(self.output_directory, "chemkin", "chem.inp"))
self.generate_cantera_files(os.path.join(self.output_directory, "chemkin", "chem_annotated.inp"))
translated_cantera_file = self.generate_cantera_files_from_chemkin(
os.path.join(self.output_directory, "chemkin", "chem.inp")
)
self.generate_cantera_files_from_chemkin(
os.path.join(self.output_directory, "chemkin", "chem_annotated.inp")
)

# Compare translated Cantera files and directly generated Cantera files

compare_yaml_files_and_report(translated_cantera_file,
os.path.join(self.output_directory, "cantera1", "chem.yaml"),
output=os.path.join(self.output_directory, "cantera1", "comparison_report.txt"))
compare_yaml_files_and_report(translated_cantera_file,
os.path.join(self.output_directory, "cantera2", "chem.yaml"),
output=os.path.join(self.output_directory, "cantera2", "comparison_report.txt"))

except EnvironmentError:
logging.exception("Could not generate Cantera files due to EnvironmentError. Check read\\write privileges in output directory.")
except Exception:
Expand Down Expand Up @@ -1845,14 +1865,15 @@ def process_reactions_to_species(self, obj):
raise TypeError("improper call, obj input was incorrect")
return potential_spcs

def generate_cantera_files(self, chemkin_file, **kwargs):
def generate_cantera_files_from_chemkin(self, chemkin_file, **kwargs):
"""
Convert a chemkin mechanism chem.inp file to a cantera mechanism file chem.yaml
and save it in the cantera directory
and save it in the cantera directory.
Returns the path to the generated cantera file.
"""
transport_file = os.path.join(os.path.dirname(chemkin_file), "tran.dat")
file_name = os.path.splitext(os.path.basename(chemkin_file))[0] + ".yaml"
out_name = os.path.join(self.output_directory, "cantera", file_name)
out_name = os.path.join(self.output_directory, "cantera_from_ck", file_name)
if "surface_file" in kwargs:
out_name = out_name.replace("-gas.", ".")
cantera_dir = os.path.dirname(out_name)
Expand All @@ -1870,6 +1891,7 @@ def generate_cantera_files(self, chemkin_file, **kwargs):
logging.exception("Error converting to Cantera format.")
logging.info("Trying again without transport data file.")
parser.convert_mech(chemkin_file, out_name=out_name, quiet=True, permissive=True, **kwargs)
return out_name

def initialize_reaction_threshold_and_react_flags(self):
num_core_species = len(self.reaction_model.core.species)
Expand Down Expand Up @@ -2157,8 +2179,9 @@ def get_git_commit(self, module_path):

if os.path.exists(os.path.join(module_path, "..", ".git")):
try:
return subprocess.check_output(["git", "log", "--format=%H%n%cd", "-1"], cwd=module_path).splitlines()
except:
head, date = subprocess.check_output(["git", "log", "--format=%H%n%cd", "-1"], cwd=module_path).splitlines()
return head.decode(), date.decode()
except (subprocess.CalledProcessError, OSError):
return "", ""
else:
return "", ""
Expand Down
12 changes: 7 additions & 5 deletions rmgpy/solver/surface.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import logging
cimport cython
import numpy as np
cimport numpy as np
from libc.math cimport exp
from libc.math cimport exp, pow

import rmgpy.constants as constants
cimport rmgpy.constants as constants
Expand Down Expand Up @@ -478,9 +478,11 @@ cdef class SurfaceReactor(ReactionSystem):
#: surface species are in mol/m2, gas phase are in mol/m3
core_species_concentrations[j] = C[j]

if self.thermo_coverage_dependence or self.coverage_dependence:
coverages = np.where(species_on_surface, np.maximum(N / total_sites, 0.0), 0.0)

# Thermodynamic coverage dependence
if self.thermo_coverage_dependence:
coverages = np.where(species_on_surface, N / total_sites, 0.0)
coverages_squared = coverages * coverages
temperature_scaled_coverages = -self.T.value_si * coverages
thermo_dep_coverage = np.empty((6, coverages.shape[0]), dtype=np.float64)
Expand Down Expand Up @@ -508,12 +510,12 @@ cdef class SurfaceReactor(ReactionSystem):
"""
for i, list_of_coverage_deps in self.coverage_dependencies.items():
# Species i, Reaction j
surface_site_fraction = N[i] / total_sites
if surface_site_fraction < 1e-15:
surface_site_fraction = coverages[i]
if surface_site_fraction <= 1e-6:
continue
for j, a, m, E in list_of_coverage_deps:
coverage_corrections[j] *= 10. ** (a * surface_site_fraction) *\
surface_site_fraction ** m *\
pow(surface_site_fraction, m) *\
exp(-1 * E * surface_site_fraction / (constants.R * self.T.value_si))
kf = kf * coverage_corrections # make a corrected copy kf, but leave the original array at self.kf unchanged
kr = kr * coverage_corrections
Expand Down
33 changes: 32 additions & 1 deletion rmgpy/species.py
Original file line number Diff line number Diff line change
Expand Up @@ -428,13 +428,18 @@ def to_chemkin(self):
from rmgpy.chemkin import get_species_identifier
return get_species_identifier(self)

def to_cantera(self, use_chemkin_identifier=False):
def to_cantera(self, use_chemkin_identifier=False, all_species=None):
"""
Converts the RMG Species object to a Cantera Species object
with the appropriate thermo data.

If use_chemkin_identifier is set to False, the species label is used
instead. Be sure that species' labels are unique when setting it False.

If all_species is provided and this is a surface species with
coverage-dependent thermo, the coverage-dependencies are attached to
the Cantera Species object via update_user_data() so that they appear
in input_data and in Solution.write_yaml() output.
"""
import cantera as ct

Expand Down Expand Up @@ -475,6 +480,32 @@ def to_cantera(self, use_chemkin_identifier=False):
if self.transport_data:
ct_species.transport = self.transport_data.to_cantera()

# Attach coverage-dependent thermo if present.
# thermo_coverage_dependence keys are adjacency-list strings; we resolve
# them to species names here using all_species. The data is stored via
# update_user_data() so it appears in ct_species.input_data and in any
# YAML serialised by Cantera (e.g. Solution.write_yaml()).
if (all_species is not None
and self.contains_surface_site()
and self.thermo
and hasattr(self.thermo, 'thermo_coverage_dependence')
and self.thermo.thermo_coverage_dependence):
from rmgpy.molecule.molecule import Molecule
cov_deps = {}
for adj_list, parameters in self.thermo.thermo_coverage_dependence.items():
mol = Molecule().from_adjacency_list(adj_list)
for sp in all_species:
if sp.is_isomorphic(mol, strict=False):
dep_name = sp.to_chemkin() if use_chemkin_identifier else sp.label
cov_deps[dep_name] = {
'units': {'energy': 'J', 'quantity': 'mol'},
'enthalpy-coefficients': [v.value_si for v in parameters['enthalpy-coefficients']],
'entropy-coefficients': [v.value_si for v in parameters['entropy-coefficients']],
}
break
if cov_deps:
ct_species.update_user_data({'coverage-dependencies': cov_deps})

return ct_species

def has_statmech(self):
Expand Down
1 change: 0 additions & 1 deletion rmgpy/thermo/nasa.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,6 @@ cdef class NASA(HeatCapacityModel):
poly.change_base_entropy(deltaS)
return self

# need to modify this to include the thermo coverage dependence
def to_cantera(self):
"""
Return the cantera equivalent NasaPoly2 object from this NASA object.
Expand Down
Loading
Loading