From a7730a63b4e45547c1d90076a9dc247f6fc8cb21 Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Sat, 25 Jan 2025 16:38:25 -0500 Subject: [PATCH 1/3] fix `juliacall` incompatibilities; see extended - CI updates: run CI on all platforms with and without RMS, split regression tests into separate job after that so that regression is only checked on one platform - installation updates: make RMG-Py actually `pip`-installable as `reactionmechanismgenerator` to avoid having to set the `PYTHONPATH` variable, which breaks `juliacall`, add a convenience script for installing RMS - code changes: fix some small bugs in the new optional-rms setup (one missed `requires_rms` and one incorrect rebase) --- .github/workflows/CI.yml | 143 +++++++---- .github/workflows/pure_python_ci.yml | 93 ------- .gitignore | 5 + Makefile | 23 +- install_rms.sh | 31 +++ rmg.py | 10 - rmgpy/rmg/main.py | 5 +- .../reactionmechanismsimulator_reactors.py | 47 +--- setup.py | 230 +++++------------- test/conftest.py | 7 + utilities.py | 32 +-- 11 files changed, 230 insertions(+), 396 deletions(-) delete mode 100644 .github/workflows/pure_python_ci.yml create mode 100644 install_rms.sh create mode 100644 test/conftest.py diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 496b9834f1..ce533a0663 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -49,7 +49,6 @@ env: # main with the name of the branch RMG_DATABASE_BRANCH: main - jobs: build-and-test: strategy: @@ -57,13 +56,16 @@ jobs: matrix: python-version: ["3.9"] os: [macos-13, macos-latest, ubuntu-latest] + include-rms: ["", "with RMS"] + exclude: + - os: macos-latest # needs Cantera 3 b/c x86 emulation breaks with Julia + include-rms: 'with RMS' + - os: macos-13 # GitHub's runners just aren't up to the task of installing Julia + include-rms: 'with RMS' runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} Build and Test Python ${{ matrix.python-version }} + name: Python ${{ matrix.python-version }} ${{ matrix.os }} Build and Test ${{ matrix.include-rms }} # skip scheduled runs from forks if: ${{ !( github.repository != 'ReactionMechanismGenerator/RMG-Py' && github.event_name == 'schedule' ) }} - env: - # This is true only if this is a reference case for the regression testing: - REFERENCE_JOB: ${{ github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py' }} defaults: run: shell: bash -l {0} @@ -83,15 +85,16 @@ jobs: miniforge-version: latest python-version: ${{ matrix.python-version }} activate-environment: rmg_env - use-mamba: true + auto-update-conda: true show-channel-urls: true channels: conda-forge,cantera,rmg + conda-remove-defaults: "true" # list the environment for debugging purposes - - name: mamba info + - name: conda info run: | - mamba info - mamba list + conda info + conda list # Clone RMG-database - name: Clone RMG-database @@ -99,57 +102,111 @@ jobs: cd .. git clone -b $RMG_DATABASE_BRANCH https://github.com/ReactionMechanismGenerator/RMG-database.git - # modify env variables as directed in the RMG installation instructions - - name: Set Environment Variables - run: | - RUNNER_CWD=$(pwd) - echo "PYTHONPATH=$RUNNER_CWD/RMG-Py:$PYTHONPATH" >> $GITHUB_ENV - echo "$RUNNER_CWD/RMG-Py" >> $GITHUB_PATH - # RMG build step - - name: make RMG - run: | - make clean - make - - # Setup Juliaup - - name: Set Julia paths - run: | - # echo "JULIAUP_DEPOT_PATH=$CONDA/envs/rmg_env/.julia" >> $GITHUB_ENV - # echo "JULIAUP_DEPOT_PATH=$CONDA/envs/rmg_env/.julia" >> $GITHUB_PATH - # echo "JULIA_DEPOT_PATH=$CONDA/envs/rmg_env/.julia" >> $GITHUB_ENV - # echo "JULIA_DEPOT_PATH=$CONDA/envs/rmg_env/.julia" >> $GITHUB_PATH - # echo "JULIA_CONDAPKG_EXE=$CONDA/condabin/mamba" >> $GITHUB_ENV - # echo "JULIA_CONDAPKG_EXE=$CONDA/condabin/mamba" >> $GITHUB_PATH - # echo "JULIA_CONDAPKG_EXE=$CONDA/condabin/mamba" >> $GITHUB_ENV - # echo "JULIA_CONDAPKG_EXE=$CONDA/condabin/mamba" >> $GITHUB_PATH - echo "JULIA_CONDAPKG_BACKEND=Current" >> $GITHUB_ENV - # echo "JULIA_CONDAPKG_BACKEND=Current" >> $GITHUB_PATH + - run: make install - name: Setup Juliaup + if: matrix.include-rms == 'with RMS' uses: julia-actions/install-juliaup@v2 with: channel: '1.9' - - name: Check Julia version - run: julia --version + - name: Set some env vars + if: matrix.include-rms == 'with RMS' + run: | + # https://juliapy.github.io/PythonCall.jl/stable/pythoncall/#If-you-already-have-Python-and-required-Python-packages-installed + echo "JULIA_CONDAPKG_BACKEND=Null" >> $GITHUB_ENV + echo "JULIA_PYTHONCALL_EXE=$CONDA_PREFIX/bin/python" >> $GITHUB_ENV # RMS installation and linking to Julia - name: Install and link Julia dependencies + if: matrix.include-rms == 'with RMS' timeout-minutes: 120 # this usually takes 20-45 minutes (or hangs for 6+ hours). - # JULIA_CONDAPKG_EXE points to the existing conda/mamba to avoid JuliaCall from installing their own. See https://juliapy.github.io/PythonCall.jl/stable/pythoncall/#If-you-already-have-a-Conda-environment. + run: . install_rms.sh + + - name: Set some other env vars + if: matrix.include-rms == 'with RMS' run: | - mamba install conda-forge::pyjuliacall - julia -e 'using Pkg; Pkg.add(Pkg.PackageSpec(name="ReactionMechanismSimulator", url="https://github.com/hwpang/ReactionMechanismSimulator.jl.git", rev="fix_installation")); using ReactionMechanismSimulator' + # ensure that juliacall in Python uses the correct julia executable and packages: https://github.com/JuliaPy/PyJuliaPkg?tab=readme-ov-file#which-julia-gets-used + echo "PYTHON_JULIAPKG_EXE=$(which julia)" >> $GITHUB_ENV + echo "PYTHON_JULIAPKG_PROJECT=$HOME/.julia/packages" >> $GITHUB_ENV - name: Install Q2DTor run: echo "" | make q2dtor # non-regression testing - name: Run Unit, Functional, and Database Tests - # aggregate into one command so we only have to eat the collection time once run: make test-all + regression-test: + needs: build-and-test + runs-on: ubuntu-latest + name: Regression Test + # skip scheduled runs from forks + if: ${{ !( github.repository != 'ReactionMechanismGenerator/RMG-Py' && github.event_name == 'schedule' ) }} + env: + # This is true only if this is a reference case for the regression testing: + REFERENCE_JOB: ${{ github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py' }} + defaults: + run: + shell: bash -l {0} + steps: + - name: Checkout RMG-Py + uses: actions/checkout@v4 + + - name: Setup Miniforge Python 3.9 + uses: conda-incubator/setup-miniconda@v3 + with: + environment-file: environment.yml + miniforge-variant: Miniforge3 + miniforge-version: latest + python-version: 3.9 + activate-environment: rmg_env + auto-update-conda: true + show-channel-urls: true + channels: conda-forge,cantera,rmg + conda-remove-defaults: "true" + + # list the environment for debugging purposes + - name: conda info + run: | + conda info + conda list + + # Clone RMG-database + - name: Clone RMG-database + run: | + cd .. + git clone -b $RMG_DATABASE_BRANCH https://github.com/ReactionMechanismGenerator/RMG-database.git + + # RMG build step + - run: make install + + - name: Setup Juliaup + uses: julia-actions/install-juliaup@v2 + with: + channel: '1.9' + + - name: Set some env vars + run: | + # https://juliapy.github.io/PythonCall.jl/stable/pythoncall/#If-you-already-have-Python-and-required-Python-packages-installed + echo "JULIA_CONDAPKG_BACKEND=Null" >> $GITHUB_ENV + echo "JULIA_PYTHONCALL_EXE=$CONDA_PREFIX/bin/python" >> $GITHUB_ENV + + # RMS installation and linking to Julia + - name: Install and link Julia dependencies + timeout-minutes: 120 # this usually takes 20-45 minutes (or hangs for 6+ hours). + run: . install_rms.sh + + - name: Set some other env vars + run: | + # ensure that juliacall in Python uses the correct julia executable and packages: https://github.com/JuliaPy/PyJuliaPkg?tab=readme-ov-file#which-julia-gets-used + echo "PYTHON_JULIAPKG_EXE=$(which julia)" >> $GITHUB_ENV + echo "PYTHON_JULIAPKG_PROJECT=$HOME/.julia/packages" >> $GITHUB_ENV + + - name: Install Q2DTor + run: echo "" | make q2dtor + # Regression Testing - Test Execution - name: Regression Tests - Execution id: regression-execution @@ -322,14 +379,14 @@ jobs: - name: Upload regression summary artifact # the annotate workflow uses this artifact to add a comment to the PR - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if : ${{ github.event_name == 'pull_request' }} with: name: regression_summary path: summary.txt - name: Upload Comparison Results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: regression_test_comparison_results path: | @@ -339,7 +396,7 @@ jobs: - name: Code coverage install and run if: success() || ( failure() && steps.regression-execution.conclusion == 'success' ) run: | - mamba install -y -c conda-forge codecov + conda install -y -c conda-forge codecov codecov build-and-push-docker: diff --git a/.github/workflows/pure_python_ci.yml b/.github/workflows/pure_python_ci.yml deleted file mode 100644 index 9cc0d88e10..0000000000 --- a/.github/workflows/pure_python_ci.yml +++ /dev/null @@ -1,93 +0,0 @@ -# Pure Python CI -# Runs a reduced set of tests compared to CI.yml, skips CD altogether, ensure pure-python -# mode (i.e. no Julia-based dependencies) works. - -name: Pure Python Continuous Integration - -on: - schedule: - # * is a special character in YAML so you have to quote this string - - cron: "0 8 * * *" - # allow running on RMG-Py on a pushed branch, only if triggered manually - workflow_dispatch: - # runs on PRs against RMG-Py (and anywhere else, but we add this for RMG-Py) - pull_request: - -# this prevents one PR from simultaneously running multiple runners, which will clog up the queue -# and prevent other PRs from running the CI -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -env: - # if running on RMG-Py but requiring changes on an un-merged branch of RMG-database, replace - # main with the name of the branch - RMG_DATABASE_BRANCH: main - - -jobs: - build-and-test: - strategy: - fail-fast: false - matrix: - python-version: ["3.9"] - os: [macos-13, macos-latest, ubuntu-latest] - runs-on: ${{ matrix.os }} - name: ${{ matrix.os }} Build and Minimal Test Python ${{ matrix.python-version }} - # skip scheduled runs from forks - if: ${{ !( github.repository != 'ReactionMechanismGenerator/RMG-Py' && github.event_name == 'schedule' ) }} - defaults: - run: - shell: bash -l {0} - steps: - - name: Checkout RMG-Py - uses: actions/checkout@v4 - - - name: Compel Arm-based Mac to use x86 - if: matrix.os == 'macos-latest' - run: echo "CONDA_SUBDIR=osx-64" >> $GITHUB_ENV - - - name: Setup Miniforge Python ${{ matrix.python-version }} - uses: conda-incubator/setup-miniconda@v3 - with: - environment-file: environment.yml - miniforge-variant: Miniforge3 - miniforge-version: latest - python-version: ${{ matrix.python-version }} - activate-environment: rmg_env - use-mamba: true - show-channel-urls: true - channels: conda-forge,cantera,rmg - - # list the environment for debugging purposes - - name: mamba info - run: | - mamba info - mamba list - - # Clone RMG-database - - name: Clone RMG-database - run: | - cd .. - git clone -b $RMG_DATABASE_BRANCH https://github.com/ReactionMechanismGenerator/RMG-database.git - - # modify env variables as directed in the RMG installation instructions - - name: Set Environment Variables - run: | - RUNNER_CWD=$(pwd) - echo "PYTHONPATH=$RUNNER_CWD/RMG-Py:$PYTHONPATH" >> $GITHUB_ENV - echo "$RUNNER_CWD/RMG-Py" >> $GITHUB_PATH - - # RMG build step - - name: make RMG - run: | - make clean - make - - - name: Install Q2DTor - run: echo "" | make q2dtor - - # non-regression testing - - name: Run Unit, Functional, and Database Tests - # aggregate into one command so we only have to eat the collection time once - run: make test-all diff --git a/.gitignore b/.gitignore index 38d73e2bc1..723a18e5cb 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,9 @@ # ################################################################################ +# build dir +reactionmechanismgenerator.egg-info/ + # MacOS files .DS_Store @@ -11,6 +14,8 @@ *.pyc *.so *.pyd +# and intermediate source files +*.c # Image files generated by RMG *.png diff --git a/Makefile b/Makefile index 06055daa3a..36c0f7b13d 100644 --- a/Makefile +++ b/Makefile @@ -7,28 +7,13 @@ CC=gcc CXX=g++ -.PHONY : all minimal main solver check pycheck arkane clean install decython documentation test q2dtor +.PHONY : all check clean install decython documentation test q2dtor -all: pycheck main solver check - -minimal: - python setup.py build_ext minimal --inplace --build-temp . - -main: - python setup.py build_ext main --inplace --build-temp . - -solver: - @ python utilities.py check-pydas - python setup.py build_ext solver --inplace --build-temp . - -arkane: - python setup.py build_ext arkane --inplace --build-temp . +all: check install check check: @ python utilities.py check-dependencies - -pycheck: - @ python utilities.py check-python + @ python utilities.py check-pydas documentation: $(MAKE) -C documentation html @@ -42,7 +27,7 @@ clean-solver: install: @ python utilities.py check-pydas - python setup.py install + python -m pip install -vv --no-cache-dir -e . q2dtor: @ echo -e "\nInstalling Q2DTor...\n" diff --git a/install_rms.sh b/install_rms.sh new file mode 100644 index 0000000000..57f495c6ab --- /dev/null +++ b/install_rms.sh @@ -0,0 +1,31 @@ +# install_rms.sh +# +# Convenience script to install ReactionMechanismSimulator inside your rmg_env conda environment +# +# Note that you will have to manually install juliaup before running this script: +# curl -fsSL https://install.julialang.org | sh +# # restart shell +# juliaup add 1.9 +# juliaup default 1.9 +# juliaup remove release + + +conda install -y conda-forge::pyjuliacall + +export JULIA_CONDAPKG_BACKEND=Null +export JULIA_PYTHONCALL_EXE=$CONDA_PREFIX/bin/python + +julia -e 'using Pkg; Pkg.add(Pkg.PackageSpec(name="ReactionMechanismSimulator", url="https://github.com/hwpang/ReactionMechanismSimulator.jl.git", rev="fix_installation")); using ReactionMechanismSimulator; Pkg.instantiate()' || echo "RMS install error - continuing anyway ¯\_(ツ)_/¯" + +export PYTHON_JULIAPKG_EXE=$(which julia) +export PYTHON_JULIAPKG_PROJECT=$HOME/.julia/packages + +echo "Copy the following text into your terminal profile (.bashrc, .zshrc, etc.): + +export JULIA_CONDAPKG_BACKEND=Null +export JULIA_PYTHONCALL_EXE=$CONDA_PREFIX/bin/python +export PYTHON_JULIAPKG_EXE=$(which julia) +export PYTHON_JULIAPKG_PROJECT=$HOME/.julia/packages + +or otherwise run these 4 commands when first opening a terminal that will run RMG requiring RMS. +""" diff --git a/rmg.py b/rmg.py index 0b27098b81..6127d761f8 100644 --- a/rmg.py +++ b/rmg.py @@ -34,16 +34,6 @@ import os.path import logging -# Before importing any RMG modules, check Python version -try: - import utilities -except ImportError: # This is likely the binary install version, which does not include utilities - # If this is in fact the binary version, conda has ensured that the python version is correct. - # It is okay to skip this test here - utilities = None -else: - utilities.check_python() - import rmgpy from rmgpy.rmg.main import RMG, initialize_log, process_profile_stats, make_profile_graph from rmgpy.util import parse_command_line_arguments diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index 3f4b574c77..1d9cb8d55f 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -514,8 +514,8 @@ def initialize(self, **kwargs): # 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 + # if RMS is installed but they did not use it, we can avoid extra work requires_rms = any(isinstance(reactor_system, RMSReactor) for reactor_system in self.reaction_systems) if self.surface_site_density: @@ -654,7 +654,7 @@ def initialize(self, **kwargs): if vapor_liquid_mass_transfer.enabled: spec.get_liquid_volumetric_mass_transfer_coefficient_data() spec.get_henry_law_constant_data() - self.reaction_model.add_species_to_edge(spec) + self.reaction_model.add_species_to_edge(spec, requires_rms=requires_rms) # Seed mechanisms: add species and reactions from seed mechanism # DON'T generate any more reactions for the seed species at this time @@ -795,6 +795,7 @@ def execute(self, initialize=True, **kwargs): ``initialize`` is a ``bool`` type flag used to determine whether to call self.initialize() """ + requires_rms=False if initialize: requires_rms = self.initialize(**kwargs) diff --git a/rmgpy/rmg/reactionmechanismsimulator_reactors.py b/rmgpy/rmg/reactionmechanismsimulator_reactors.py index f40ba703d6..77b0ffb8b0 100644 --- a/rmgpy/rmg/reactionmechanismsimulator_reactors.py +++ b/rmgpy/rmg/reactionmechanismsimulator_reactors.py @@ -32,32 +32,13 @@ """ import itertools import logging -import sys import numpy as np -import rmgpy.constants as constants - -NO_JULIA = True -try: - import juliacall - from juliacall import Main - Main.seval("using PythonCall") - Main.seval("using ReactionMechanismSimulator") - Main.seval("using ReactionMechanismSimulator.Sundials") - NO_JULIA = False -except: - logging.warning("Julia import failed, RMS reactors not available.") - -from rmgpy import constants -from rmgpy.data.kinetics.depository import DepositoryReaction -from rmgpy.data.kinetics.family import TemplateReaction from rmgpy.data.solvation import SolventData from rmgpy.kinetics.arrhenius import ( Arrhenius, - ArrheniusBM, ArrheniusChargeTransfer, - ArrheniusEP, Marcus, MultiArrhenius, MultiPDepArrhenius, @@ -65,7 +46,6 @@ ) from rmgpy.kinetics.chebyshev import Chebyshev from rmgpy.kinetics.falloff import Lindemann, ThirdBody, Troe -from rmgpy.kinetics.kineticsdata import KineticsData from rmgpy.kinetics.surface import StickingCoefficient, SurfaceChargeTransfer from rmgpy.molecule.fragment import Fragment from rmgpy.reaction import Reaction @@ -77,7 +57,17 @@ from rmgpy.species import Species from rmgpy.thermo.nasa import NASA, NASAPolynomial from rmgpy.thermo.thermodata import ThermoData -from rmgpy.thermo.wilhoit import Wilhoit + + +NO_JULIA = True +try: + from juliacall import Main + Main.seval("using PythonCall") + Main.seval("using ReactionMechanismSimulator") + Main.seval("using ReactionMechanismSimulator.Sundials") + NO_JULIA = False +except Exception as e: + logging.warning(f"Julia import failed, RMS reactors not available.\nException: {str(e)}\nStacktrace:\n{e.__traceback__}") def to_julia(obj): @@ -104,21 +94,6 @@ def to_julia(obj): else: # Other native Python project does not need special conversion. return obj -NO_JULIA = False -try: - if __debug__: - from os.path import abspath, dirname, exists, join - - 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 diffeqpy import de - from julia import Main - from pyrms import rms -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: """ diff --git a/setup.py b/setup.py index 604a856eb1..4bebd5a556 100644 --- a/setup.py +++ b/setup.py @@ -27,16 +27,7 @@ # # ############################################################################### -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 +from setuptools import setup try: from Cython.Build import cythonize @@ -51,8 +42,10 @@ print('NumPy (http://numpy.scipy.org/) is required to build or install RMG Py.') raise +from setuptools import find_packages + # Create annotated HTML files for each of the Cython modules -Options.annotate = True +Options.annotate = False directives = { # Set input language version to python 3 @@ -63,143 +56,68 @@ # 'embedsignature': True, } -################################################################################ - -main_ext_modules = [ +ext_modules = [ # RMG - Extension('rmgpy.rmgobject', ['rmgpy/rmgobject.pyx']), + 'rmgpy/rmgobject.pyx', # Kinetics - Extension('rmgpy.kinetics.arrhenius', ['rmgpy/kinetics/arrhenius.pyx']), - Extension('rmgpy.kinetics.chebyshev', ['rmgpy/kinetics/chebyshev.pyx']), - Extension('rmgpy.kinetics.kineticsdata', ['rmgpy/kinetics/kineticsdata.pyx']), - Extension('rmgpy.kinetics.falloff', ['rmgpy/kinetics/falloff.pyx']), - Extension('rmgpy.kinetics.model', ['rmgpy/kinetics/model.pyx']), - Extension('rmgpy.kinetics.tunneling', ['rmgpy/kinetics/tunneling.pyx']), - Extension('rmgpy.kinetics.surface', ['rmgpy/kinetics/surface.pyx']), - Extension('rmgpy.kinetics.uncertainties', ['rmgpy/kinetics/uncertainties.pyx']), + 'rmgpy/kinetics/arrhenius.pyx', + 'rmgpy/kinetics/chebyshev.pyx', + 'rmgpy/kinetics/kineticsdata.pyx', + 'rmgpy/kinetics/falloff.pyx', + 'rmgpy/kinetics/model.pyx', + 'rmgpy/kinetics/tunneling.pyx', + 'rmgpy/kinetics/surface.pyx', + 'rmgpy/kinetics/uncertainties.pyx', # 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=['.']), - # Pressure dependence - Extension('rmgpy.pdep.collision', ['rmgpy/pdep/collision.pyx']), - Extension('rmgpy.pdep.configuration', ['rmgpy/pdep/configuration.pyx']), - Extension('rmgpy.pdep.me', ['rmgpy/pdep/me.pyx']), - Extension('rmgpy.pdep.msc', ['rmgpy/pdep/msc.pyx']), - Extension('rmgpy.pdep.reaction', ['rmgpy/pdep/reaction.pyx']), - Extension('rmgpy.pdep.rs', ['rmgpy/pdep/rs.pyx']), - Extension('rmgpy.pdep.cse', ['rmgpy/pdep/cse.pyx']), - # Statistical mechanics - Extension('rmgpy.statmech.conformer', ['rmgpy/statmech/conformer.pyx']), - Extension('rmgpy.statmech.mode', ['rmgpy/statmech/mode.pyx']), - Extension('rmgpy.statmech.rotation', ['rmgpy/statmech/rotation.pyx']), - Extension('rmgpy.statmech.schrodinger', ['rmgpy/statmech/schrodinger.pyx']), - Extension('rmgpy.statmech.torsion', ['rmgpy/statmech/torsion.pyx']), - Extension('rmgpy.statmech.translation', ['rmgpy/statmech/translation.pyx']), - Extension('rmgpy.statmech.vibration', ['rmgpy/statmech/vibration.pyx']), - # Thermodynamics - Extension('rmgpy.thermo.thermodata', ['rmgpy/thermo/thermodata.pyx']), - Extension('rmgpy.thermo.model', ['rmgpy/thermo/model.pyx']), - Extension('rmgpy.thermo.nasa', ['rmgpy/thermo/nasa.pyx']), - Extension('rmgpy.thermo.wilhoit', ['rmgpy/thermo/wilhoit.pyx']), - # Miscellaneous - Extension('rmgpy.constants', ['rmgpy/constants.py'], include_dirs=['.']), - Extension('rmgpy.quantity', ['rmgpy/quantity.py'], include_dirs=['.']), - Extension('rmgpy.reaction', ['rmgpy/reaction.py'], include_dirs=['.']), - Extension('rmgpy.species', ['rmgpy/species.py'], include_dirs=['.']), - Extension('rmgpy.chemkin', ['rmgpy/chemkin.pyx'], include_dirs=['.']), -] - -solver_ext_modules = [ - Extension('rmgpy.solver.base', ['rmgpy/solver/base.pyx'], include_dirs=['.']), - Extension('rmgpy.solver.simple', ['rmgpy/solver/simple.pyx'], include_dirs=['.']), - Extension('rmgpy.solver.liquid', ['rmgpy/solver/liquid.pyx'], include_dirs=['.']), - Extension('rmgpy.solver.mbSampled', ['rmgpy/solver/mbSampled.pyx'], include_dirs=['.']), - Extension('rmgpy.solver.surface', ['rmgpy/solver/surface.pyx'], include_dirs=['.']), -] - -arkane_ext_modules = [ - # RMG - Extension('rmgpy.rmgobject', ['rmgpy/rmgobject.pyx']), - # Kinetics - Extension('rmgpy.kinetics.arrhenius', ['rmgpy/kinetics/arrhenius.pyx']), - Extension('rmgpy.kinetics.chebyshev', ['rmgpy/kinetics/chebyshev.pyx']), - Extension('rmgpy.kinetics.kineticsdata', ['rmgpy/kinetics/kineticsdata.pyx']), - Extension('rmgpy.kinetics.falloff', ['rmgpy/kinetics/falloff.pyx']), - Extension('rmgpy.kinetics.model', ['rmgpy/kinetics/model.pyx']), - Extension('rmgpy.kinetics.tunneling', ['rmgpy/kinetics/tunneling.pyx']), + 'rmgpy/molecule/atomtype.py', + 'rmgpy/molecule/element.py', + 'rmgpy/molecule/graph.pyx', + 'rmgpy/molecule/group.py', + 'rmgpy/molecule/molecule.py', + 'rmgpy/molecule/symmetry.py', + 'rmgpy/molecule/vf2.pyx', + 'rmgpy/molecule/converter.py', + 'rmgpy/molecule/translator.py', + 'rmgpy/molecule/util.py', + 'rmgpy/molecule/inchi.py', + 'rmgpy/molecule/resonance.py', + 'rmgpy/molecule/pathfinder.py', + 'rmgpy/molecule/kekulize.pyx', # Pressure dependence - Extension('rmgpy.pdep.collision', ['rmgpy/pdep/collision.pyx']), - Extension('rmgpy.pdep.configuration', ['rmgpy/pdep/configuration.pyx']), - Extension('rmgpy.pdep.me', ['rmgpy/pdep/me.pyx']), - Extension('rmgpy.pdep.msc', ['rmgpy/pdep/msc.pyx']), - Extension('rmgpy.pdep.reaction', ['rmgpy/pdep/reaction.pyx']), - Extension('rmgpy.pdep.rs', ['rmgpy/pdep/rs.pyx']), - Extension('rmgpy.pdep.cse', ['rmgpy/pdep/cse.pyx']), + 'rmgpy/pdep/collision.pyx', + 'rmgpy/pdep/configuration.pyx', + 'rmgpy/pdep/me.pyx', + 'rmgpy/pdep/msc.pyx', + 'rmgpy/pdep/reaction.pyx', + 'rmgpy/pdep/rs.pyx', + 'rmgpy/pdep/cse.pyx', # Statistical mechanics - Extension('rmgpy.statmech.conformer', ['rmgpy/statmech/conformer.pyx']), - Extension('rmgpy.statmech.mode', ['rmgpy/statmech/mode.pyx']), - Extension('rmgpy.statmech.rotation', ['rmgpy/statmech/rotation.pyx']), - Extension('rmgpy.statmech.schrodinger', ['rmgpy/statmech/schrodinger.pyx']), - Extension('rmgpy.statmech.torsion', ['rmgpy/statmech/torsion.pyx']), - Extension('rmgpy.statmech.translation', ['rmgpy/statmech/translation.pyx']), - Extension('rmgpy.statmech.vibration', ['rmgpy/statmech/vibration.pyx']), + 'rmgpy/statmech/conformer.pyx', + 'rmgpy/statmech/mode.pyx', + 'rmgpy/statmech/rotation.pyx', + 'rmgpy/statmech/schrodinger.pyx', + 'rmgpy/statmech/torsion.pyx', + 'rmgpy/statmech/translation.pyx', + 'rmgpy/statmech/vibration.pyx', # Thermodynamics - Extension('rmgpy.thermo.thermodata', ['rmgpy/thermo/thermodata.pyx']), - Extension('rmgpy.thermo.model', ['rmgpy/thermo/model.pyx']), - Extension('rmgpy.thermo.nasa', ['rmgpy/thermo/nasa.pyx']), - Extension('rmgpy.thermo.wilhoit', ['rmgpy/thermo/wilhoit.pyx']), + 'rmgpy/thermo/thermodata.pyx', + 'rmgpy/thermo/model.pyx', + 'rmgpy/thermo/nasa.pyx', + 'rmgpy/thermo/wilhoit.pyx', # Miscellaneous - Extension('rmgpy.constants', ['rmgpy/constants.py'], include_dirs=['.']), - Extension('rmgpy.quantity', ['rmgpy/quantity.py'], include_dirs=['.']), + 'rmgpy/constants.py', + 'rmgpy/quantity.py', + 'rmgpy/reaction.py', + 'rmgpy/species.py', + 'rmgpy/chemkin.pyx', + # solvers + 'rmgpy/solver/base.pyx', + 'rmgpy/solver/simple.pyx', + 'rmgpy/solver/liquid.pyx', + 'rmgpy/solver/mbSampled.pyx', + 'rmgpy/solver/surface.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) - ext_modules.extend(solver_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 'solver' in sys.argv: - # This is for `python setup.py build_ext solver` - sys.argv.remove('solver') - ext_modules.extend(solver_ext_modules) -if 'arkane' in sys.argv: - # This is for `python setup.py build_ext arkane` - sys.argv.remove('arkane') - ext_modules.extend(main_ext_modules) - ext_modules.extend(arkane_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) - temporary_list.extend(solver_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 = [ 'Arkane.py', 'rmg.py', @@ -215,41 +133,27 @@ 'scripts/standardizeModelSpeciesNames.py', 'scripts/thermoEstimator.py', 'scripts/isotopes.py', - 'testing/databaseTest.py', ] -modules = [] -for root, dirs, files in os.walk('rmgpy'): - if 'test_data' in root: - continue - for f in files: - if f.endswith('.py') or f.endswith('.pyx'): - if 'Test' not in f and '__init__' not in f: - module = 'rmgpy' + root.partition('rmgpy')[-1].replace('/', '.') + '.' + f.partition('.py')[0] - modules.append(module) -for root, dirs, files in os.walk('arkane'): - if 'data' in root: - continue - for f in files: - if f.endswith('.py') or f.endswith('.pyx'): - if 'Test' not in f and '__init__' not in f: - module = 'arkane' + root.partition('arkane')[-1].replace('/', '.') + '.' + f.partition('.py')[0] - modules.append(module) - # Read the version number exec(open('rmgpy/version.py').read()) # Initiate the build and/or installation setup( - name='RMG-Py', + name='reactionmechanismgenerator', 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', 'arkane'], - py_modules=modules, + python_requires='>=3.9,<3.10', + packages=find_packages(where='.', include=["rmgpy*"]) + find_packages(where='.', include=["arkane*"]), scripts=scripts, - ext_modules=cythonize(ext_modules, build_dir='build', compiler_directives=directives), - include_dirs=['.', numpy.get_include()], + include_package_data=True, + package_data={ + "": ["*.pxd"], + }, + ext_modules=cythonize(ext_modules, compiler_directives=directives), + include_dirs=numpy.get_include(), + install_requires=["Cython", "numpy"], ) diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000000..e8ce4f9c01 --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,7 @@ +import multiprocessing + +multiprocessing.set_start_method('fork') + +from openbabel import pybel + +pybel.ob.obErrorLog.SetOutputLevel(0) diff --git a/utilities.py b/utilities.py index 87d39e7384..10178c10df 100644 --- a/utilities.py +++ b/utilities.py @@ -59,20 +59,10 @@ def check_dependencies(): print(""" There are missing dependencies as listed above. Please install them before proceeding. -Using Anaconda, these dependencies can be individually installed from the RMG channel as follows: - - conda install -c rmg [package name] -{0} -You can alternatively update your environment and install all missing dependencies as follows: - conda env update -f environment.yml Be sure to activate your conda environment (rmg_env by default) before installing or updating. -""".format(""" -RDKit should be installed from the RDKit channel instead: - - conda install -c rdkit rdkit -""" if missing['rdkit'] else '')) +""") else: print(""" Everything was found :) @@ -228,21 +218,6 @@ def check_pydas(): elif dassl: f.write('DEF DASPK = 0\n') - -def check_python(): - """ - Check that Python 3 is in the environment. - """ - major = sys.version_info.major - minor = sys.version_info.minor - if not (major == 3 and minor >= 7): - sys.exit('\nRMG-Py requires Python 3.7 or higher. You are using Python {0}.{1}.\n\n' - 'If you are using Anaconda, you should create a new environment using\n\n' - ' conda env create -f environment.yml\n\n' - 'If you have an existing rmg_env, you can remove it using\n\n' - ' conda remove --name rmg_env --all\n'.format(major, minor)) - - def clean(subdirectory=''): """ Removes files generated during compilation. @@ -253,7 +228,7 @@ def clean(subdirectory=''): if platform.system() == 'Windows': extensions = ['.pyd', '.pyc'] else: - extensions = ['.so', '.pyc'] + extensions = ['.so', '.pyc', ".c"] # Remove temporary build files print('Removing build directory...') @@ -393,7 +368,6 @@ def replace_header(oldfile): parser.add_argument('command', metavar='COMMAND', type=str, choices=['check-dependencies', 'check-pydas', - 'check-python', 'clean', 'clean-solver', 'update-headers'], @@ -405,8 +379,6 @@ def replace_header(oldfile): check_dependencies() elif args.command == 'check-pydas': check_pydas() - elif args.command == 'check-python': - check_python() elif args.command == 'clean': clean() elif args.command == 'clean-solver': From 0e33bf3cfc2ec983bffc55ea1a1a5021c9f58a33 Mon Sep 17 00:00:00 2001 From: JacksonBurns Date: Sat, 25 Jan 2025 16:44:47 -0500 Subject: [PATCH 2/3] docker build should wait for regression test --- .github/workflows/CI.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ce533a0663..2d09a850fd 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -406,7 +406,7 @@ jobs: # who knows ¯\_(ツ)_/¯ # # taken from https://github.com/docker/build-push-action - needs: build-and-test + needs: regression-test runs-on: ubuntu-latest if: github.ref == 'refs/heads/main' && github.repository == 'ReactionMechanismGenerator/RMG-Py' steps: From 80bb62fb632cc5c5224c6cfe14c125322e29ec68 Mon Sep 17 00:00:00 2001 From: jonwzheng Date: Mon, 27 Jan 2025 17:01:08 -0500 Subject: [PATCH 3/3] change liquid initial conditions to surface initial conditions for ConstantTLiquidSurfaceReactor --- rmgpy/rmg/reactionmechanismsimulator_reactors.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rmgpy/rmg/reactionmechanismsimulator_reactors.py b/rmgpy/rmg/reactionmechanismsimulator_reactors.py index 77b0ffb8b0..d593858115 100644 --- a/rmgpy/rmg/reactionmechanismsimulator_reactors.py +++ b/rmgpy/rmg/reactionmechanismsimulator_reactors.py @@ -527,7 +527,7 @@ def generate_reactor(self, phase_system): cat_constant_species = [cspc for cspc in self.const_spc_names if cspc in [spc.name for spc in surf.species]] domainliq, y0liq, pliq = Main.ConstantTVDomain(phase=liq, initialconds=to_julia(liq_initial_cond), constantspecies=to_julia(liq_constant_species)) domaincat, y0cat, pcat = Main.ConstantTAPhiDomain( - phase=surf, initialconds=to_julia(liq_initial_cond), constantspecies=to_julia(cat_constant_species), + phase=surf, initialconds=to_julia(self.initial_conditions["surface"]), constantspecies=to_julia(cat_constant_species), ) if interface.reactions == []: inter, pinter = Main.ReactiveInternalInterfaceConstantTPhi(