From d326a1b9f89b8f19ad9fe590d2e78d42ff082ca7 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Sat, 16 Sep 2023 20:16:36 -0400 Subject: [PATCH 01/17] build an rmgmolecule package in place - small updates to rmg-py recipe, more coming later - removed conda_build.yml action since it will now be done in a private repo - added a recipe for rmgmolecule - add warnings on molecule when running from rmgmolecule that some functionality is not available --- .conda/meta.yaml | 8 +- .github/workflows/conda_build.yml | 58 ------- .rmgmolecule_conda/build.sh | 2 + .rmgmolecule_conda/conda_build_config.yaml | 2 + .rmgmolecule_conda/meta.yaml | 53 +++++++ rmgmolecule_setup.py | 176 +++++++++++++++++++++ rmgpy/molecule/draw.py | 12 +- rmgpy/molecule/fragment.py | 8 +- rmgpy/molecule/inchi.py | 8 +- 9 files changed, 261 insertions(+), 66 deletions(-) delete mode 100644 .github/workflows/conda_build.yml create mode 100644 .rmgmolecule_conda/build.sh create mode 100644 .rmgmolecule_conda/conda_build_config.yaml create mode 100644 .rmgmolecule_conda/meta.yaml create mode 100644 rmgmolecule_setup.py diff --git a/.conda/meta.yaml b/.conda/meta.yaml index b1df7678e6..9e74a621fc 100644 --- a/.conda/meta.yaml +++ b/.conda/meta.yaml @@ -11,7 +11,7 @@ build: requirements: build: - - {{ compiler('c') }} # [unix] + - {{ compiler('c') }} host: - cython >=0.25.2 - lpsolve55 @@ -78,10 +78,8 @@ test: - rmgpy - arkane commands: - - rmg.py examples/rmg/superminimal/input.py # [unix] - - Arkane.py examples/arkane/networks/n-butanol/input.py # [unix] - - python %SCRIPTS%\rmg.py examples\rmg\superminimal\input.py # [win] - - python %SCRIPTS\Arkane.py examples\arkane\networks\n-butanol\input.py # [win] + - python-jl rmg.py examples/rmg/superminimal/input.py + - python arkane.py examples/arkane/networks/n-butanol about: home: https://github.com/ReactionMechanismGenerator/RMG-Py diff --git a/.github/workflows/conda_build.yml b/.github/workflows/conda_build.yml deleted file mode 100644 index 8ddbef0877..0000000000 --- a/.github/workflows/conda_build.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Conda Build - -on: - push: - branches: - - stable -jobs: - build-linux: - runs-on: ubuntu-latest - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - environment-file: environment.yml - python-version: 3.7 - activate-environment: rmg_env - - name: Conda info - run: | - conda info - conda list - - name: Build Binary - env: - CONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }} - run: | - conda install -y conda-build - conda install -y anaconda-client - conda config --add channels rmg - conda config --set anaconda_upload yes - conda build --token $CONDA_TOKEN --user rmg .conda - build-osx: - runs-on: macos-latest - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - environment-file: environment.yml - python-version: 3.7 - activate-environment: rmg_env - - name: Conda info - run: | - conda info - conda list - - name: Build Binary - env: - CONDA_TOKEN: ${{ secrets.ANACONDA_TOKEN }} - run: | - conda install -y conda-build - conda install -y anaconda-client - conda config --add channels rmg - conda config --set anaconda_upload yes - xcrun --show-sdk-path - conda build --token $CONDA_TOKEN --user rmg .conda diff --git a/.rmgmolecule_conda/build.sh b/.rmgmolecule_conda/build.sh new file mode 100644 index 0000000000..b68ece3906 --- /dev/null +++ b/.rmgmolecule_conda/build.sh @@ -0,0 +1,2 @@ +# Install rmgmolecule +python rmgmolecule_setup.py install diff --git a/.rmgmolecule_conda/conda_build_config.yaml b/.rmgmolecule_conda/conda_build_config.yaml new file mode 100644 index 0000000000..83e1bc711e --- /dev/null +++ b/.rmgmolecule_conda/conda_build_config.yaml @@ -0,0 +1,2 @@ +numpy: + - 1.19 \ No newline at end of file diff --git a/.rmgmolecule_conda/meta.yaml b/.rmgmolecule_conda/meta.yaml new file mode 100644 index 0000000000..62cb422d87 --- /dev/null +++ b/.rmgmolecule_conda/meta.yaml @@ -0,0 +1,53 @@ +# meta.yaml - package specification for rmgmolecule subpackage +package: + name: rmgmolecule + version: 1.0.0 + +source: + path: ../ + +build: + number: {{ environ.get('GIT_DESCRIBE_NUMBER', 0) }} + +requirements: + build: + - {{ compiler('c') }} # [unix] + host: + - cython >=0.25.2 + - lpsolve55 + - numpy + - openbabel >=3 + - pyrdl + - python==3.7 + - quantities + - rdkit >=2018 + - scipy + - scikit-learn + - setuptools + run: + - cairo + - cairocffi + - cython >=0.25.2 + - gprof2dot + - graphviz + - jinja2 + - jupyter + - lpsolve55 + - {{ pin_compatible('numpy') }} + - openbabel >=3 + - pydot + - pyrdl + - python==3.7 + - quantities + - rdkit >=2018 + - scikit-learn +test: + imports: + - rmgpy.molecule + commands: + - python -c 'from rmgpy.molecule import Molecule; mol=Molecule().from_smiles("CC")' + +about: + home: https://github.com/ReactionMechanismGenerator/RMG-Py + license: MIT + summary: "The ReactionMechanismGenerator Molecule Subpackage" \ No newline at end of file diff --git a/rmgmolecule_setup.py b/rmgmolecule_setup.py new file mode 100644 index 0000000000..8614e22ede --- /dev/null +++ b/rmgmolecule_setup.py @@ -0,0 +1,176 @@ +import sys +import os +from collections import OrderedDict + +try: + from distutils.core import setup + from distutils.extension import Extension +except ImportError: + print("The distutils package is required to build or install RMG Py.") + raise + +try: + from Cython.Build import cythonize + from Cython.Compiler import Options +except ImportError: + print("Cython (http://www.cython.org/) is required to build or install RMG Py.") + raise + +try: + import numpy +except ImportError: + print("NumPy (http://numpy.scipy.org/) is required to build or install RMG Py.") + raise + +# Create annotated HTML files for each of the Cython modules +Options.annotate = True + +directives = { + # Set input language version to python 3 + "language_level": 3, + # Turn on profiling capacity for all Cython modules + # 'profile': True, + # Embed call signatures in cythonized files - enable when building documentation + # 'embedsignature': True, +} + + +main_ext_modules = [ + # Molecules and molecular representations + Extension( + "rmgpy.molecule.atomtype", + ["rmgpy/molecule/atomtype.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.element", + ["rmgpy/molecule/element.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.graph", + ["rmgpy/molecule/graph.pyx"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.group", + ["rmgpy/molecule/group.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.molecule", + ["rmgpy/molecule/molecule.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.symmetry", + ["rmgpy/molecule/symmetry.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.vf2", + ["rmgpy/molecule/vf2.pyx"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.converter", + ["rmgpy/molecule/converter.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.translator", + ["rmgpy/molecule/translator.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.util", + ["rmgpy/molecule/util.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.inchi", + ["rmgpy/molecule/inchi.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.resonance", + ["rmgpy/molecule/resonance.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.pathfinder", + ["rmgpy/molecule/pathfinder.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.molecule.kekulize", + ["rmgpy/molecule/kekulize.pyx"], + include_dirs=["."], + ), + Extension( + "rmgpy.constants", + ["rmgpy/constants.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.quantity", + ["rmgpy/quantity.py"], + include_dirs=["."], + ), + Extension( + "rmgpy.rmgobject", + ["rmgpy/rmgobject.pyx"], + ), +] + +ext_modules = [] +if "install" in sys.argv: + # This is so users can still do simply `python setup.py install` + ext_modules.extend(main_ext_modules) +if "main" in sys.argv: + # This is for `python setup.py build_ext main` + sys.argv.remove("main") + ext_modules.extend(main_ext_modules) +if "minimal" in sys.argv: + # This starts with the full install list, but removes anything that has a pure python mode + # i.e. in only includes things whose source is .pyx + sys.argv.remove("minimal") + temporary_list = [] + temporary_list.extend(main_ext_modules) + for module in temporary_list: + for source in module.sources: + if os.path.splitext(source)[1] == ".pyx": + ext_modules.append(module) + +# Remove duplicates while preserving order: +ext_modules = list(OrderedDict.fromkeys(ext_modules)) + +scripts = [] + +modules = ["rmgpy.exceptions", "rmgpy.version"] + +__version__ = "1.0.0" + +import logging + +logging.error(ext_modules) +# Initiate the build and/or installation +setup( + name="RMG-Py", + version=__version__, + description="Reaction Mechanism Generator", + author="William H. Green and the RMG Team", + author_email="rmg_dev@mit.edu", + url="http://reactionmechanismgenerator.github.io", + packages=[ + "rmgpy.molecule", + ], + py_modules=modules, + scripts=scripts, + ext_modules=cythonize( + ext_modules, + build_dir="build", + compiler_directives=directives, + ), + include_dirs=[".", numpy.get_include()], +) diff --git a/rmgpy/molecule/draw.py b/rmgpy/molecule/draw.py index 6dd6c8b46b..c79fc19ca8 100644 --- a/rmgpy/molecule/draw.py +++ b/rmgpy/molecule/draw.py @@ -61,7 +61,12 @@ from rdkit.Chem import AllChem from rmgpy.molecule.molecule import Atom, Molecule -from rmgpy.qm.molecule import Geometry + +Geometry = None +try: + from rmgpy.qm.molecule import Geometry +except ImportError: + logging.info("Unable to import Geometry rmgpy.qm.molecule - feature disabled.") ################################################################################ @@ -437,6 +442,11 @@ def _generate_coordinates(self): # Generate the RDkit molecule from the RDkit molecule, use geometry # in order to match the atoms in the rdmol with the atoms in the # RMG molecule (which is required to extract coordinates). + if Geometry is None: + raise RuntimeError(""" +Missing rmgpy.qm.molecule.Geometry required for 2D coordinate generation. +Please install the full version of RMG to use this function. + """) self.geometry = Geometry(None, None, self.molecule, None) rdmol, rd_atom_idx = self.geometry.rd_build() diff --git a/rmgpy/molecule/fragment.py b/rmgpy/molecule/fragment.py index 8b30c1e32e..06e25f7346 100644 --- a/rmgpy/molecule/fragment.py +++ b/rmgpy/molecule/fragment.py @@ -1020,7 +1020,13 @@ def assign_representative_molecule(self): def assign_representative_species(self): - from rmgpy.species import Species + try: + from rmgpy.species import Species + except ImportError as ie: + raise RuntimeError( + "Cannot call Fragment.assign_representative_species() in rmgmolecule installation." + " Please install the full version of RMG." + ) from ie self.assign_representative_molecule() self.species_repr = Species(molecule=[self.mol_repr]) self.symmetry_number = self.get_symmetry_number() diff --git a/rmgpy/molecule/inchi.py b/rmgpy/molecule/inchi.py index 59e49d8522..789a00e0b0 100644 --- a/rmgpy/molecule/inchi.py +++ b/rmgpy/molecule/inchi.py @@ -702,7 +702,13 @@ def _convert_3_atom_2_bond_path(start, mol): with a number of actions that reflect the changes in bond orders and unpaired electrons that the molecule should undergo. """ - from rmgpy.data.kinetics.family import ReactionRecipe + try: + from rmgpy.data.kinetics.family import ReactionRecipe + except ImportError as ie: + raise RuntimeError( + "Cannot call inchi._convert_3_atom_2_bond_path() in rmgmolecule installation." + " Please install the full version of RMG." + ) from ie def is_valid(mol): """Check if total bond order of oxygen atoms is smaller than 4.""" From 4a0ff2de7ecd56e2b10a8dcc10e9bab7ee8736e4 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 26 Sep 2023 21:59:20 -0400 Subject: [PATCH 02/17] fix the RMG-Py conda build changes required to make the RMG-Py conda build work again. detailed description: - recipe now contains the appropriate requirements - removed macos travis-specific thing - added comments in build.sh for historical preservation of my failed efforts - other changes are the initial steps toward making the conda build work, future commits will make the build tests pass the ci runs on a self-hosted runner in the green group offices. required a specific version of conda build and libmamba to be installed (see PR of this commit for more details), currently fails tests because rmg tries to import Julia during testing. will now need to 'bury' the Julia import and make it optional, including docs updates, etc. --- .conda/build.sh | 17 +++- .conda/conda_build_config.yaml | 3 - .conda/meta.yaml | 175 ++++++++++++++++++++++++++------- environment.yml | 12 +-- rmg.py | 2 +- setup.py | 1 - utilities.py | 2 +- 7 files changed, 156 insertions(+), 56 deletions(-) diff --git a/.conda/build.sh b/.conda/build.sh index c942c713eb..c0ec39927a 100644 --- a/.conda/build.sh +++ b/.conda/build.sh @@ -1,6 +1,15 @@ -# Install RMG +# conda build script +# +# conda executes this script during the build process to make the rmg +# binary make install -# lazy "install" of everything in our 'external' folder. -# most of which should probably be elsewhere -cp -R ${SRC_DIR}/external ${SP_DIR} +# The below code does not work because the Julia programming langauge is a very interesting technical demo that is completely unfit for actual +# deployment in any real world projects. +# +# RMG will be shipped as a pure python package, and then RMS installed by the user. +# +# export PYTHON=$PREFIX/bin/python +# export PYTHONPATH=$SRC_DIR:$PYTHONPATH +# python -c "import julia; julia.install(); import diffeqpy; diffeqpy.install()" +# julia -e 'using Pkg; Pkg.add(PackageSpec(name="Functors",version="0.4.3")); Pkg.pin("Functors"); Pkg.add(PackageSpec(name="ReactionMechanismSimulator",rev="for_rmg")); using ReactionMechanismSimulator' diff --git a/.conda/conda_build_config.yaml b/.conda/conda_build_config.yaml index 6a0cca7f0c..4fe9a38c7b 100644 --- a/.conda/conda_build_config.yaml +++ b/.conda/conda_build_config.yaml @@ -3,6 +3,3 @@ python: numpy: - 1.15 -# Specifically for Travis build. Should change if building locally. -CONDA_BUILD_SYSROOT: - - /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk # [osx] diff --git a/.conda/meta.yaml b/.conda/meta.yaml index 9e74a621fc..7afdd611e9 100644 --- a/.conda/meta.yaml +++ b/.conda/meta.yaml @@ -13,64 +13,165 @@ requirements: build: - {{ compiler('c') }} host: + - cairo + - cairocffi + - ffmpeg + - xlrd + - xlwt + - h5py + - graphviz + - markupsafe + - psutil + - ncurses + - suitesparse + + # external software tools for chemistry + - coolprop + - cantera=2.6 + - mopac + - cclib >=1.6.3,!=1.8.0 + - openbabel >=3 + + # Python tools + - python >=3.7 + - coverage - cython >=0.25.2 + - scikit-learn + - scipy + - numpy >=1.10.0 + - pydot + - jinja2 + - jupyter + - pymongo + - pyparsing + - pyyaml + - networkx + - matplotlib >=1.5 + - mpmath + - pandas + + # packages we maintain + - rmgdatabase + - gprof2dot - lpsolve55 - - numpy - - openbabel >=3 - - pydas >=1.0.2 - - pydqed >=1.0.1 + - muq2 + - numdifftools + - pydas >=1.0.3 + - pydqed >=1.0.3 - pyrdl - - python - quantities - - rdkit >=2018 - - scipy - - setuptools + - symmetry + - chemprop==0.0.1 + - rdkit >=2020.03.3.0 + + - liblapack =*=*mkl run: - cairo - cairocffi - - cantera >=2.3.0 - - cclib >=1.6.3 - - chemprop + - ffmpeg + - xlrd + - xlwt + - h5py + - graphviz + - markupsafe + - psutil + - ncurses + - suitesparse + + # external software tools for chemistry - coolprop + - cantera=2.6 + - mopac + - cclib >=1.6.3,!=1.8.0 + - openbabel >=3 + + # Python tools + - python >=3.7 - coverage - cython >=0.25.2 - - ffmpeg - - gprof2dot - - graphviz - - h5py + - scikit-learn + - scipy + - numpy >=1.10.0 + - pydot - jinja2 - jupyter - - lpsolve55 - - markupsafe + - pymongo + - pyparsing + - pyyaml + - networkx - matplotlib >=1.5 - - mopac - mpmath + - pandas + + # packages we maintain + - rmgdatabase + - gprof2dot + - lpsolve55 - muq2 - - networkx - - nose - numdifftools - - {{ pin_compatible('numpy') }} - - openbabel >=3 - - pandas + - pydas >=1.0.3 + - pydqed >=1.0.3 + - pyrdl + - quantities + - symmetry + - chemprop==0.0.1 + - rdkit >=2020.03.3.0 + + - liblapack =*=*mkl +test: + requires: + - cairo + - cairocffi + - ffmpeg + - xlrd + - xlwt + - h5py + - graphviz + - markupsafe - psutil - - pydas >=1.0.2 + - ncurses + - suitesparse + + # external software tools for chemistry + - coolprop + - cantera=2.6 + - mopac + - cclib >=1.6.3,!=1.8.0 + - openbabel >=3 + + # Python tools + - python >=3.7 + - coverage + - cython >=0.25.2 + - scikit-learn + - scipy + - numpy >=1.10.0 - pydot - - pydqed >=1.0.1 + - jinja2 + - jupyter - pymongo - pyparsing - - pyrdl - - python - pyyaml - - pyzmq + - networkx + - matplotlib >=1.5 + - mpmath + - pandas + + # packages we maintain + - rmgdatabase + - gprof2dot + - lpsolve55 + - muq2 + - numdifftools + - pydas >=1.0.3 + - pydqed >=1.0.3 + - pyrdl - quantities - - rdkit >=2018 - - rmgdatabase >=3.2.0 - - scikit-learn - - scipy - symmetry - - xlrd - - xlwt -test: + - chemprop==0.0.1 + - rdkit >=2020.03.3.0 + + - liblapack =*=*mkl source_files: - 'examples/rmg/superminimal' - 'examples/arkane/networks/n-butanol' @@ -78,8 +179,8 @@ test: - rmgpy - arkane commands: - - python-jl rmg.py examples/rmg/superminimal/input.py - - python arkane.py examples/arkane/networks/n-butanol + - rmg.py examples/rmg/superminimal/input.py + - arkane.py examples/arkane/networks/n-butanol about: home: https://github.com/ReactionMechanismGenerator/RMG-Py diff --git a/environment.yml b/environment.yml index 5155a74b09..5080ac2063 100644 --- a/environment.yml +++ b/environment.yml @@ -45,10 +45,6 @@ dependencies: - conda-forge::cclib >=1.6.3,!=1.8.0 - conda-forge::openbabel >= 3 -# general-purpose external software tools - - conda-forge::julia>=1.8.5,!=1.9.0 - - conda-forge::pyjulia >=0.6 - # Python tools - python >=3.7 - coverage @@ -75,21 +71,20 @@ dependencies: - matplotlib >=1.5 - mpmath - pandas + - conda-forge::numdifftools # packages we maintain - rmg::gprof2dot - rmg::lpsolve55 - rmg::muq2 - - rmg::numdifftools - rmg::pydas >=1.0.3 - rmg::pydqed >=1.0.3 - rmg::pyrdl - - rmg::pyrms - rmg::quantities - rmg::symmetry # packages we would like to stop maintaining (and why) - - rmg::diffeqpy + #- rmg::diffeqpy # we should use the official verison https://github.com/SciML/diffeqpy), # rather than ours (which is only made so that we can get it from conda) # It is only on pip, so we will need to do something like: @@ -103,8 +98,7 @@ dependencies: # We should use the official channel, not sure how difficult this # change will be. -# conda mutex metapackage - - nomkl + - liblapack =*=*mkl # additional packages that are required, but not specified here (and why) # pydqed, pydas, mopac, and likely others require a fortran compiler (specifically gfortran) diff --git a/rmg.py b/rmg.py index 26c27c00a4..0b27098b81 100644 --- a/rmg.py +++ b/rmg.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python-jl +#!/usr/bin/env python ############################################################################### # # diff --git a/setup.py b/setup.py index 604a856eb1..17fdab4a88 100644 --- a/setup.py +++ b/setup.py @@ -215,7 +215,6 @@ 'scripts/standardizeModelSpeciesNames.py', 'scripts/thermoEstimator.py', 'scripts/isotopes.py', - 'testing/databaseTest.py', ] modules = [] diff --git a/utilities.py b/utilities.py index c8e7425818..f84963a632 100644 --- a/utilities.py +++ b/utilities.py @@ -310,7 +310,7 @@ def update_headers(): start of each file, be sure to double-check the results to make sure important lines aren't accidentally overwritten. """ - shebang = """#!/usr/bin/env python-jl + shebang = """#!/usr/bin/env python """ From 2f48a09fb1606c058ee7745aeecc66961266654e Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 26 Sep 2023 22:16:36 -0400 Subject: [PATCH 03/17] slightly reduce the required dependencies for rmgmolecule --- .rmgmolecule_conda/meta.yaml | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/.rmgmolecule_conda/meta.yaml b/.rmgmolecule_conda/meta.yaml index 62cb422d87..ce1e04118b 100644 --- a/.rmgmolecule_conda/meta.yaml +++ b/.rmgmolecule_conda/meta.yaml @@ -11,36 +11,31 @@ build: requirements: build: - - {{ compiler('c') }} # [unix] + - {{ compiler('c') }} host: + - cairo + - cairocffi - cython >=0.25.2 - lpsolve55 - numpy - openbabel >=3 + - pydot - pyrdl - python==3.7 - quantities - rdkit >=2018 - - scipy - - scikit-learn - - setuptools run: - cairo - cairocffi - cython >=0.25.2 - - gprof2dot - - graphviz - - jinja2 - - jupyter - lpsolve55 - - {{ pin_compatible('numpy') }} + - numpy - openbabel >=3 - pydot - pyrdl - python==3.7 - quantities - rdkit >=2018 - - scikit-learn test: imports: - rmgpy.molecule From 16e285f44ec6f21f7f28a4901c5446dca369500e Mon Sep 17 00:00:00 2001 From: Jackson Burns <33505528+JacksonBurns@users.noreply.github.com> Date: Thu, 28 Sep 2023 19:27:34 -0400 Subject: [PATCH 04/17] remove libmkl trick - incompatible with latest conda --- .conda/meta.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.conda/meta.yaml b/.conda/meta.yaml index 7afdd611e9..09ca7ba811 100644 --- a/.conda/meta.yaml +++ b/.conda/meta.yaml @@ -63,8 +63,6 @@ requirements: - symmetry - chemprop==0.0.1 - rdkit >=2020.03.3.0 - - - liblapack =*=*mkl run: - cairo - cairocffi @@ -116,8 +114,6 @@ requirements: - symmetry - chemprop==0.0.1 - rdkit >=2020.03.3.0 - - - liblapack =*=*mkl test: requires: - cairo @@ -170,8 +166,6 @@ test: - symmetry - chemprop==0.0.1 - rdkit >=2020.03.3.0 - - - liblapack =*=*mkl source_files: - 'examples/rmg/superminimal' - 'examples/arkane/networks/n-butanol' From d8cae9990899e624b57bd6e6dcd7d31f5a763f4e Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Fri, 29 Sep 2023 15:42:22 -0400 Subject: [PATCH 05/17] bury import of Julia dependencies, avoid extra julia operations - the import for the Julia dependencies is now properly buried, s.t. it will not interfere with running Arkane w/o Julia installed - inside main, input, and model there is a new argument requires_rms. This argument is passed to prune, add_species*, and other functions that use those functions to avoid moving things to RMS when we either (1) are not using an RMS reactor or (2) don't even have Julia dependencies installed at all --- rmgpy/rmg/input.py | 5 +- rmgpy/rmg/main.py | 36 +++++---- rmgpy/rmg/model.py | 81 +++++++++---------- ...=> reactionmechanismsimulator_reactors.py} | 34 ++++---- 4 files changed, 80 insertions(+), 76 deletions(-) rename rmgpy/rmg/{reactors.py => reactionmechanismsimulator_reactors.py} (99%) diff --git a/rmgpy/rmg/input.py b/rmgpy/rmg/input.py index e04cd82e2e..f776d63027 100644 --- a/rmgpy/rmg/input.py +++ b/rmgpy/rmg/input.py @@ -48,9 +48,10 @@ from rmgpy.solver.surface import SurfaceReactor from rmgpy.util import as_list from rmgpy.data.surface import MetalDatabase -from rmgpy.rmg.reactors import Reactor, ConstantVIdealGasReactor, ConstantTLiquidSurfaceReactor, ConstantTVLiquidReactor, ConstantTPIdealGasReactor from rmgpy.data.vaporLiquidMassTransfer import liquidVolumetricMassTransferCoefficientPowerLaw from rmgpy.molecule.fragment import Fragment +from rmgpy.rmg.reactionmechanismsimulator_reactors import Reactor, ConstantVIdealGasReactor, ConstantTLiquidSurfaceReactor, ConstantTVLiquidReactor, ConstantTPIdealGasReactor, NO_JULIA + ################################################################################ @@ -1558,6 +1559,8 @@ def read_input_file(path, rmg0): exec(f.read(), global_context, local_context) except (NameError, TypeError, SyntaxError) as e: logging.error('The input file "{0}" was invalid:'.format(full_path)) + if NO_JULIA: + logging.error("During runtime, import of Julia dependencies failed. To use phase systems and RMS reactors, install RMG-Py with RMS.") logging.exception(e) raise finally: diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index 8291db4a13..3ff5c6c814 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -79,7 +79,8 @@ from rmgpy.tools.plot import plot_sensitivity from rmgpy.tools.uncertainty import Uncertainty, process_local_results from rmgpy.yml import RMSWriter -from rmgpy.rmg.reactors import Reactor +from rmgpy.rmg.reactionmechanismsimulator_reactors import Reactor as RMSReactor +from rmgpy.rmg.reactionmechanismsimulator_reactors import NO_JULIA ################################################################################ @@ -506,6 +507,12 @@ def initialize(self, **kwargs): # Read input file self.load_input(self.input_file) + + # Check if ReactionMechanismSimulator reactors are being used + # if RMS is not installed but the user attempted to use it, the load_input_file would have failed + # if RMS is installed but they did not use it, we can avoid extra work + # if RMS is not installed and they did not use it, we avoid calling certain functions that would raise an error + requires_rms = any(isinstance(RMSReactor, self.reaction_systems)) if kwargs.get("restart", ""): import rmgpy.rmg.input @@ -549,10 +556,10 @@ def initialize(self, **kwargs): self.load_database() for spec in self.initial_species: - self.reaction_model.add_species_to_edge(spec) + self.reaction_model.add_species_to_edge(spec, requires_rms=requires_rms) for reaction_system in self.reaction_systems: - if isinstance(reaction_system, Reactor): + if not NO_JULIA and isinstance(reaction_system, RMSReactor): reaction_system.finish_termination_criteria() # Load restart seed mechanism (if specified) @@ -617,12 +624,12 @@ def initialize(self, **kwargs): # Seed mechanisms: add species and reactions from seed mechanism # DON'T generate any more reactions for the seed species at this time for seed_mechanism in self.seed_mechanisms: - self.reaction_model.add_seed_mechanism_to_core(seed_mechanism, react=False) + self.reaction_model.add_seed_mechanism_to_core(seed_mechanism, react=False, requires_rms=requires_rms) # Reaction libraries: add species and reactions from reaction library to the edge so # that RMG can find them if their rates are large enough for library, option in self.reaction_libraries: - self.reaction_model.add_reaction_library_to_edge(library) + self.reaction_model.add_reaction_library_to_edge(library, requires_rms=requires_rms) # Also always add in a few bath gases (since RMG-Java does) for label, smiles in [("Ar", "[Ar]"), ("He", "[He]"), ("Ne", "[Ne]"), ("N2", "N#N")]: @@ -703,9 +710,7 @@ def initialize(self, **kwargs): # advantages to write it here: this is run only once (as species indexes does not change over the generation) if self.solvent is not None: for index, reaction_system in enumerate(self.reaction_systems): - if ( - not isinstance(reaction_system, Reactor) and reaction_system.const_spc_names is not None - ): # if no constant species provided do nothing + if ((NO_JULIA or not isinstance(reaction_system, RMSReactor)) and reaction_system.const_spc_names is not None): # if no constant species provided do nothing reaction_system.get_const_spc_indices(self.reaction_model.core.species) # call the function to identify indices in the solver self.initialize_reaction_threshold_and_react_flags() @@ -714,6 +719,7 @@ def initialize(self, **kwargs): self.reaction_model.initialize_index_species_dict() self.initialize_seed_mech() + return requires_rms def register_listeners(self): """ @@ -748,7 +754,7 @@ def execute(self, initialize=True, **kwargs): """ if initialize: - self.initialize(**kwargs) + requires_rms = self.initialize(**kwargs) # register listeners self.register_listeners() @@ -778,7 +784,7 @@ def execute(self, initialize=True, **kwargs): # Update react flags if self.filter_reactions: # Run the reaction system to update threshold and react flags - if isinstance(reaction_system, Reactor): + if not NO_JULIA and requires_rms and isinstance(reaction_system, RMSReactor): self.update_reaction_threshold_and_react_flags( rxn_sys_unimol_threshold=np.zeros((len(self.reaction_model.core.species),), bool), rxn_sys_bimol_threshold=np.zeros((len(self.reaction_model.core.species), len(self.reaction_model.core.species)), bool), @@ -899,7 +905,7 @@ def execute(self, initialize=True, **kwargs): prune = False try: - if isinstance(reaction_system, Reactor): + if not NO_JULIA and requires_rms and isinstance(reaction_system, RMSReactor): ( terminated, resurrected, @@ -992,7 +998,7 @@ def execute(self, initialize=True, **kwargs): # Add objects to enlarge to the core first for objectToEnlarge in objects_to_enlarge: - self.reaction_model.enlarge(objectToEnlarge) + self.reaction_model.enlarge(objectToEnlarge, requires_rms=requires_rms) if model_settings.filter_reactions: # Run a raw simulation to get updated reaction system threshold values @@ -1001,7 +1007,7 @@ def execute(self, initialize=True, **kwargs): temp_model_settings.tol_keep_in_edge = 0 if not resurrected: try: - if isinstance(reaction_system, Reactor): + if not NO_JULIA and requires_rms and isinstance(reaction_system, RMSReactor): ( terminated, resurrected, @@ -1070,7 +1076,7 @@ def execute(self, initialize=True, **kwargs): skip_update=True, ) logging.warning( - "Reaction thresholds/flags for Reaction System {0} was not updated due " "to resurrection".format(index + 1) + "Reaction thresholds/flags for Reaction System {0} was not updated due to resurrection".format(index + 1) ) logging.info("") @@ -1093,6 +1099,7 @@ def execute(self, initialize=True, **kwargs): unimolecular_react=self.unimolecular_react, bimolecular_react=self.bimolecular_react, trimolecular_react=self.trimolecular_react, + requires_rms=requires_rms, ) if old_edge_size != len(self.reaction_model.edge.reactions) or old_core_size != len(self.reaction_model.core.reactions): @@ -1126,6 +1133,7 @@ def execute(self, initialize=True, **kwargs): model_settings.tol_move_to_core, model_settings.maximum_edge_species, model_settings.min_species_exist_iterations_for_prune, + requires_rms=requires_rms, ) # Perform garbage collection after pruning collected = gc.collect() diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index 951b670d00..326ca1657b 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -57,7 +57,7 @@ from rmgpy.species import Species from rmgpy.thermo.thermoengine import submit from rmgpy.rmg.decay import decay_species -from rmgpy.rmg.reactors import PhaseSystem, Phase, Interface, Reactor +from rmgpy.rmg.reactionmechanismsimulator_reactors import PhaseSystem, Phase, Interface, Reactor, NO_JULIA from rmgpy.molecule.fragment import Fragment ################################################################################ @@ -75,7 +75,7 @@ def __init__(self, species=None, reactions=None, phases=None, interfaces={}): if phases is None: phases = {"Default": Phase(), "Surface": Phase()} interfaces = {frozenset({"Default", "Surface"}): Interface(list(phases.values()))} - self.phase_system = PhaseSystem(phases, interfaces) + self.phase_system = None if NO_JULIA else PhaseSystem(phases, interfaces) def __reduce__(self): """ @@ -589,7 +589,7 @@ def make_new_pdep_reaction(self, forward): return forward - def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bimolecular_react=None, trimolecular_react=None): + def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bimolecular_react=None, trimolecular_react=None, requires_rms=False): """ Enlarge a reaction model by processing the objects in the list `new_object`. If `new_object` is a @@ -635,7 +635,7 @@ def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bi display(new_species) # if running in IPython --pylab mode, draws the picture! # Add new species - reactions_moved_from_edge = self.add_species_to_core(new_species) + reactions_moved_from_edge = self.add_species_to_core(new_species, requires_rms=requires_rms) elif isinstance(new_object, tuple) and isinstance(new_object[0], PDepNetwork) and self.pressure_dependence: pdep_network, new_species = new_object @@ -799,7 +799,7 @@ def clear_surface_adjustments(self): self.new_surface_spcs_loss = set() self.new_surface_rxns_loss = set() - def process_new_reactions(self, new_reactions, new_species, pdep_network=None, generate_thermo=True, generate_kinetics=True): + def process_new_reactions(self, new_reactions, new_species, pdep_network=None, generate_thermo=True, generate_kinetics=True, requires_rms=False): """ Process a list of newly-generated reactions involving the new core species or explored isomer `new_species` in network `pdep_network`. @@ -821,12 +821,12 @@ def process_new_reactions(self, new_reactions, new_species, pdep_network=None, g if spec not in self.core.species: all_species_in_core = False if spec not in self.edge.species: - self.add_species_to_edge(spec) + self.add_species_to_edge(spec, requires_rms=requires_rms) for spec in rxn.products: if spec not in self.core.species: all_species_in_core = False if spec not in self.edge.species: - self.add_species_to_edge(spec) + self.add_species_to_edge(spec, requires_rms=requires_rms) isomer_atoms = sum([len(spec.molecule[0].atoms) for spec in rxn.reactants]) @@ -854,9 +854,9 @@ def process_new_reactions(self, new_reactions, new_species, pdep_network=None, g # The reaction is not new, so it should already be in the core or edge continue if all_species_in_core: - self.add_reaction_to_core(rxn) + self.add_reaction_to_core(rxn, requires_rms=requires_rms) else: - self.add_reaction_to_edge(rxn) + self.add_reaction_to_edge(rxn, requires_rms=requires_rms) else: # Add the reaction to the appropriate unimolecular reaction network # If pdep_network is not None then that will be the network the @@ -1111,7 +1111,7 @@ def log_enlarge_summary( logging.info(" The model edge has {0:d} species and {1:d} reactions".format(edge_species_count, edge_reaction_count)) logging.info("") - def add_species_to_core(self, spec): + def add_species_to_core(self, spec, requires_rms=False): """ Add a species `spec` to the reaction model core (and remove from edge if necessary). This function also moves any reactions in the edge that gain @@ -1141,7 +1141,8 @@ def add_species_to_core(self, spec): rxn_list = [] if spec in self.edge.species: - self.edge.phase_system.pass_species(spec.label, self.core.phase_system) + if not NO_JULIA and requires_rms: + self.edge.phase_system.pass_species(spec.label, self.core.phase_system) # If species was in edge, remove it logging.debug("Removing species %s from edge.", spec) self.edge.species.remove(spec) @@ -1163,29 +1164,24 @@ def add_species_to_core(self, spec): for rxn in rxn_list: self.add_reaction_to_core(rxn) logging.debug("Moving reaction from edge to core: %s", rxn) - else: - if spec.molecule[0].contains_surface_site(): - self.core.phase_system.phases["Surface"].add_species(spec, edge_phase=self.edge.phase_system.phases["Surface"]) - self.edge.phase_system.species_dict[spec.label] = spec - self.core.phase_system.species_dict[spec.label] = spec - else: - self.core.phase_system.phases["Default"].add_species(spec, edge_phase=self.edge.phase_system.phases["Default"]) - self.edge.phase_system.species_dict[spec.label] = spec - self.core.phase_system.species_dict[spec.label] = spec + elif not NO_JULIA and requires_rms: + destination_phase = "Surface" if spec.molecule[0].contains_surface_site() else "Default" + self.core.phase_system.phases[destination_phase].add_species(spec, edge_phase=self.edge.phase_system.phases[destination_phase]) + self.edge.phase_system.species_dict[spec.label] = spec + self.core.phase_system.species_dict[spec.label] = spec return rxn_list - def add_species_to_edge(self, spec): + def add_species_to_edge(self, spec, requires_rms=False): """ - Add a species `spec` to the reaction model edge. + Add a species `spec` to the reaction model edge and optionally the RMS phase. """ self.edge.species.append(spec) - if spec.molecule[0].contains_surface_site(): - self.edge.phase_system.phases["Surface"].add_species(spec) - self.edge.phase_system.species_dict[spec.label] = spec - else: - self.edge.phase_system.phases["Default"].add_species(spec) - self.edge.phase_system.species_dict[spec.label] = spec + if NO_JULIA or not requires_rms: + return + destination_phase = "Surface" if spec.molecule[0].contains_surface_site() else "Default" + self.edge.phase_system.phases[destination_phase].add_species(spec) + self.edge.phase_system.species_dict[spec.label] = spec def set_thermodynamic_filtering_parameters( self, Tmax, thermo_tol_keep_spc_in_edge, min_core_size_for_prune, maximum_edge_species, reaction_systems @@ -1302,7 +1298,7 @@ def remove_empty_pdep_networks(self): del self.network_dict[source] self.network_list.remove(network) - def prune(self, reaction_systems, tol_keep_in_edge, tol_move_to_core, maximum_edge_species, min_species_exist_iterations_for_prune): + def prune(self, reaction_systems, tol_keep_in_edge, tol_move_to_core, maximum_edge_species, min_species_exist_iterations_for_prune, requires_rms=False): """ Remove species from the model edge based on the simulation results from the list of `reaction_systems`. @@ -1382,7 +1378,7 @@ def prune(self, reaction_systems, tol_keep_in_edge, tol_move_to_core, maximum_ed for index, spec in species_to_prune[0:prune_due_to_rate_counter]: logging.info("Pruning species %s", spec) logging.debug(" %-56s %10.4e", spec, max_edge_species_rate_ratios[index]) - self.remove_species_from_edge(reaction_systems, spec) + self.remove_species_from_edge(reaction_systems, spec, requires_rms=requires_rms) if len(species_to_prune) - prune_due_to_rate_counter > 0: logging.info( "Pruning %d species to obtain an edge size of %d species", len(species_to_prune) - prune_due_to_rate_counter, maximum_edge_species @@ -1390,7 +1386,7 @@ def prune(self, reaction_systems, tol_keep_in_edge, tol_move_to_core, maximum_ed for index, spec in species_to_prune[prune_due_to_rate_counter:]: logging.info("Pruning species %s", spec) logging.debug(" %-56s %10.4e", spec, max_edge_species_rate_ratios[index]) - self.remove_species_from_edge(reaction_systems, spec) + self.remove_species_from_edge(reaction_systems, spec, requires_rms=requires_rms) # Delete any networks that became empty as a result of pruning if self.pressure_dependence: @@ -1398,7 +1394,7 @@ def prune(self, reaction_systems, tol_keep_in_edge, tol_move_to_core, maximum_ed logging.info("") - def remove_species_from_edge(self, reaction_systems, spec): + def remove_species_from_edge(self, reaction_systems, spec, requires_rms=False): """ Remove species `spec` from the reaction model edge. """ @@ -1410,7 +1406,7 @@ def remove_species_from_edge(self, reaction_systems, spec): # clean up species references in reaction_systems for reaction_system in reaction_systems: - if not isinstance(reaction_system, Reactor): + if NO_JULIA or not requires_rms or not isinstance(reaction_system, Reactor): try: reaction_system.species_index.pop(spec) except KeyError: @@ -1482,7 +1478,7 @@ def remove_species_from_edge(self, reaction_systems, spec): self.species_cache.remove(spec) self.species_cache.append(None) - def add_reaction_to_core(self, rxn): + def add_reaction_to_core(self, rxn, requires_rms=False): """ Add a reaction `rxn` to the reaction model core (and remove from edge if necessary). This function assumes `rxn` has already been checked to @@ -1491,7 +1487,8 @@ def add_reaction_to_core(self, rxn): """ if rxn not in self.core.reactions: self.core.reactions.append(rxn) - if rxn not in self.edge.reactions: + + if not NO_JULIA and requires_rms and rxn not in self.edge.reactions: # If a reaction is not in edge but is going to add to core, it is either a seed mechanism or a newly generated reaction where all reactants and products are already in core # If the reaction is in edge, then the corresponding rms_rxn was moved from edge phase to core phase in pass_species already. rms_species_list = self.core.phase_system.get_rms_species_list() @@ -1508,7 +1505,7 @@ def add_reaction_to_core(self, rxn): if rxn in self.edge.reactions: self.edge.reactions.remove(rxn) - def add_reaction_to_edge(self, rxn): + def add_reaction_to_edge(self, rxn, requires_rms=False): """ Add a reaction `rxn` to the reaction model edge. This function assumes `rxn` has already been checked to ensure it is supposed to be an edge @@ -1517,6 +1514,8 @@ def add_reaction_to_edge(self, rxn): edge). """ self.edge.reactions.append(rxn) + if NO_JULIA or not requires_rms: + return rms_species_list = self.edge.phase_system.get_rms_species_list() species_names = self.edge.phase_system.get_species_names() bits = np.array([spc.molecule[0].contains_surface_site() for spc in rxn.reactants + rxn.products]) @@ -1573,7 +1572,7 @@ def get_stoichiometry_matrix(self): stoichiometry[i, j] = nu return stoichiometry.tocsr() - def add_seed_mechanism_to_core(self, seed_mechanism, react=False): + def add_seed_mechanism_to_core(self, seed_mechanism, react=False, requires_rms=False): """ Add all species and reactions from `seed_mechanism`, a :class:`KineticsPrimaryDatabase` object, to the model core. If `react` @@ -1680,7 +1679,7 @@ def add_seed_mechanism_to_core(self, seed_mechanism, react=False): spec.get_liquid_volumetric_mass_transfer_coefficient_data() spec.get_henry_law_constant_data() - self.add_species_to_core(spec) + self.add_species_to_core(spec, requires_rms=requires_rms) for rxn in self.new_reaction_list: if self.pressure_dependence and rxn.is_unimolecular(): @@ -1703,7 +1702,7 @@ def add_seed_mechanism_to_core(self, seed_mechanism, react=False): new_edge_reactions=[], ) - def add_reaction_library_to_edge(self, reaction_library): + def add_reaction_library_to_edge(self, reaction_library, requires_rms=False): """ Add all species and reactions from `reaction_library`, a :class:`KineticsPrimaryDatabase` object, to the model edge. @@ -1802,7 +1801,7 @@ def add_reaction_library_to_edge(self, reaction_library): spec.get_liquid_volumetric_mass_transfer_coefficient_data() spec.get_henry_law_constant_data() - self.add_species_to_edge(spec) + self.add_species_to_edge(spec, requires_rms=requires_rms) for rxn in self.new_reaction_list: if not ( @@ -1817,7 +1816,7 @@ def add_reaction_library_to_edge(self, reaction_library): ) ): # Don't add to the edge library reactions that were already processed - self.add_reaction_to_edge(rxn) + self.add_reaction_to_edge(rxn, requires_rms=requires_rms) if self.save_edge_species: from rmgpy.chemkin import mark_duplicate_reaction diff --git a/rmgpy/rmg/reactors.py b/rmgpy/rmg/reactionmechanismsimulator_reactors.py similarity index 99% rename from rmgpy/rmg/reactors.py rename to rmgpy/rmg/reactionmechanismsimulator_reactors.py index cc30b86116..d778aabca2 100644 --- a/rmgpy/rmg/reactors.py +++ b/rmgpy/rmg/reactionmechanismsimulator_reactors.py @@ -35,26 +35,6 @@ import logging import itertools -if __debug__: - try: - from os.path import dirname, abspath, join, exists - - path_rms = dirname(dirname(dirname(abspath(__file__)))) - from julia.api import Julia - - jl = Julia(sysimage=join(path_rms, "rms.so")) if exists(join(path_rms, "rms.so")) else Julia(compiled_modules=False) - from pyrms import rms - from diffeqpy import de - from julia import Main - except Exception as e: - import warnings - - warnings.warn("Unable to import Julia dependencies, original error: " + str(e), RuntimeWarning) -else: - from pyrms import rms - from diffeqpy import de - from julia import Main - from rmgpy.species import Species from rmgpy.reaction import Reaction from rmgpy.thermo.nasa import NASAPolynomial, NASA @@ -70,6 +50,20 @@ from rmgpy.data.kinetics.family import TemplateReaction from rmgpy.data.kinetics.depository import DepositoryReaction +NO_JULIA = False +try: + if __debug__: + from os.path import dirname, abspath, join, exists + from julia.api import Julia + path_rms = dirname(dirname(dirname(abspath(__file__)))) + jl = Julia(sysimage=join(path_rms, "rms.so")) if exists(join(path_rms, "rms.so")) else Julia(compiled_modules=False) + from pyrms import rms + from diffeqpy import de + from julia import Main +except Exception as e: + logging.info("Unable to import Julia dependencies, original error: " + str(e) + ". RMS features will not be available on this execution.") + NO_JULIA = True + class PhaseSystem: """ From 3ff4ab561e11859784760a32b9d8effc02a93aae Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Fri, 29 Sep 2023 15:49:01 -0400 Subject: [PATCH 06/17] add back the optional RMS install to the CI and the Dockerfile will also need to add to the docs for the installation instructions for installation using conda binaries and installation from source --- .github/workflows/CI.yml | 6 ++++++ Dockerfile | 2 ++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fa18af19c9..2c06375867 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -122,6 +122,12 @@ jobs: activate-environment: rmg_env use-mamba: true + # installs the extra RMS conda dependencies + - name: Add RMS dependencies + run: | + conda install -c conda-forge julia>=1.8.5,!=1.9.0 pyjulia>=0.6 + conda install -c rmg numdifftools pyrms diffeqpy + # list the environment for debugging purposes - name: mamba info run: | diff --git a/Dockerfile b/Dockerfile index 803374c02a..149e8b89dd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -45,6 +45,8 @@ RUN git clone --single-branch --branch main --depth 1 https://github.com/Reactio # build the conda environment WORKDIR /rmg/RMG-Py RUN conda env create --file environment.yml && \ + conda install -c conda-forge julia>=1.8.5,!=1.9.0 pyjulia>=0.6 && \ + conda install -c rmg numdifftools pyrms diffeqpy && \ conda clean --all --yes # This runs all subsequent commands inside the rmg_env conda environment From a02dd48fce41f7aa440be8f7b858bcf60e9e9a58 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Mon, 2 Oct 2023 09:30:53 -0400 Subject: [PATCH 07/17] bury make new species RMS-dependent check --- rmgpy/rmg/model.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index 326ca1657b..1ad68a4077 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -349,9 +349,10 @@ def make_new_species(self, object, label="", reactive=True, check_existing=True, orilabel = spec.label label = orilabel i = 2 - while any([label in phase.names for phase in self.edge.phase_system.phases.values()]): - label = orilabel + "-" + str(i) - i += 1 + if self.edge.phase_system: # None when RMS not installed + while any([label in phase.names for phase in self.edge.phase_system.phases.values()]): + label = orilabel + "-" + str(i) + i += 1 spec.label = label logging.debug("Creating new species %s", spec.label) From aff20385fbfcd9bf2e5d182b086f6f3ed9e709c1 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Mon, 2 Oct 2023 11:06:40 -0400 Subject: [PATCH 08/17] skip registering rms listener when not needed, more rms use patches missed some rms-dependent calls in previous commits --- rmgpy/rmg/main.py | 13 +++++++------ rmgpy/rmg/model.py | 3 ++- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index 3ff5c6c814..0eca00b9f1 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -512,7 +512,7 @@ def initialize(self, **kwargs): # if RMS is not installed but the user attempted to use it, the load_input_file would have failed # if RMS is installed but they did not use it, we can avoid extra work # if RMS is not installed and they did not use it, we avoid calling certain functions that would raise an error - requires_rms = any(isinstance(RMSReactor, self.reaction_systems)) + requires_rms = any(isinstance(reactor_system, RMSReactor) for reactor_system in self.reaction_systems) if kwargs.get("restart", ""): import rmgpy.rmg.input @@ -721,14 +721,15 @@ def initialize(self, **kwargs): self.initialize_seed_mech() return requires_rms - def register_listeners(self): + def register_listeners(self, requires_rms=False): """ Attaches listener classes depending on the options found in the RMG input file. """ self.attach(ChemkinWriter(self.output_directory)) - self.attach(RMSWriter(self.output_directory)) + if not NO_JULIA and requires_rms: + self.attach(RMSWriter(self.output_directory)) if self.generate_output_html: self.attach(OutputHTMLWriter(self.output_directory)) @@ -740,7 +741,7 @@ def register_listeners(self): if self.save_simulation_profiles: for index, reaction_system in enumerate(self.reaction_systems): - if isinstance(reaction_system, Reactor): + if not NO_JULIA and requires_rms and isinstance(reaction_system, RMSReactor): typ = type(reaction_system) raise InputError(f"save_simulation_profiles=True not compatible with reactor of type {typ}") reaction_system.attach(SimulationProfileWriter(self.output_directory, index, self.reaction_model.core.species)) @@ -757,7 +758,7 @@ def execute(self, initialize=True, **kwargs): requires_rms = self.initialize(**kwargs) # register listeners - self.register_listeners() + self.register_listeners(requires_rms=requires_rms) self.done = False @@ -2225,7 +2226,7 @@ def __init__(self, reaction_system, bspc): if isinstance(value, list): self.Ranges[key] = [v.value_si for v in value] - if isinstance(reaction_system, Reactor): + if not NO_JULIA and isinstance(reaction_system, RMSReactor): self.tmax = reaction_system.tf else: for term in reaction_system.termination: diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index 1ad68a4077..1a27059f47 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -1403,7 +1403,8 @@ def remove_species_from_edge(self, reaction_systems, spec, requires_rms=False): # remove the species self.edge.species.remove(spec) self.index_species_dict.pop(spec.index) - self.edge.phase_system.remove_species(spec) + if not NO_JULIA and requires_rms: + self.edge.phase_system.remove_species(spec) # clean up species references in reaction_systems for reaction_system in reaction_systems: From e8c776a12bec744275b3d95c8f86ed19429b797c Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Mon, 2 Oct 2023 11:22:48 -0400 Subject: [PATCH 09/17] incorrect capitalization in Arkane test --- .conda/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.conda/meta.yaml b/.conda/meta.yaml index 09ca7ba811..407fb11f04 100644 --- a/.conda/meta.yaml +++ b/.conda/meta.yaml @@ -174,7 +174,7 @@ test: - arkane commands: - rmg.py examples/rmg/superminimal/input.py - - arkane.py examples/arkane/networks/n-butanol + - Arkane.py examples/arkane/networks/n-butanol about: home: https://github.com/ReactionMechanismGenerator/RMG-Py From d93ed0072f3d436ffbe5d7803913eaf77d831181 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Mon, 2 Oct 2023 11:34:57 -0400 Subject: [PATCH 10/17] another syntax error in arkane testing call --- .conda/meta.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.conda/meta.yaml b/.conda/meta.yaml index 407fb11f04..cb54c8beda 100644 --- a/.conda/meta.yaml +++ b/.conda/meta.yaml @@ -174,7 +174,7 @@ test: - arkane commands: - rmg.py examples/rmg/superminimal/input.py - - Arkane.py examples/arkane/networks/n-butanol + - Arkane.py examples/arkane/networks/n-butanol/input.py about: home: https://github.com/ReactionMechanismGenerator/RMG-Py From 6e011ffcc248d424b9ba098fe161dad8f18d65be Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Mon, 2 Oct 2023 17:36:45 -0400 Subject: [PATCH 11/17] override channels in CI install of RMS to ensure we get right channel --- .github/workflows/CI.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 2c06375867..12539a716b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -125,8 +125,8 @@ jobs: # installs the extra RMS conda dependencies - name: Add RMS dependencies run: | - conda install -c conda-forge julia>=1.8.5,!=1.9.0 pyjulia>=0.6 - conda install -c rmg numdifftools pyrms diffeqpy + conda install -c conda-forge --override-channels julia>=1.8.5,!=1.9.0 pyjulia>=0.6 + conda install -c rmg --override-channels numdifftools pyrms diffeqpy # list the environment for debugging purposes - name: mamba info From 64dd40715294ac2071422a2fcb9c41f66d96568d Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Mon, 2 Oct 2023 17:36:56 -0400 Subject: [PATCH 12/17] can we build docs without installing RMS? --- .github/workflows/docs.yml | 5 ----- documentation/Makefile | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 82c694d42b..2a38d443c9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -31,11 +31,6 @@ jobs: run: | mamba info mamba list - - name: Install and link Julia dependencies - run: | - julia -e "using Pkg; Pkg.add(PackageSpec(url=\"https://github.com/ReactionMechanismGenerator/ReactionMechanismSimulator.jl\", rev=\"main\"))" - julia -e "using Pkg; Pkg.add(\"PyCall\"); Pkg.add(\"DifferentialEquations\")" - python -c "import julia; julia.install()" - name: Install and compile RMG run: | cd .. diff --git a/documentation/Makefile b/documentation/Makefile index fb901249e9..cac86744cf 100644 --- a/documentation/Makefile +++ b/documentation/Makefile @@ -3,7 +3,7 @@ # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = python-jl $$(which sphinx-build) +SPHINXBUILD = python $$(which sphinx-build) PAPER = BUILDDIR = build From 80cf45718006333fb699b6283ac0a3baea588696 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 3 Oct 2023 08:59:08 -0400 Subject: [PATCH 13/17] add nomkl back to environment --- environment.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index 5080ac2063..be687f85b2 100644 --- a/environment.yml +++ b/environment.yml @@ -98,7 +98,8 @@ dependencies: # We should use the official channel, not sure how difficult this # change will be. - - liblapack =*=*mkl +# conda mutex metapackage + - nomkl # additional packages that are required, but not specified here (and why) # pydqed, pydas, mopac, and likely others require a fortran compiler (specifically gfortran) From f4b67918b425544a2e1e3ffb9db80f738dff435f Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 3 Oct 2023 12:13:29 -0400 Subject: [PATCH 14/17] squash --- rmgpy/rmg/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index 1a27059f47..033a049c3e 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -1163,7 +1163,7 @@ def add_species_to_core(self, spec, requires_rms=False): # Move any identified reactions to the core for rxn in rxn_list: - self.add_reaction_to_core(rxn) + self.add_reaction_to_core(rxn, requires_rms=requires_rms) logging.debug("Moving reaction from edge to core: %s", rxn) elif not NO_JULIA and requires_rms: destination_phase = "Surface" if spec.molecule[0].contains_surface_site() else "Default" From 292718fe725520a7c764f202ff456dbf4f3bdec7 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 3 Oct 2023 12:15:36 -0400 Subject: [PATCH 15/17] import Reactor as RMSReactor inside Model for clarity --- rmgpy/rmg/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index 033a049c3e..d8d0e1facb 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -57,7 +57,8 @@ from rmgpy.species import Species from rmgpy.thermo.thermoengine import submit from rmgpy.rmg.decay import decay_species -from rmgpy.rmg.reactionmechanismsimulator_reactors import PhaseSystem, Phase, Interface, Reactor, NO_JULIA +from rmgpy.rmg.reactionmechanismsimulator_reactors import PhaseSystem, Phase, Interface, NO_JULIA +from rmgpy.rmg.reactionmechanismsimulator_reactors import Reactor as RMSReactor from rmgpy.molecule.fragment import Fragment ################################################################################ @@ -1408,7 +1409,7 @@ def remove_species_from_edge(self, reaction_systems, spec, requires_rms=False): # clean up species references in reaction_systems for reaction_system in reaction_systems: - if NO_JULIA or not requires_rms or not isinstance(reaction_system, Reactor): + if NO_JULIA or not requires_rms or not isinstance(reaction_system, RMSReactor): try: reaction_system.species_index.pop(spec) except KeyError: From 34796db0d5b37f729b932a90b22ca54e61d32879 Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 3 Oct 2023 18:09:20 -0400 Subject: [PATCH 16/17] missed some requires_rms in enlarge() --- rmgpy/rmg/main.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index 0eca00b9f1..7226614857 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -701,10 +701,10 @@ def initialize(self, **kwargs): # This is necessary so that the PDep algorithm can identify the bath gas for spec in self.initial_species: if not spec.reactive: - self.reaction_model.enlarge(spec) + self.reaction_model.enlarge(spec, requires_rms=requires_rms) for spec in self.initial_species: if spec.reactive: - self.reaction_model.enlarge(spec) + self.reaction_model.enlarge(spec, requires_rms=requires_rms) # chatelak: store constant SPC indices in the reactor attributes if any constant SPC provided in the input file # advantages to write it here: this is run only once (as species indexes does not change over the generation) @@ -715,7 +715,7 @@ def initialize(self, **kwargs): self.initialize_reaction_threshold_and_react_flags() if self.filter_reactions and self.init_react_tuples: - self.react_init_tuples() + self.react_init_tuples(requires_rms=requires_rms) self.reaction_model.initialize_index_species_dict() self.initialize_seed_mech() @@ -828,6 +828,7 @@ def execute(self, initialize=True, **kwargs): unimolecular_react=self.unimolecular_react, bimolecular_react=self.bimolecular_react, trimolecular_react=self.trimolecular_react, + requires_rms=requires_rms, ) if not np.isinf(self.model_settings_list[0].thermo_tol_keep_spc_in_edge): @@ -1899,7 +1900,7 @@ def initialize_reaction_threshold_and_react_flags(self): if self.trimolecular: self.trimolecular_react[:num_restart_spcs, :num_restart_spcs, :num_restart_spcs] = False - def react_init_tuples(self): + def react_init_tuples(self, requires_rms=False): """ Reacts tuples given in the react block """ @@ -1932,6 +1933,7 @@ def react_init_tuples(self): unimolecular_react=self.unimolecular_react, bimolecular_react=self.bimolecular_react, trimolecular_react=self.trimolecular_react, + requires_rms=requires_rms, ) def update_reaction_threshold_and_react_flags( From 7de8a3a7ba2552b0dff7263b09f8b785c3bddffa Mon Sep 17 00:00:00 2001 From: Jackson Burns Date: Tue, 3 Oct 2023 21:57:54 -0400 Subject: [PATCH 17/17] add requires_rms to pdep, thermo filtering, and process_new_rxns --- rmgpy/rmg/main.py | 4 ++-- rmgpy/rmg/model.py | 28 ++++++++++++++-------------- rmgpy/rmg/pdep.py | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index 7226614857..6fe384fbb6 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -841,7 +841,7 @@ def execute(self, initialize=True, **kwargs): ) if not np.isinf(self.model_settings_list[0].thermo_tol_keep_spc_in_edge): - self.reaction_model.thermo_filter_down(maximum_edge_species=self.model_settings_list[0].maximum_edge_species) + self.reaction_model.thermo_filter_down(maximum_edge_species=self.model_settings_list[0].maximum_edge_species, requires_rms=requires_rms) logging.info("Completed initial enlarge edge step.\n") @@ -1108,7 +1108,7 @@ def execute(self, initialize=True, **kwargs): reactor_done = False if not np.isinf(self.model_settings_list[0].thermo_tol_keep_spc_in_edge): - self.reaction_model.thermo_filter_down(maximum_edge_species=model_settings.maximum_edge_species) + self.reaction_model.thermo_filter_down(maximum_edge_species=model_settings.maximum_edge_species, requires_rms=requires_rms) max_num_spcs_hit = len(self.reaction_model.core.species) >= model_settings.max_num_species diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index d8d0e1facb..e7a1dc2bad 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -643,7 +643,7 @@ def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bi pdep_network, new_species = new_object new_reactions.extend(pdep_network.explore_isomer(new_species)) - self.process_new_reactions(new_reactions, new_species, pdep_network) + self.process_new_reactions(new_reactions, new_species, pdep_network, requires_rms=requires_rms) else: raise TypeError( @@ -667,7 +667,7 @@ def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bi if len(products) == 1 and products[0] == species: new_reactions = network.explore_isomer(species) - self.process_new_reactions(new_reactions, species, network) + self.process_new_reactions(new_reactions, species, network, requires_rms=requires_rms) network.update_configurations(self) index = 0 break @@ -692,7 +692,7 @@ def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bi # Identify a core species which was used to generate the reaction # This is only used to determine the reaction direction for processing spc = spcTuple[0] - self.process_new_reactions(rxnList, spc) + self.process_new_reactions(rxnList, spc, requires_rms=requires_rms) ################################################################ # Begin processing the new species and reactions @@ -704,7 +704,7 @@ def enlarge(self, new_object=None, react_edge=False, unimolecular_react=None, bi # Do thermodynamic filtering if not np.isinf(self.thermo_tol_keep_spc_in_edge) and self.new_species_list != []: - self.thermo_filter_species(self.new_species_list) + self.thermo_filter_species(self.new_species_list, requires_rms=requires_rms) # Update unimolecular (pressure dependent) reaction networks if self.pressure_dependence: @@ -1131,7 +1131,7 @@ def add_species_to_core(self, spec, requires_rms=False): if spec in self.edge.species: # remove forbidden species from edge logging.info("Species {0} was Forbidden and not added to Core...Removing from Edge.".format(spec)) - self.remove_species_from_edge(self.reaction_systems, spec) + self.remove_species_from_edge(self.reaction_systems, spec, requires_rms=requires_rms) # remove any empty pdep networks as a result of species removal if self.pressure_dependence: self.remove_empty_pdep_networks() @@ -1207,7 +1207,7 @@ def set_thermodynamic_filtering_parameters( self.reaction_systems = reaction_systems self.maximum_edge_species = maximum_edge_species - def thermo_filter_species(self, spcs): + def thermo_filter_species(self, spcs, requires_rms=False): """ checks Gibbs energy of the species in species against the maximum allowed Gibbs energy @@ -1222,13 +1222,13 @@ def thermo_filter_species(self, spcs): "greater than the thermo_tol_keep_spc_in_edge of " "{3} ".format(spc, G, Gn, self.thermo_tol_keep_spc_in_edge) ) - self.remove_species_from_edge(self.reaction_systems, spc) + self.remove_species_from_edge(self.reaction_systems, spc, requires_rms=requires_rms) # Delete any networks that became empty as a result of pruning if self.pressure_dependence: self.remove_empty_pdep_networks() - def thermo_filter_down(self, maximum_edge_species, min_species_exist_iterations_for_prune=0): + def thermo_filter_down(self, maximum_edge_species, min_species_exist_iterations_for_prune=0, requires_rms=False): """ removes species from the edge based on their Gibbs energy until maximum_edge_species is reached under the constraint that all removed species are older than @@ -1270,7 +1270,7 @@ def thermo_filter_down(self, maximum_edge_species, min_species_exist_iterations_ logging.info( "Removing species {0} from edge to meet maximum number of edge species, Gibbs " "number is {1}".format(spc, Gns[rInds[i]]) ) - self.remove_species_from_edge(self.reaction_systems, spc) + self.remove_species_from_edge(self.reaction_systems, spc, requires_rms=requires_rms) # Delete any networks that became empty as a result of pruning if self.pressure_dependence: @@ -1643,9 +1643,9 @@ def add_seed_mechanism_to_core(self, seed_mechanism, react=False, requires_rms=F # This unimolecular library reaction is flagged as `elementary_high_p` and has Arrhenius type kinetics. # We should calculate a pressure-dependent rate for it if len(rxn.reactants) == 1: - self.process_new_reactions(new_reactions=[rxn], new_species=rxn.reactants[0]) + self.process_new_reactions(new_reactions=[rxn], new_species=rxn.reactants[0], requires_rms=requires_rms) else: - self.process_new_reactions(new_reactions=[rxn], new_species=rxn.products[0]) + self.process_new_reactions(new_reactions=[rxn], new_species=rxn.products[0], requires_rms=requires_rms) # Perform species constraints and forbidden species checks @@ -1693,7 +1693,7 @@ def add_seed_mechanism_to_core(self, seed_mechanism, react=False, requires_rms=F submit(spec, self.solvent_name) rxn.fix_barrier_height(force_positive=True) - self.add_reaction_to_core(rxn) + self.add_reaction_to_core(rxn, requires_rms=requires_rms) # Check we didn't introduce unmarked duplicates self.mark_chemkin_duplicates() @@ -1765,9 +1765,9 @@ def add_reaction_library_to_edge(self, reaction_library, requires_rms=False): # This unimolecular library reaction is flagged as `elementary_high_p` and has Arrhenius type kinetics. # We should calculate a pressure-dependent rate for it if len(rxn.reactants) == 1: - self.process_new_reactions(new_reactions=[rxn], new_species=rxn.reactants[0]) + self.process_new_reactions(new_reactions=[rxn], new_species=rxn.reactants[0], requires_rms=requires_rms) else: - self.process_new_reactions(new_reactions=[rxn], new_species=rxn.products[0]) + self.process_new_reactions(new_reactions=[rxn], new_species=rxn.products[0], requires_rms=requires_rms) # Perform species constraints and forbidden species checks for spec in self.new_species_list: diff --git a/rmgpy/rmg/pdep.py b/rmgpy/rmg/pdep.py index 0e1398e51a..89b4104d2b 100644 --- a/rmgpy/rmg/pdep.py +++ b/rmgpy/rmg/pdep.py @@ -917,7 +917,7 @@ def update(self, reaction_model, pdep_settings): f'from the {rxn.library} library, and was not added to the model') break else: - reaction_model.add_reaction_to_core(net_reaction) + reaction_model.add_reaction_to_core(net_reaction, requires_rms=True) else: # Check whether netReaction already exists in the edge as a LibraryReaction for rxn in reaction_model.edge.reactions: @@ -929,7 +929,7 @@ def update(self, reaction_model, pdep_settings): f'from the {rxn.library} library, and was not added to the model') break else: - reaction_model.add_reaction_to_edge(net_reaction) + reaction_model.add_reaction_to_edge(net_reaction, requires_rms=True) # Set/update the net reaction kinetics using interpolation model kdata = K[:, :, i, j].copy()