Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion documentation/source/users/rmg/input.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ all of RMG's reaction families. ::
maximumRadicalElectrons=2,
maximumSingletCarbenes=1,
maximumCarbeneRadicals=0,
maximumIsotopicAtoms=2,
maximumFusedRings=3,
allowSingletO2 = False,
speciesCuttingThreshold=20,
)
Expand All @@ -1031,6 +1031,8 @@ from either the input file, seed mechanisms, or reaction libraries to bypass the
Note that this should be done with caution, since the constraints will still apply to subsequent
products that form.

``maximumFusedRings`` is the maximum number of rings in any fused ring system in the species.
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The constraint name 'maximumFusedRings' suggests it counts fused rings (rings sharing edges), but the implementation also counts spirocyclic rings (rings sharing a single atom) since get_polycycles() returns both types. Consider clarifying in the documentation whether spirocyclic rings are intentionally included in this constraint, or if the behavior should be restricted to only fused rings.

Suggested change
``maximumFusedRings`` is the maximum number of rings in any fused ring system in the species.
``maximumFusedRings`` is the maximum number of rings in any fused or spirocyclic ring system in the species (i.e., it counts both fused rings, which share edges, and spirocyclic rings, which share a single atom).

Copilot uses AI. Check for mistakes.

By default, the ``allowSingletO2`` flag is set to ``False``. See :ref:`representing_oxygen` for more information.

Note that ``speciesCuttingThreshold`` is set by default to 20 heavy atoms. This means that if a species containing
Expand Down
4 changes: 0 additions & 4 deletions documentation/source/users/rmg/modules/isotopes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ not match the same depository reactions, leading to inconsistent kinetics. If
degeneracy errors arise when generating reactions from scratch, try using this
option to see if it reduces errors in degeneracy.

The arguement ``--maximumIsotopicAtoms [integer]`` limits the number of enriched
atoms in any isotopologue in the model. This is beneficial for decreasing model
size, runtime of model creation and runtime necessary for analysis.

Adding kinetic isotope effects which are described in this paper can be obtained
through the argument ``--kineticIsotopeEffect simple``. Currently this is the
only supported method, though this can be extended to other effects.
Expand Down
2 changes: 0 additions & 2 deletions examples/rmg/MR_test/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,5 @@
#If this is false or missing, RMG will throw an error if the more less-stable form of O2 is entered
#which doesn't react in the RMG system. normally input O2 as triplet with SMILES [O][O]
#allowSingletO2=False,
# maximum allowed number of non-normal isotope atoms:
#maximumIsotopicAtoms=2,
)

2 changes: 0 additions & 2 deletions examples/rmg/commented/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,8 +287,6 @@
# If this is false or missing, RMG will throw an error if the more less-stable form of O2 is entered
# which doesn't react in the RMG system. normally input O2 as triplet with SMILES [O][O]
# allowSingletO2=False,
# maximum allowed number of non-normal isotope atoms:
# maximumIsotopicAtoms=2,
)

# optional block allows thermo to be estimated through quantum calculations
Expand Down
7 changes: 7 additions & 0 deletions rmgpy/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

from rmgpy.species import Species


def pass_cutting_threshold(species):
"""
Pass in either a `Species` or `Molecule` object and checks whether it passes
Expand Down Expand Up @@ -58,6 +59,7 @@ def pass_cutting_threshold(species):

return False


def fails_species_constraints(species):
"""
Pass in either a `Species` or `Molecule` object and checks whether it passes
Expand Down Expand Up @@ -139,4 +141,9 @@ def fails_species_constraints(species):
if struct.get_singlet_carbene_count() > 0 and struct.get_radical_count() > max_carbene_radicals:
return True

max_fused_rings = species_constraints.get('maximumFusedRings', -1)
if max_fused_rings != -1 and struct.is_cyclic():
if struct.get_ring_count_in_largest_fused_ring_system() > max_fused_rings:
return True

return False
29 changes: 29 additions & 0 deletions rmgpy/molecule/molecule.py
Original file line number Diff line number Diff line change
Expand Up @@ -3002,6 +3002,35 @@ def get_desorbed_molecules(self):

return desorbed_molecules

def get_ring_count_in_largest_fused_ring_system(self) -> int:
"""
Get the number of rings in the largest fused ring system in the molecule.
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring should clarify that monocycles (single rings) return 0 since they are not considered fused ring systems. Consider updating the docstring to: "Get the number of rings in the largest fused ring system in the molecule. Returns 0 if the molecule has no fused rings (only monocycles or no rings)."

Suggested change
Get the number of rings in the largest fused ring system in the molecule.
Get the number of rings in the largest fused ring system in the molecule.
Returns 0 if the molecule has no fused rings (only monocycles or no rings).

Copilot uses AI. Check for mistakes.
"""
cython.declare(polycycles=list, sssr=list, sssr_sets=list, ring_counts=list)
cython.declare(polycycle=list, ring=list)

polycycles = self.get_polycycles()
if not polycycles:
return 0

sssr = self.get_smallest_set_of_smallest_rings()
if not sssr:
return 0

sssr_sets = [set(r) for r in sssr]

ring_counts = list()
for polycycle in polycycles:
poly_set = set(polycycle)
ring_count = 0
for ring_set in sssr_sets:
if ring_set.issubset(poly_set):
ring_count += 1
ring_counts.append(ring_count)

return max(ring_counts) if ring_counts else 0


# this variable is used to name atom IDs so that there are as few conflicts by
# using the entire space of integer objects
atom_id_counter = -2 ** 15
1 change: 1 addition & 0 deletions rmgpy/rmg/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,7 @@ def generated_species_constraints(**kwargs):
'maximumRadicalElectrons',
'maximumSingletCarbenes',
'maximumCarbeneRadicals',
'maximumFusedRings',
'allowSingletO2',
'speciesCuttingThreshold',
]
Expand Down
1 change: 0 additions & 1 deletion scripts/rmg2to3.py
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,6 @@
# rmgpy.tools.isotopes
'useOriginalReactions': 'use_original_reactions',
'kineticIsotopeEffect': 'kinetic_isotope_effect',
'maximumIsotopicAtoms': 'maximum_isotopic_atoms',
# rmgpy.tools.loader
'generateImages': 'generate_images',
'useJava': 'use_java',
Expand Down
11 changes: 11 additions & 0 deletions test/rmgpy/molecule/moleculeTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -3043,3 +3043,14 @@ def test_remove_van_der_waals_bonds(self):
assert len(mol.get_all_edges()) == 2
mol.remove_van_der_waals_bonds()
assert len(mol.get_all_edges()) == 1

def test_get_ring_count_in_largest_fused_ring_system(self):
"""Test that we can count the rings in the largest fused ring system."""
mol = Molecule(smiles="CCCC")
assert mol.get_ring_count_in_largest_fused_ring_system() == 0
mol = Molecule(smiles="c1ccccc1")
assert mol.get_ring_count_in_largest_fused_ring_system() == 0
mol = Molecule(smiles="c12ccccc1cccc2")
assert mol.get_ring_count_in_largest_fused_ring_system() == 2
mol = Molecule(smiles="C[C]1C2C(=O)C3CC4C(=O)C=C2CC143")
assert mol.get_ring_count_in_largest_fused_ring_system() == 4
Loading