diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 864db7add52..bff0c433596 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -278,3 +278,87 @@ jobs: shell: cmd - name: Test Cantera run: scons test + + # Running of Python examples separated due to significant run time. + # Installation restrictions exist on systems with older numpy versions. + python-examples-ubuntu: + name: Python Examples on Ubuntu with Python 3.8 + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v2 + name: Checkout the repository + with: + submodules: recursive + - name: Setup python + uses: actions/setup-python@v1 + with: + python-version: '3.8' + architecture: x64 + - name: Install Apt dependencies + run: sudo apt-get install libboost-dev gfortran + - name: Upgrade pip + run: python3 -m pip install -U pip setuptools wheel + - name: Install Python dependencies + run: python3 -m pip install ruamel.yaml scons numpy cython h5py pandas matplotlib scipy + - name: Build Cantera + run: python3 `which scons` build -j2 + - name: Test Cantera + run: python3 `which scons` test + - name: Run Python examples + run: python3 `which scons` check-examples + + python-examples-windows: + name: Python Examples on Windows with Python 3.8 + runs-on: windows-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v2 + name: Checkout the repository + with: + submodules: recursive + - name: Set Up Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + architecture: 'x64' + - name: Install Python dependencies + run: | + python -m pip install -U pip setuptools + python -m pip install scons pypiwin32 numpy ruamel.yaml cython h5py matplotlib scipy + - name: Build Cantera + run: | + scons build -j2 boost_inc_dir=%BOOST_ROOT_1_69_0% debug=n VERBOSE=y python_package=full ^ + msvc_version=14.2 f90_interface=n + shell: cmd + - name: Test Cantera + run: scons test + - name: Run Python examples + run: scons check-examples + + python-examples-macos: + name: Python Examples on macOS with Python 3.8 + runs-on: macos-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v2 + name: Checkout the repository + with: + submodules: recursive + - name: Setup Python + uses: actions/setup-python@v1 + with: + python-version: 3.8 + architecture: x64 + - name: Install Brew dependencies + run: brew install boost libomp + - name: Upgrade pip + run: python3 -m pip install -U pip setuptools wheel + - name: Install Python dependencies + run: python3 -m pip install ruamel.yaml scons numpy cython h5py matplotlib scipy + - name: Build Cantera + run: python3 `which scons` build -j2 + - name: Test Cantera + run: python3 `which scons` test + - name: Run Python examples + run: python3 `which scons` check-examples diff --git a/SConstruct b/SConstruct index 276f9b1e0be..f311c225701 100644 --- a/SConstruct +++ b/SConstruct @@ -30,6 +30,8 @@ Basic usage: 'scons samples' - Compile the C++ and Fortran samples. + 'scons check-examples' - Run all Python examples + 'scons msi' - Build a Windows installer (.msi) for Cantera. 'scons sphinx' - Build the Sphinx documentation @@ -46,7 +48,8 @@ if not COMMAND_LINE_TARGETS: sys.exit(0) valid_commands = ('build','clean','install','uninstall', - 'help','msi','samples','sphinx','doxygen','dump') + 'help','msi','samples','sphinx','doxygen','dump', + 'check-examples') for command in COMMAND_LINE_TARGETS: if command not in valid_commands and not command.startswith('test'): @@ -1567,6 +1570,7 @@ env['build_targets'] = buildTargets libraryTargets = [] # objects that go in the Cantera library installTargets = [] sampleTargets = [] +checkTargets = [] def build(targets): """ Wrapper to add target to list of build targets """ @@ -1579,6 +1583,11 @@ def buildSample(*args, **kwargs): sampleTargets.extend(targets) return targets +def checkExample(targets): + """ Wrapper to add target to list of sample checks """ + checkTargets.extend(targets) + return targets + def install(*args, **kwargs): """ Wrapper to add target to list of install targets """ if not addInstallActions: @@ -1671,7 +1680,7 @@ if not env['renamed_shared_libraries']: env['cantera_shared_libs'] = linkLibs # Add targets from the SConscript files in the various subdirectories -Export('env', 'build', 'libraryTargets', 'install', 'buildSample') +Export('env', 'build', 'libraryTargets', 'install', 'buildSample', 'checkExample') # ext needs to come before src so that libraryTargets is fully populated VariantDir('build/ext', 'ext', duplicate=0) @@ -1704,6 +1713,7 @@ if env['doxygen_docs'] or env['sphinx_docs']: VariantDir('build/samples', 'samples', duplicate=0) sampledir_excludes = ['\\.o$', '^~$', '\\.in', 'SConscript'] SConscript('build/samples/cxx/SConscript') +SConscript('build/samples/python/SConscript') # Install C++ samples install(env.RecursiveInstall, '$inst_sampledir/cxx', @@ -1721,6 +1731,7 @@ if env['f90_interface'] == 'y': ### Meta-targets ### build_samples = Alias('samples', sampleTargets) +check_samples = Alias('check-examples', checkTargets) def postBuildMessage(target, source, env): print("*******************************************************") @@ -1781,15 +1792,18 @@ def postInstallMessage(target, source, env): """.format(**env_dict)) if os.name != 'nt': + env['setup_cantera'] = pjoin(env['ct_bindir'], 'setup_cantera') + env['setup_cantera_csh'] = pjoin(env['ct_bindir'], 'setup_cantera.csh') install_message += textwrap.dedent(""" + Setup scripts to configure the environment for Cantera are at: - setup script (bash) {ct_bindir!s}/setup_cantera - setup script (csh/tcsh) {ct_bindir!s}/setup_cantera.csh + setup script (bash) {setup_cantera!s} + setup script (csh/tcsh) {setup_cantera_csh!s} It is recommended that you run the script for your shell by typing: - source {ct_bindir!s}/setup_cantera + source {setup_cantera!s} before using Cantera, or else include its contents in your shell login script. """.format(**env_dict)) diff --git a/interfaces/cython/SConscript b/interfaces/cython/SConscript index adcff5c3965..4e8791fa3dc 100644 --- a/interfaces/cython/SConscript +++ b/interfaces/cython/SConscript @@ -83,14 +83,13 @@ localenv.Depends(ext, localenv['cantera_staticlib']) for f in (mglob(localenv, 'cantera', 'py') + mglob(localenv, 'cantera/test', 'py') + - mglob(localenv, 'cantera/examples/tutorial', 'py') + - mglob(localenv, 'cantera/examples/equilibrium', 'py') + mglob(localenv, 'cantera/examples/kinetics', 'py') + - mglob(localenv, 'cantera/examples/transport', 'py') + - mglob(localenv, 'cantera/examples/reactors', 'py') + + mglob(localenv, 'cantera/examples/multiphase', 'py') + mglob(localenv, 'cantera/examples/onedim', 'py') + + mglob(localenv, 'cantera/examples/reactors', 'py') + mglob(localenv, 'cantera/examples/surface_chemistry', 'py') + - mglob(localenv, 'cantera/examples/misc', 'py')): + mglob(localenv, 'cantera/examples/thermo', 'py') + + mglob(localenv, 'cantera/examples/transport', 'py')): localenv.Depends(mod, f) # Determine installation path and install the Python module diff --git a/interfaces/cython/cantera/examples/thermo/sound_speed.py b/interfaces/cython/cantera/examples/thermo/sound_speed.py index 7b9c269bb66..8fd8f7a5ca1 100644 --- a/interfaces/cython/cantera/examples/thermo/sound_speed.py +++ b/interfaces/cython/cantera/examples/thermo/sound_speed.py @@ -8,7 +8,7 @@ import math -def equilSoundSpeeds(gas, rtol=1.0e-6, maxiter=5000): +def equilSoundSpeeds(gas, rtol=1.0e-6, max_iter=5000): """ Returns a tuple containing the equilibrium and frozen sound speeds for a gas with an equilibrium composition. The gas is first set to an @@ -17,7 +17,7 @@ def equilSoundSpeeds(gas, rtol=1.0e-6, maxiter=5000): """ # set the gas to equilibrium at its current T and P - gas.equilibrate('TP', rtol=rtol, maxiter=maxiter) + gas.equilibrate('TP', rtol=rtol, max_iter=max_iter) # save properties s0 = gas.s @@ -35,7 +35,7 @@ def equilSoundSpeeds(gas, rtol=1.0e-6, maxiter=5000): afrozen = math.sqrt((p1 - p0)/(gas.density - r0)) # now equilibrate the gas holding S and P constant - gas.equilibrate('SP', rtol=rtol, maxiter=maxiter) + gas.equilibrate('SP', rtol=rtol, max_iter=max_iter) # equilibrium sound speed aequil = math.sqrt((p1 - p0)/(gas.density - r0)) diff --git a/samples/README.txt b/samples/python/README.txt similarity index 100% rename from samples/README.txt rename to samples/python/README.txt diff --git a/samples/python/SConscript b/samples/python/SConscript new file mode 100644 index 00000000000..9058eac687c --- /dev/null +++ b/samples/python/SConscript @@ -0,0 +1,56 @@ +from buildutils import * + +Import('env', 'checkExample') + +localenv = env.Clone() +localenv.PrependENVPath('LD_LIBRARY_PATH', Dir('#build/lib').abspath) +localenv.PrependENVPath('PYTHONPATH', Dir('#build/python').abspath) +localenv['ENV']['CANTERA_DATA'] = (Dir('#build/data').abspath) +localenv['ENV']['MPLBACKEND'] = 'Agg' +if localenv['OS'] == 'Windows': + localenv['ENV']['HOMEPATH'] = os.environ['HOMEPATH'] + +subdirs = [ + 'kinetics', + 'multiphase', + 'onedim', + 'reactors', + 'surface_chemistry', + 'thermo', + 'transport', +] + +broken = [ + 'extract_submechanism.py', # issue #821 + 'diffusion_flame_extinction.py', # issue #872 + 'catalytic_combustion.py', # issue #873 + 'piston.py', # issue #875 + 'surf_pfr.py', # issue #877 + 'ion_burner_flame.py', # poor convergence +] + +dependencies=(env['python_module'] + env['python_extension']) + +warning_filters = [r'-W error::DeprecationWarning:'] +if localenv['OS'] == 'Windows': + # add exception as pywintypes imports a deprecated module; + # text containing whitespaces needs to be enclosed by double quotes + warning_filters.append(r'-W ignore:"the imp module is deprecated":') + +if localenv['python_package'] == 'full': + + for subdir in subdirs: + sub = Dir('#build/python/cantera/examples/{}'.format(subdir)).abspath + sample_files = mglob(localenv, sub, 'py') + for f in sample_files: + if f.name in broken: + print("Skipping broken Python example '{}'".format(f.name)) + continue + target = localenv.Command("${SOURCE.name.replace('.py', '.run')}", f, + "cd {} && ${{python_cmd}} {} " + "${{SOURCE.name}}".format(sub, + " ".join(warning_filters))) + localenv.Install(target) + for dep in dependencies: + localenv.Depends(target, dep) + checkExample(target)