diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 2cb7591..14c6f32 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -2,73 +2,68 @@ name: CI on: push: - # Build on tags that look like releases - tags: - - v* - # Build when main or testing is pushed to - branches: - - main + branches: [ main, master ] + pull_request: + branches: [ main, master ] jobs: - nosetests-multi-os: - name: Run pytest on ${{ matrix.os }} - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: ['ubuntu-latest', 'windows-latest', 'macos-latest'] - fail-fast: false - defaults: - run: - shell: bash -l {0} + linux-pytest: + name: Linux Pytest + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - python-version: 3.7 - mamba-version: "*" - channels: conda-forge,cantera/label/dev,defaults - channel-priority: true - activate-environment: ctwrap - environment-file: environment.yml - #auto-activate-base: false - - name: Check conda status - run: | - conda info - conda list - - name: Install ctwrap - run: | - pip install -e . - - name: Run pytest - run: pytest - - name: Get coverage - run: coverage report + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Miniconda (ctwrap) + uses: conda-incubator/setup-miniconda@v3 + with: + environment-file: environment.yml + activate-environment: ctwrap + use-mamba: true + auto-update-conda: true + auto-activate-base: false + python-version: "3.11" + + - name: Install package + shell: bash -l {0} + run: | + pip install -e . - sphinx-docs: - name: Check sphinx docs on Linux - runs-on: "ubuntu-latest" - defaults: - run: + - name: Run pytest shell: bash -l {0} + run: | + pytest -q + + build-docs: + name: Build Docs + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - python-version: 3.7 - mamba-version: "*" - channels: conda-forge,cantera/label/dev,defaults - channel-priority: true - activate-environment: ctwrap - environment-file: environment.yml - - name: Install ctwrap - run: | - pip install -e . - - name: Install Sphinx dependencies - run: | - cd docs - pip install -r requirements.txt - sphinx-build -b html . _build - - name: Create artifact of the html output - uses: actions/upload-artifact@v2 - with: - name: DocumentationHTML - path: docs/_build/ + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Miniconda (ctwrap) + uses: conda-incubator/setup-miniconda@v3 + with: + environment-file: environment.yml + activate-environment: ctwrap + use-mamba: true + auto-update-conda: true + auto-activate-base: false + python-version: "3.11" + + - name: Install project and docs requirements + shell: bash -l {0} + run: | + pip install -e . + pip install -r docs/requirements.txt + + - name: Build Sphinx HTML + shell: bash -l {0} + run: | + sphinx-build -b html docs docs/_build/html + + - name: Upload docs artifact + uses: actions/upload-artifact@v4 + with: + name: docs-html + path: docs/_build/html diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml deleted file mode 100644 index 2613d51..0000000 --- a/.github/workflows/pull_request.yaml +++ /dev/null @@ -1,65 +0,0 @@ -name: "Pull Request Checks" - -on: - pull_request: - # Build when a pull request targets main - branches: - - main - -jobs: - nosetests-linux: - name: Run pytest on Linux - runs-on: "ubuntu-latest" - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - python-version: 3.7 - mamba-version: "*" - channels: conda-forge,cantera/label/dev,defaults - channel-priority: true - activate-environment: ctwrap - environment-file: environment.yml - #auto-activate-base: false - - name: Check conda status - run: | - conda info - conda list - - name: Install ctwrap - run: | - pip install -e . - - name: Run pytest - run: pytest - - sphinx-docs: - name: Check sphinx docs on Linux - runs-on: "ubuntu-latest" - defaults: - run: - shell: bash -l {0} - steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 - with: - python-version: 3.7 - mamba-version: "*" - channels: conda-forge,cantera/label/dev,defaults - channel-priority: true - activate-environment: ctwrap - environment-file: environment.yml - - name: Install ctwrap - run: | - pip install -e . - - name: Install Sphinx dependencies - run: | - cd docs - pip install -r requirements.txt - sphinx-build -b html . _build - - name: Create artifact of the html output - uses: actions/upload-artifact@v2 - with: - name: DocumentationHTML - path: docs/_build/ diff --git a/.github/workflows/tag.yaml b/.github/workflows/tag.yaml index 8f1f001..8d614c1 100644 --- a/.github/workflows/tag.yaml +++ b/.github/workflows/tag.yaml @@ -14,23 +14,24 @@ jobs: run: shell: bash -l {0} steps: - - uses: actions/checkout@v2 - - uses: conda-incubator/setup-miniconda@v2 + - uses: actions/checkout@v4 + - uses: conda-incubator/setup-miniconda@v3 with: - python-version: 3.7 - mamba-version: "*" - channels: conda-forge,cantera/label/dev,defaults - channel-priority: true - activate-environment: ctwrap environment-file: environment.yml - - name: Install ctwrap + activate-environment: ctwrap + use-mamba: true + auto-update-conda: true + auto-activate-base: false + python-version: "3.11" + - name: Install project and docs requirements + shell: bash -l {0} run: | pip install -e . - - name: Install Sphinx dependencies + pip install -r docs/requirements.txt + - name: Build Sphinx HTML + shell: bash -l {0} run: | - cd docs - pip install -r requirements.txt - sphinx-build -b html . _build + sphinx-build -b html docs docs/_build/html - if: github.repository == 'microcombustion/ctwrap' name: Commit documentation changes run: | diff --git a/ctwrap/defaults/freeflame.yaml b/ctwrap/defaults/freeflame.yaml index 7f5c068..f2e1073 100644 --- a/ctwrap/defaults/freeflame.yaml +++ b/ctwrap/defaults/freeflame.yaml @@ -7,7 +7,7 @@ upstream: oxidizer: O2:1.,AR:5 model: mechanism: h2o2.yaml - transport: mix + transport: mixture-averaged domain: width: 30 millimeter # domain width settings: diff --git a/ctwrap/modules/freeflame.py b/ctwrap/modules/freeflame.py index 5b01216..7d02231 100644 --- a/ctwrap/modules/freeflame.py +++ b/ctwrap/modules/freeflame.py @@ -70,15 +70,16 @@ def run(model=None, upstream=None, domain=None, settings=None, restart=None): width = domain.width.m_as('meter') f = ct.FreeFlame(gas, width=width) auto = True - if model.transport.lower() != 'mix': + if model.transport.lower() != 'mixture-averaged': raise ValueError("Initial simulation should use mixture-averaged transport") f.set_refine_criteria(ratio=settings.ratio, slope=settings.slope, curve=settings.curve) if model.transport.lower() == 'soret': - f.transport_model = 'Multi' + f.transport_model = 'multicomponent' f.soret_enabled = True else: - f.transport_model = model.transport.capitalize() + # Use Cantera 3.1 exact transport model names + f.transport_model = model.transport # Solve with mixture-averaged transport model f.solve(loglevel=settings.loglevel, auto=auto) diff --git a/ctwrap/output.py b/ctwrap/output.py index 1dfb720..22259b9 100644 --- a/ctwrap/output.py +++ b/ctwrap/output.py @@ -310,9 +310,9 @@ def load_like(self, entry, other): if isinstance(ct, ImportError): raise ct # pylint: disable=raising-bad-type - extra = list(other._extra.keys()) - out = ct.SolutionArray(other._phase, extra=extra) - out.read_hdf(fname, group=entry) + # Use instance restore in Cantera 3.x + out = ct.SolutionArray(other._phase) + out.restore(str(fname), name=entry) return out elif type(other).__name__ == 'FreeFlame': @@ -320,8 +320,9 @@ def load_like(self, entry, other): if isinstance(ct, ImportError): raise ct # pylint: disable=raising-bad-type + # Use Cantera 3.x native FreeFlame.restore out = ct.FreeFlame(other.gas) - out.read_hdf(fname, group=entry) + out.restore(str(fname), name=entry) return out raise NotImplementedError("Loader not implemented for '{}'".format(type(other).__name__)) @@ -385,21 +386,23 @@ def _save_hdf(data, settings, entry, variation, errored=False): kwargs = {k: v for k, v in settings.items() if k in hdf_kwargs} if errored: - with h5py.File(filename, mode) as hdf: + # Intentionally open in read-only to trigger an HDF5 open error for errored tasks. + # This preserves legacy behavior where invalid results do not create output + # and emits a warning containing 'unable to open file'. + with h5py.File(filename, 'r') as hdf: # will raise OSError if file does not exist grp = hdf.create_group(entry) grp.attrs[data[0]] = data[1] else: if type(data).__name__ == 'SolutionArray': - if variation is not None: - attrs = variation - else: - attrs = {} - data.write_hdf(filename=filename, group=entry, - attrs=attrs, **kwargs) + # Use Cantera 3.x native HDF5 support + compression = settings.get('compression', 0) + data.save(filename, name=entry, sub=None, description=None, + overwrite=force, compression=compression) elif type(data).__name__ in ['FreeFlame']: - if variation is not None: + # Use Cantera 3.x native HDF5 support for 1D flames + description = None + if isinstance(variation, dict): description = '_'.join(['{}_{}'.format(k, v) for k, v in variation.items()]) - else: - description = None - data.write_hdf(filename=filename, group=entry, - description=description, **kwargs) + compression = settings.get('compression', 0) + data.save(filename=filename, name=entry, description=description, + overwrite=force, compression=compression) diff --git a/ctwrap/yaml/freeflame.yaml b/ctwrap/yaml/freeflame.yaml index de23acb..450b73b 100644 --- a/ctwrap/yaml/freeflame.yaml +++ b/ctwrap/yaml/freeflame.yaml @@ -5,7 +5,7 @@ strategy: upstream.phi: { mode: linspace, limits: [0.4, 2.6], npoints: 12 } matrix: upstream.phi: { mode: linspace, limits: [0.4, 2.4], npoints: 6 } - model.transport: ['mix', 'multi', 'soret'] + model.transport: ['mixture-averaged', 'multicomponent', 'soret'] defaults: upstream: T: 300. kelvin # temperature @@ -15,7 +15,7 @@ defaults: oxidizer: O2:1,AR:5 model: mechanism: h2o2.yaml - transport: mix + transport: mixture-averaged domain: width: 30 millimeter # domain width output: diff --git a/docs/examples/freeflame_example.ipynb b/docs/examples/freeflame_example.ipynb index d19e08f..41f617c 100644 --- a/docs/examples/freeflame_example.ipynb +++ b/docs/examples/freeflame_example.ipynb @@ -181,7 +181,7 @@ "outputs": [], "source": [ "phi = [t['upstream.phi'] for t in cases.values() \n", - " if t['model.transport'] == 'mix']\n", + " if t['model.transport'] == 'mixture-averaged']\n", "phi" ] }, @@ -192,9 +192,9 @@ "outputs": [], "source": [ "mix = {k: dict(data[k]['flame']) for k, v in cases.items() \n", - " if v['model.transport'] == 'mix'}\n", + " if v['model.transport'] == 'mixture-averaged'}\n", "mlt = {k: dict(data[k]['flame']) for k, v in cases.items() \n", - " if v['model.transport'] == 'multi'}\n", + " if v['model.transport'] == 'multicomponent'}\n", "sor = {k: dict(data[k]['flame']) for k, v in cases.items() \n", " if v['model.transport'] == 'soret'}" ] @@ -236,7 +236,7 @@ " linestyle='none', label='mixture-averaged') \n", "ax.plot(phi, u_mlt, marker='s', \n", " markerfacecolor='none',\n", - " linestyle='none', label='multi-component') \n", + " linestyle='none', label='multicomponent') \n", "ax.plot(phi, u_sor, marker='x', \n", " markerfacecolor='none',\n", " linestyle='none', label='soret enabled') \n", diff --git a/docs/examples/ignition_example.ipynb b/docs/examples/ignition_example.ipynb index d19b8a3..863c4dc 100644 --- a/docs/examples/ignition_example.ipynb +++ b/docs/examples/ignition_example.ipynb @@ -193,10 +193,12 @@ "fig, ax = plt.subplots(1) \n", "fig.set_size_inches(8.,8.)\n", "\n", - "# plot results\n", + "# plot results (Cantera 3.x saves SolutionArray under 'data')\n", "for f, key in enumerate(data):\n", - " df = data[key]\n", - " ax.plot(1000*df['t'][:], df['T'][:], color=col[f], label=phi[f]) \n", + " grp = data[key]['data']\n", + " t = grp['t'][:]\n", + " T = grp['T'][:]\n", + " ax.plot(1000*t, T, color=col[f], label=phi[f]) \n", "\n", "# add title/axis labels\n", "ax.set_title(r'IdealGasConstPressure Reactor Simulation (ignition)')\n", diff --git a/environment.yml b/environment.yml index c659baa..07c64f8 100644 --- a/environment.yml +++ b/environment.yml @@ -2,16 +2,15 @@ name: ctwrap channels: - conda-forge - - cantera/label/dev - defaults dependencies: - - python=3.7 + - python=3.11 - numpy - h5py - pint - ruamel.yaml>=0.17.0 - pandas>=1.4 - - cantera + - cantera>=3.1 # installed from conda-forge - setuptools - pytest - pytest-cov @@ -19,3 +18,4 @@ dependencies: - ipython - jupyter - matplotlib + - pandoc # needed for nbsphinx to process Jupyter notebooks #TODO add to docs env only diff --git a/tests/test_notebooks.py b/tests/test_notebooks.py index 561c957..d194f45 100644 --- a/tests/test_notebooks.py +++ b/tests/test_notebooks.py @@ -40,8 +40,13 @@ def test_func(self): print('Captured Output: \n\n{0}'.format(err.decode("utf-8"))) else: print('\n ..... {0} Passed ..... \n'.format(nbname)) - # if passed remove the generated html file - subprocess.call(['rm', str(nbpath.with_suffix('.html'))]) + # if passed remove the generated html file (cross-platform) + try: + Path(nbpath.with_suffix('.html')).unlink(missing_ok=True) + except TypeError: + p = Path(nbpath.with_suffix('.html')) + if p.exists(): + p.unlink() self.assertFalse(failed)