From 420f93aca360353769892c2ca75230a1f35a3e67 Mon Sep 17 00:00:00 2001 From: Richard West Date: Thu, 19 Dec 2024 10:01:25 -0500 Subject: [PATCH 1/6] Report which family kinetics came from in comment. For auto-generated trees, the reaction family was not reported in the kinetics comment. Now it is. (Also replaced some format() calls with f-strings) --- rmgpy/data/kinetics/rules.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/rmgpy/data/kinetics/rules.py b/rmgpy/data/kinetics/rules.py index d20a4a6ce4..7a71070937 100644 --- a/rmgpy/data/kinetics/rules.py +++ b/rmgpy/data/kinetics/rules.py @@ -367,20 +367,18 @@ def estimate_kinetics(self, template, degeneracy=1): kinetics = deepcopy(entry.data) if entry0 == entry: - kinetics.comment = "Estimated from node {}".format(entry.label) + kinetics.comment = f"Estimated from node {entry.label} in family {self.label.replace('/rules','')}." kinetics.A.value_si *= degeneracy if degeneracy > 1: - kinetics.comment += "\n" - kinetics.comment += "Multiplied by reaction path degeneracy {0}".format(degeneracy) - return kinetics,entry + kinetics.comment += f"\nMultiplied by reaction path degeneracy {degeneracy}" + return kinetics, entry else: - kinetics.comment = "Matched node {}\n".format(entry0.label) - kinetics.comment += "Estimated from node {}".format(entry.label) + kinetics.comment = f"Matched node {entry0.label}\n" + kinetics.comment += f"Estimated from node {entry.label} in family {self.label.replace('/rules','')}." kinetics.A.value_si *= degeneracy if degeneracy > 1: - kinetics.comment += "\n" - kinetics.comment += "Multiplied by reaction path degeneracy {0}".format(degeneracy) - return kinetics,None + kinetics.comment += f"\nMultiplied by reaction path degeneracy {degeneracy}" + return kinetics, None original_leaves = get_template_label(template) template_list = [template] From b08be3fb51b75761f8a08d72cc14715ec7233828 Mon Sep 17 00:00:00 2001 From: Richard West Date: Thu, 19 Dec 2024 10:03:10 -0500 Subject: [PATCH 2/6] If a pdep network causes problems, improve logging for debugging. If a pdep network is going to cause the job to crash with an InvalidMicrocanonicalRateError, then report a summary of the troublesome network and draw a PDF of it before crashing, to make it easier to figure out what's wrong. --- rmgpy/pdep/network.py | 1 + rmgpy/rmg/pdep.py | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/rmgpy/pdep/network.py b/rmgpy/pdep/network.py index d3dafa5ae5..7aa2d14301 100644 --- a/rmgpy/pdep/network.py +++ b/rmgpy/pdep/network.py @@ -408,6 +408,7 @@ def set_conditions(self, T, P, ymB=None): logging.error("Increasing number of grains did not decrease error enough " "(Current badness: {0:.1f}, previous {1:.1f}). Something must be wrong with " "network {2}".format(badness, previous_error.badness(), self.label)) + self.log_summary() raise error previous_error = error success = False diff --git a/rmgpy/rmg/pdep.py b/rmgpy/rmg/pdep.py index 834f0f7243..f5433cd7a2 100644 --- a/rmgpy/rmg/pdep.py +++ b/rmgpy/rmg/pdep.py @@ -44,7 +44,7 @@ import rmgpy.reaction from rmgpy.constants import R from rmgpy.data.kinetics.library import LibraryReaction -from rmgpy.exceptions import PressureDependenceError, NetworkError +from rmgpy.exceptions import PressureDependenceError, NetworkError, InvalidMicrocanonicalRateError from rmgpy.pdep import Configuration from rmgpy.rmg.react import react_species from rmgpy.statmech import Conformer @@ -876,7 +876,13 @@ def update(self, reaction_model, pdep_settings, requires_rms=False): # Calculate the rate coefficients self.initialize(Tmin, Tmax, Pmin, Pmax, maximum_grain_size, minimum_grain_count, active_j_rotor, active_k_rotor, rmgmode) - K = self.calculate_rate_coefficients(Tlist, Plist, method) + try: + K = self.calculate_rate_coefficients(Tlist, Plist, method) + except InvalidMicrocanonicalRateError: + if output_directory: + job.draw(output_directory, file_format='pdf') + logging.info(f"Network {self.index} has been drawn and saved as a pdf in {output_directory}.") + raise # Generate PDepReaction objects configurations = [] From af7e324f40a10d021c6b18a345396feb43499aaf Mon Sep 17 00:00:00 2001 From: Richard West Date: Wed, 16 Jul 2025 16:21:58 -0400 Subject: [PATCH 3/6] PressureDependenceJob.draw(output_directory) now takes optional filename_stem argument. Instead of it always being called network.pdf you can now change what it's called. Default is still network.pdf, and argument order is preserved if you call as ordered arguments, for backwards compatibility. This new feature is used when saving the diagram in the case of an InvalidMicrocanonicalRateError --- arkane/pdep.py | 8 ++++++-- rmgpy/rmg/pdep.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/arkane/pdep.py b/arkane/pdep.py index 60fb0fd05d..7f0e6b22d3 100644 --- a/arkane/pdep.py +++ b/arkane/pdep.py @@ -618,7 +618,7 @@ def plot(self, output_directory): plt.savefig(os.path.join(output_directory, 'plots', 'kinetics_{0:d}.pdf'.format(count))) plt.close() - def draw(self, output_directory, file_format='pdf'): + def draw(self, output_directory, file_format='pdf', filename_stem='network'): """ Generate a PDF drawing of the pressure-dependent reaction network. This requires that Cairo and its Python wrapper be available; if not, @@ -626,6 +626,10 @@ def draw(self, output_directory, file_format='pdf'): You may also generate different formats of drawings, by changing format to one of the following: `pdf`, `svg`, `png`. + + The default filename stem is 'network', which will result in a file + named 'network.pdf' in the specified output directory. You can change + this by passing a different `filename_stem` argument. """ # Skip this step if cairo is not installed @@ -639,7 +643,7 @@ def draw(self, output_directory, file_format='pdf'): from rmgpy.pdep.draw import NetworkDrawer - path = os.path.join(output_directory, 'network.' + file_format) + path = os.path.join(output_directory, f'{filename_stem}.{file_format}') NetworkDrawer().draw(self.network, file_format=file_format, path=path) diff --git a/rmgpy/rmg/pdep.py b/rmgpy/rmg/pdep.py index f5433cd7a2..8546519b59 100644 --- a/rmgpy/rmg/pdep.py +++ b/rmgpy/rmg/pdep.py @@ -880,7 +880,7 @@ def update(self, reaction_model, pdep_settings, requires_rms=False): K = self.calculate_rate_coefficients(Tlist, Plist, method) except InvalidMicrocanonicalRateError: if output_directory: - job.draw(output_directory, file_format='pdf') + job.draw(output_directory, filename_stem=f'network{self.index:d}_{len(self.isomers)}', file_format='pdf') logging.info(f"Network {self.index} has been drawn and saved as a pdf in {output_directory}.") raise From a364d08b00989f5e1746fd0632db3d9ff26fab76 Mon Sep 17 00:00:00 2001 From: Richard West Date: Wed, 16 Jul 2025 16:30:24 -0400 Subject: [PATCH 4/6] Make drawing all PES diagrams an option in the input file. You can set generatePESDiagrams=True in the options() block of an input file if you want to save them all. This is presumably a runtime penalty, or at least a hard disk space penalty, and probably shouldn't be a default. Default is False. Fixup: When the pdep_settings was made from an Arkane explorer job rather than an RMG run, the generate_PES_diagrams attribute does not exist. So we now assume False if not specified. --- documentation/source/users/rmg/input.rst | 3 +++ examples/rmg/1,3-hexadiene/input.py | 1 + examples/rmg/commented/input.py | 2 ++ examples/rmg/ethane-oxidation/input.py | 1 + rmgpy/rmg/input.py | 6 +++++- rmgpy/rmg/main.py | 3 +++ rmgpy/rmg/pdep.py | 2 ++ 7 files changed, 17 insertions(+), 1 deletion(-) diff --git a/documentation/source/users/rmg/input.rst b/documentation/source/users/rmg/input.rst index 1106f3a341..e9198d568d 100644 --- a/documentation/source/users/rmg/input.rst +++ b/documentation/source/users/rmg/input.rst @@ -965,6 +965,7 @@ Miscellaneous options:: units='si', generateOutputHTML=True, generatePlots=False, + generatePESDiagrams=False, saveSimulationProfiles=True, verboseComments=False, saveEdgeSpecies=True, @@ -986,6 +987,8 @@ HTML file for your model containing all the species and reactions. Turning this Setting ``generatePlots`` to ``True`` will generate a number of plots describing the statistics of the RMG job, including the reaction model core and edge size and memory use versus execution time. These will be placed in the output directory in the plot/ folder. +Setting ``generatePESDiagrams`` to ``True`` will generate potential energy surface diagrams for each pressure dependent network in the model. These diagrams will be saved in the ``pdep/`` folder in the output directory. Only applicable if pressure dependence is enabled. + Setting ``saveSimulationProfiles`` to ``True`` will make RMG save csv files of the simulation in .csv files in the ``solver/`` folder. The filename will be ``simulation_1_26.csv`` where the first number corresponds to the reaciton system, and the second number corresponds to the total number of species at the point of the simulation. Therefore, the highest second number will indicate the latest simulation that RMG has complete while enlarging the core model. The information inside the csv file will provide the time, reactor volume in m^3, as well as mole fractions of the individual species. Setting ``verboseComments`` to ``True`` will make RMG generate chemkin files with complete verbose commentary for the kinetic and thermo parameters. This will be helpful in debugging what values are being averaged for the kinetics. Note that this may produce very large files. diff --git a/examples/rmg/1,3-hexadiene/input.py b/examples/rmg/1,3-hexadiene/input.py index 8ccf652c15..b335150a2d 100644 --- a/examples/rmg/1,3-hexadiene/input.py +++ b/examples/rmg/1,3-hexadiene/input.py @@ -89,4 +89,5 @@ units='si', generateOutputHTML=False, generatePlots=False, + generatePESDiagrams=True, ) diff --git a/examples/rmg/commented/input.py b/examples/rmg/commented/input.py index 6cc5173c9f..0d6c78b5fe 100644 --- a/examples/rmg/commented/input.py +++ b/examples/rmg/commented/input.py @@ -217,6 +217,8 @@ generateOutputHTML=True, # generates plots of the RMG's performance statistics. Not helpful if you just want a model. generatePlots=False, + # generates potential energy surface diagrams for pressure dependent networks in the model. + generatePESDiagrams=False, # saves mole fraction of species in 'solver/' to help you create plots saveSimulationProfiles=False, # gets RMG to output comments on where kinetics were obtained in the chemkin file. diff --git a/examples/rmg/ethane-oxidation/input.py b/examples/rmg/ethane-oxidation/input.py index 64fdfbd67c..f5c378e069 100644 --- a/examples/rmg/ethane-oxidation/input.py +++ b/examples/rmg/ethane-oxidation/input.py @@ -67,4 +67,5 @@ units='si', generateOutputHTML=False, generatePlots=False, + generatePESDiagrams=True, ) diff --git a/rmgpy/rmg/input.py b/rmgpy/rmg/input.py index 05a5569efd..8c34e815d4 100644 --- a/rmgpy/rmg/input.py +++ b/rmgpy/rmg/input.py @@ -1369,7 +1369,7 @@ def pressure_dependence( def options(name='Seed', generateSeedEachIteration=True, saveSeedToDatabase=False, units='si', saveRestartPeriod=None, - generateOutputHTML=False, generatePlots=False, saveSimulationProfiles=False, verboseComments=False, + generateOutputHTML=False, generatePlots=False, generatePESDiagrams=False, saveSimulationProfiles=False, verboseComments=False, saveEdgeSpecies=False, keepIrreversible=False, trimolecularProductReversible=True, wallTime='00:00:00:00', saveSeedModulus=-1): if saveRestartPeriod: @@ -1386,6 +1386,9 @@ def options(name='Seed', generateSeedEachIteration=True, saveSeedToDatabase=Fals logging.warning('Generate Output HTML option was turned on. Note that this will slow down model generation.') rmg.generate_output_html = generateOutputHTML rmg.generate_plots = generatePlots + rmg.generate_PES_diagrams = generatePESDiagrams + if generatePESDiagrams: + logging.info('Potential Energy Surface diagrams will be generated in the "pdep" folder.') rmg.save_simulation_profiles = saveSimulationProfiles rmg.verbose_comments = verboseComments if saveEdgeSpecies: @@ -1835,6 +1838,7 @@ def save_input_file(path, rmg): f.write(' units = "{0}",\n'.format(rmg.units)) f.write(' generateOutputHTML = {0},\n'.format(rmg.generate_output_html)) f.write(' generatePlots = {0},\n'.format(rmg.generate_plots)) + f.write(' generatePESDiagrams = {0},\n'.format(rmg.generate_PES_diagrams)) f.write(' saveSimulationProfiles = {0},\n'.format(rmg.save_simulation_profiles)) f.write(' saveEdgeSpecies = {0},\n'.format(rmg.save_edge_species)) f.write(' keepIrreversible = {0},\n'.format(rmg.keep_irreversible)) diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index f7241499ae..88ea75ff4c 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -143,6 +143,7 @@ class RMG(util.Subject): `units` The unit system to use to save output files (currently must be 'si') `generate_output_html` ``True`` to draw pictures of the species and reactions, saving a visualized model in an output HTML file. ``False`` otherwise `generate_plots` ``True`` to generate plots of the job execution statistics after each iteration, ``False`` otherwise + `generate_PES_diagrams` ``True`` to generate potential energy surface diagrams for pressure dependent networks in the model, ``False`` otherwise `verbose_comments` ``True`` to keep the verbose comments for database estimates, ``False`` otherwise `save_edge_species` ``True`` to save chemkin and HTML files of the edge species, ``False`` otherwise `keep_irreversible` ``True`` to keep ireversibility of library reactions as is ('<=>' or '=>'). ``False`` (default) to force all library reactions to be reversible ('<=>') @@ -222,6 +223,7 @@ def clear(self): self.units = "si" self.generate_output_html = None self.generate_plots = None + self.generate_PES_diagrams = None self.save_simulation_profiles = None self.verbose_comments = None self.save_edge_species = None @@ -271,6 +273,7 @@ def load_input(self, path=None): if self.pressure_dependence: self.pressure_dependence.output_file = self.output_directory self.reaction_model.pressure_dependence = self.pressure_dependence + self.pressure_dependence.generate_PES_diagrams = self.generate_PES_diagrams if self.solvent: self.reaction_model.solvent_name = self.solvent diff --git a/rmgpy/rmg/pdep.py b/rmgpy/rmg/pdep.py index 8546519b59..464aadaee6 100644 --- a/rmgpy/rmg/pdep.py +++ b/rmgpy/rmg/pdep.py @@ -872,6 +872,8 @@ def update(self, reaction_model, pdep_settings, requires_rms=False): if output_directory: job.save_input_file( os.path.join(output_directory, 'pdep', 'network{0:d}_{1:d}.py'.format(self.index, len(self.isomers)))) + if getattr(pdep_settings, 'generate_PES_diagrams', False): + job.draw(os.path.join(output_directory, 'pdep'), filename_stem=f'network{self.index:d}_{len(self.isomers):d}', file_format='pdf') # Calculate the rate coefficients self.initialize(Tmin, Tmax, Pmin, Pmax, maximum_grain_size, minimum_grain_count, active_j_rotor, active_k_rotor, From d7cb5cb684d977eba6c6e410258bf8a6a599bab6 Mon Sep 17 00:00:00 2001 From: Richard West Date: Wed, 23 Jul 2025 23:40:35 -0300 Subject: [PATCH 5/6] Allow comment parsing to not die from "in family __" comment. It doesn't actually _parse_ the new comment to figure out what family the thing comes from, but in most contexts it is not needed. At least now it doesn't crash. Also added the new phrase to at least one item in the testing examples so the unit tests could catch this. Co-authored-by: Richard West Co-authored-by: Sevy Harris --- rmgpy/data/kinetics/family.py | 1 + test/rmgpy/test_data/parsing_data/chem_annotated.inp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rmgpy/data/kinetics/family.py b/rmgpy/data/kinetics/family.py index 102c5f13ff..4bb0ee949e 100644 --- a/rmgpy/data/kinetics/family.py +++ b/rmgpy/data/kinetics/family.py @@ -4487,6 +4487,7 @@ def extract_source_from_comments(self, reaction): template_matches = re.search(template_pattern, full_comment_string) if autogen_node_matches is not None: # autogenerated trees template_str = autogen_node_matches.group(1).split('Multiplied by reaction path degeneracy')[0].strip() + template_str = template_str.split('in family')[0].strip() tokens = template_str.split() if len(tokens) == 2: # The node was probably split because wordwrap was turned off assert len(template_str) > 115, 'The node name is too short to have been broken up by the chemkin writer' diff --git a/test/rmgpy/test_data/parsing_data/chem_annotated.inp b/test/rmgpy/test_data/parsing_data/chem_annotated.inp index dbeb771e6f..2e81aa0fa7 100644 --- a/test/rmgpy/test_data/parsing_data/chem_annotated.inp +++ b/test/rmgpy/test_data/parsing_data/chem_annotated.inp @@ -259,7 +259,7 @@ HCCO(23)(+M)=O(2)+C2H(21)(+M) 1.000e+00 0.000 0.000 ! Reaction index: Chemkin #25; RMG #97 ! Template reaction: Disproportionation ! Flux pairs: CH3CHCH3, C3H6(28); C2H5(27), C2H6; -! Estimated from node Root_Ext-2R!H-R_2R!H->C_4R->C +! Estimated from node Root_Ext-2R!H-R_2R!H->C_4R->C in family Disproportionation. ! Multiplied by reaction path degeneracy 6.0 C2H5(27)+CH3CHCH3<=>C2H6+C3H6(28) 3.000e+11 0.000 0.000 From 0ed78ec087dec96d65e393bca6e911ca40eb4f92 Mon Sep 17 00:00:00 2001 From: Richard West Date: Wed, 23 Jul 2025 23:46:43 -0300 Subject: [PATCH 6/6] Tweak comment about saving network pdf for debugging. --- rmgpy/rmg/pdep.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rmgpy/rmg/pdep.py b/rmgpy/rmg/pdep.py index 464aadaee6..54d62df106 100644 --- a/rmgpy/rmg/pdep.py +++ b/rmgpy/rmg/pdep.py @@ -882,8 +882,9 @@ def update(self, reaction_model, pdep_settings, requires_rms=False): K = self.calculate_rate_coefficients(Tlist, Plist, method) except InvalidMicrocanonicalRateError: if output_directory: - job.draw(output_directory, filename_stem=f'network{self.index:d}_{len(self.isomers)}', file_format='pdf') - logging.info(f"Network {self.index} has been drawn and saved as a pdf in {output_directory}.") + filename_stem = f'network{self.index:d}_{len(self.isomers):d}' + job.draw(output_directory, filename_stem=filename_stem, file_format='pdf') + logging.info(f"Network {self.index} has been drawn and saved as {filename_stem}.pdf in {output_directory} to aid debugging.") raise # Generate PDepReaction objects