diff --git a/Makefile b/Makefile index 205637a1be..1f1997af76 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ eg0: all rm -rf testing/eg0/* cp examples/rmg/superminimal/input.py testing/eg0/input.py @ echo "Running eg0: superminimal (H2 oxidation) example" - RMG_DISABLE_JULIA=1 python rmg.py testing/eg0/input.py + python rmg.py testing/eg0/input.py eg1: all mkdir -p testing/eg1 @@ -72,7 +72,7 @@ eg1: all cp examples/rmg/minimal/input.py testing/eg1/input.py coverage erase @ echo "Running eg1: minimal (ethane pyrolysis) example with coverage tracking AND profiling" - RMG_DISABLE_JULIA=1 coverage run rmg.py -p testing/eg1/input.py + coverage run rmg.py -p testing/eg1/input.py coverage report coverage html @@ -82,7 +82,7 @@ eg2: all cp examples/rmg/1,3-hexadiene/input.py testing/eg2/input.py coverage erase @ echo "Running eg2: 1,3-hexadiene example with profiling" - RMG_DISABLE_JULIA=1 python rmg.py -p testing/eg2/input.py + python rmg.py -p testing/eg2/input.py eg3: all mkdir -p testing/eg3 @@ -90,28 +90,28 @@ eg3: all cp examples/rmg/liquid_phase/input.py testing/eg3/input.py coverage erase @ echo "Running eg3: liquid_phase example with profiling" - RMG_DISABLE_JULIA=1 python rmg.py -p testing/eg3/input.py + python rmg.py -p testing/eg3/input.py eg5: all mkdir -p testing/eg5 rm -rf testing/eg5/* cp examples/rmg/heptane-eg5/input.py testing/eg5/input.py @ echo "Running eg5: heptane example" - RMG_DISABLE_JULIA=1 python rmg.py testing/eg5/input.py + python rmg.py testing/eg5/input.py eg6: all mkdir -p testing/eg6 rm -rf testing/eg6/* cp examples/rmg/ethane-oxidation/input.py testing/eg6/input.py @ echo "Running eg6: ethane-oxidation example" - RMG_DISABLE_JULIA=1 python rmg.py testing/eg6/input.py + python rmg.py testing/eg6/input.py eg7: all mkdir -p testing/eg7 rm -rf testing/eg7/* cp examples/rmg/gri_mech_rxn_lib/input.py testing/eg7/input.py @ echo "Running eg7: gri_mech_rxn_lib example" - RMG_DISABLE_JULIA=1 python rmg.py testing/eg7/input.py + python rmg.py testing/eg7/input.py scoop: all mkdir -p testing/scoop @@ -119,7 +119,7 @@ scoop: all cp examples/rmg/minimal/input.py testing/scoop/input.py coverage erase @ echo "Running minimal example with SCOOP" - RMG_DISABLE_JULIA=1 python -m scoop -n 2 rmg.py -v testing/scoop/input.py + python -m scoop -n 2 rmg.py -v testing/scoop/input.py eg4: all mkdir -p testing/eg4 diff --git a/rmgpy/__init__.py b/rmgpy/__init__.py index 04c3c5a9f2..387bf82d2f 100644 --- a/rmgpy/__init__.py +++ b/rmgpy/__init__.py @@ -154,10 +154,6 @@ def reset(self): # The global settings object settings = Settings(path=None) -# Global flag to disable Julia imports (useful for scripts that don't need RMS) -DISABLE_JULIA = False - - ################################################################################ diff --git a/rmgpy/pdep/sls.py b/rmgpy/pdep/sls.py index 12a0dd8e05..341ff89860 100644 --- a/rmgpy/pdep/sls.py +++ b/rmgpy/pdep/sls.py @@ -33,29 +33,39 @@ """ import logging -import sys +import os import numpy as np import scipy.linalg import scipy.optimize as opt -import scipy.sparse as sparse +import rmgpy import rmgpy.constants as constants from rmgpy.pdep.me import generate_full_me_matrix, states_to_configurations from rmgpy.rmg.reactionmechanismsimulator_reactors import to_julia -NO_JULIA = False -try: - from juliacall import Main +class LazyMain: + """ + A Lazy wrapper for the juliacall Main module, that will delay importing + the underlying Main module until it is first called. + This is to delay loading Julia until it's really needed. + """ + # If you modify this class, please consider making similar changes to + # rmgpy/rmg/reactionmechanismsimulator_reactors.py, which has a similar LazyMain class + def __getattr__(self, name): + try: + from juliacall import Main as JuliaMain + Main = JuliaMain + Main.seval("using ReactionMechanismSimulator.SciMLBase") + Main.seval("using ReactionMechanismSimulator.Sundials") + except Exception as e: + logging.error("Failed to import Julia and load ReactionMechanismSimulator components needed.") + raise + globals()['Main'] = JuliaMain # Replace proxy with real thing, for next time it's called + return getattr(JuliaMain, name) # Return the attribute for the first time it's called + +Main = LazyMain() - Main.seval("using ReactionMechanismSimulator.SciMLBase") - Main.seval("using ReactionMechanismSimulator.Sundials") -except Exception as e: - logging.info( - f"Unable to import Julia dependencies, original error: {str(e)}" - ". Master equation method 'ode' will not be available on this execution." - ) - NO_JULIA = True def get_initial_condition(network, x0, indices): @@ -167,11 +177,6 @@ def get_rate_coefficients_SLS(network, T, P, method="mexp", neglect_high_energy_ tau = np.abs(1.0 / fastest_reaction) if method == "ode": - if NO_JULIA: - raise RuntimeError( - "Required Julia dependencies for method 'ode' are not installed.\n" - "Please follow the steps to install Julia dependencies at https://reactionmechanismgenerator.github.io/RMG-Py/users/rmg/installation/anacondaDeveloper.html." - ) f = Main.seval( """ function f(du, u, M, t) diff --git a/rmgpy/rmg/input.py b/rmgpy/rmg/input.py index ba48ceb48a..c2bebbe985 100644 --- a/rmgpy/rmg/input.py +++ b/rmgpy/rmg/input.py @@ -47,7 +47,6 @@ from rmgpy.quantity import Energy, Quantity, RateCoefficient, SurfaceConcentration from rmgpy.rmg.model import CoreEdgeReactionModel from rmgpy.rmg.reactionmechanismsimulator_reactors import ( - NO_JULIA, ConstantTLiquidSurfaceReactor, ConstantTPIdealGasReactor, ConstantTVLiquidReactor, @@ -1593,11 +1592,6 @@ 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." - " (https://reactionmechanismgenerator.github.io/RMG-Py/users/rmg/installation/anacondaDeveloper.html)" - ) logging.exception(e) raise finally: diff --git a/rmgpy/rmg/main.py b/rmgpy/rmg/main.py index 1d9cb8d55f..11a885deed 100644 --- a/rmgpy/rmg/main.py +++ b/rmgpy/rmg/main.py @@ -75,7 +75,6 @@ from rmgpy.rmg.model import CoreEdgeReactionModel, Species from rmgpy.rmg.output import OutputHTMLWriter from rmgpy.rmg.pdep import PDepNetwork, PDepReaction -from rmgpy.rmg.reactionmechanismsimulator_reactors import NO_JULIA from rmgpy.rmg.reactionmechanismsimulator_reactors import Reactor as RMSReactor from rmgpy.rmg.settings import ModelSettings from rmgpy.solver.base import TerminationConversion, TerminationTime diff --git a/rmgpy/rmg/model.py b/rmgpy/rmg/model.py index 713e7b6ea0..d677755b07 100644 --- a/rmgpy/rmg/model.py +++ b/rmgpy/rmg/model.py @@ -57,7 +57,6 @@ from rmgpy.rmg.pdep import PDepNetwork, PDepReaction from rmgpy.rmg.react import react_all from rmgpy.rmg.reactionmechanismsimulator_reactors import ( - NO_JULIA, Interface, Phase, PhaseSystem, @@ -81,7 +80,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 = None if NO_JULIA else PhaseSystem(phases, interfaces) + self.phase_system = PhaseSystem(phases, interfaces) def __reduce__(self): """ @@ -355,7 +354,7 @@ def make_new_species(self, object, label="", reactive=True, check_existing=True, orilabel = spec.label label = orilabel i = 2 - if self.edge.phase_system: # None when RMS not installed + if self.edge.phase_system: # !!! Not maintained when operating with require_rms=False? while any([label in phase.names for phase in self.edge.phase_system.phases.values()]): label = orilabel + "-" + str(i) i += 1 diff --git a/rmgpy/rmg/reactionmechanismsimulator_reactors.py b/rmgpy/rmg/reactionmechanismsimulator_reactors.py index be94141b24..4831586f3c 100644 --- a/rmgpy/rmg/reactionmechanismsimulator_reactors.py +++ b/rmgpy/rmg/reactionmechanismsimulator_reactors.py @@ -35,6 +35,7 @@ import os import numpy as np +import rmgpy from rmgpy.data.solvation import SolventData from rmgpy.kinetics.arrhenius import ( @@ -59,25 +60,29 @@ from rmgpy.thermo.nasa import NASA, NASAPolynomial from rmgpy.thermo.thermodata import ThermoData - -NO_JULIA = True - -# Check if Julia should be disabled via module-level variable or environment variable -import rmgpy -if hasattr(rmgpy, 'DISABLE_JULIA') and rmgpy.DISABLE_JULIA: - logging.info("Julia imports disabled via rmgpy.DISABLE_JULIA flag") -elif os.environ.get('RMG_DISABLE_JULIA', '').lower() in ('1', 'true', 'yes'): - logging.info("Julia imports disabled via RMG_DISABLE_JULIA environment variable") -else: - try: - from juliacall import Main - Main.seval("using PythonCall") - Main.seval("using ReactionMechanismSimulator") - Main.seval("using ReactionMechanismSimulator.Sundials") - NO_JULIA = False - except ImportError as e: - logging.warning("Julia import failed, RMS reactors not available. " - "Exception: %s", str(e)) +class LazyMain: + """ + A Lazy wrapper for the juliacall Main module, that will delay importing + the underlying Main module until it is first called. + This is to delay loading Julia until it's really needed. + """ + # If you modify this class, please consider making similar changes to + # rmgpy/pdep/sls.py, which has a similar LazyMain class. + def __getattr__(self, name): + try: + from juliacall import Main as JuliaMain + Main = JuliaMain + Main.seval("using PythonCall") + Main.seval("using ReactionMechanismSimulator") + Main.seval("using ReactionMechanismSimulator.Sundials") + except Exception as e: + logging.error("Failed to import Julia and load ReactionmechanismSimulator. " + "To use phase systems and RMS reactors, install RMG-Py with RMS.") + logging.exception(e) + raise + globals()['Main'] = JuliaMain # Replace proxy with real thing, for next time it's called + return getattr(JuliaMain, name) # Return the attribute for the first time it's called +Main = LazyMain() def to_julia(obj): @@ -95,6 +100,7 @@ def to_julia(obj): object : Main.Dict | Main.Vector | Main.Matrix | object The Julia object """ + if isinstance(obj, dict): return Main.PythonCall.pyconvert(Main.Dict, obj) elif isinstance(obj, (list, np.ndarray)): diff --git a/rmgpy/tools/regression.py b/rmgpy/tools/regression.py index d738827588..428918ae74 100644 --- a/rmgpy/tools/regression.py +++ b/rmgpy/tools/regression.py @@ -37,9 +37,6 @@ import os.path import sys -import rmgpy -rmgpy.DISABLE_JULIA = True # disable Julia to save time and warnings - from rmgpy.molecule import Molecule from rmgpy.quantity import Quantity from rmgpy.species import Species diff --git a/scripts/checkModels.py b/scripts/checkModels.py index 3d005dd2e3..6d5f37cb94 100644 --- a/scripts/checkModels.py +++ b/scripts/checkModels.py @@ -32,10 +32,6 @@ import math import sys -# Disable Julia imports for checkModels.py since they're not needed -import rmgpy -rmgpy.DISABLE_JULIA = True - from rmgpy.tools.diffmodels import execute logger = logging.getLogger('checkModels')