diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml index 96aaea347..a022a6634 100644 --- a/.github/workflows/black.yml +++ b/.github/workflows/black.yml @@ -8,17 +8,27 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v6 + + - name: Get changed Python files + id: changed-py-files + uses: tj-actions/changed-files@v47 + with: + files: | + **.py - name: Set up Python - uses: actions/setup-python@v2 + if: steps.changed-py-files.outputs.any_changed == 'true' + uses: actions/setup-python@v6 with: python-version: "3.11" - name: Install Black + if: steps.changed-py-files.outputs.any_changed == 'true' run: | python -m pip install --upgrade pip pip install black - name: Check Black Formatting - run: black --check . + if: steps.changed-py-files.outputs.any_changed == 'true' + run: black --check ${{ steps.changed-py-files.outputs.all_changed_files }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ba7925a8f..48c6f83ec 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -10,7 +10,25 @@ on: - main - develop +# Global configuration for all jobs. Can be overridden at the job level if needed. +env: + # Conda channels: Set conda forge as only channel and disable defaults channel + CONDA_MINICONDA_VERSION: latest + CONDA_AUTO_UPDATE_CONDA: true + CONDA_CHANNELS: conda-forge + CONDA_REMOVE_DEFAULTS: true + CONDA_USE_ONLY_TAR_BZ2: false + CONDA_ENVIRONMENT_FILE: environment-dev.yml + CONDA_ENVIRONMENT_NAME: mhkit-dev-env + # Coveralls: Set to false to ignore upload failures during outages. + # Check status: https://status.coveralls.io/ + COVERALLS_FAIL_ON_ERROR: true + jobs: + # Set the operating system matrix for the following jobs/tests + # conda-forge-build + # pip-build + # hindcast-calls set-os: runs-on: ubuntu-latest outputs: @@ -24,6 +42,8 @@ jobs: echo "matrix_os=[\"windows-latest\", \"ubuntu-latest\", \"macos-latest\"]" >> $GITHUB_OUTPUT fi + + # This job decides if the hindcast test suite should run. check-changes: runs-on: ubuntu-latest outputs: @@ -31,11 +51,11 @@ jobs: should-run-hindcast: ${{ steps.hindcast-logic.outputs.should-run-hindcast }} steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Check for changes in wave/io/hindcast id: changes - uses: dorny/paths-filter@v3 + uses: dorny/paths-filter@v4 with: filters: | wave_io_hindcast: @@ -56,45 +76,39 @@ jobs: PYTHON_VER: 3.11 steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: 'latest' - auto-update-conda: true - python-version: ${{ env.PYTHON_VER }} - activate-environment: TESTconda - use-only-tar-bz2: false - - - name: Create MHKiT Conda environment - shell: bash -l {0} - run: | - conda env create -f environment.yml - conda activate mhkit-env + miniconda-version: ${{ env.CONDA_MINICONDA_VERSION }} + auto-update-conda: ${{ env.CONDA_AUTO_UPDATE_CONDA }} + environment-file: ${{ env.CONDA_ENVIRONMENT_FILE }} + activate-environment: ${{ env.CONDA_ENVIRONMENT_NAME }} + python-version: ${{ matrix.python-version }} + use-only-tar-bz2: ${{ env.CONDA_USE_ONLY_TAR_BZ2 }} + channels: ${{ env.CONDA_CHANNELS }} + conda-remove-defaults: ${{ env.CONDA_REMOVE_DEFAULTS }} - name: Install testing dependencies shell: bash -l {0} run: | - conda activate mhkit-env conda install -y pytest coverage coveralls - name: Install mhkit shell: bash -l {0} run: | - conda activate mhkit-env - pip install -e ".[all,dev]" --no-deps + pip install -e ".[all,dev]" --no-deps - name: Prepare non-hindcast API data shell: bash -l {0} run: | - conda activate mhkit-env pytest mhkit/tests/river/test_io_usgs.py pytest mhkit/tests/tidal/test_io.py pytest mhkit/tests/wave/io/test_cdip.py - name: Upload data as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: data path: ~/.cache/mhkit @@ -107,43 +121,37 @@ jobs: if: (needs.check-changes.outputs.should-run-hindcast == 'true') steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: 'latest' - auto-update-conda: true - activate-environment: TEST - python-version: ${{ env.PYTHON_VER }} - use-only-tar-bz2: false - - - name: Create MHKiT Conda environment - shell: bash -l {0} - run: | - conda env create -f environment.yml - conda activate mhkit-env + miniconda-version: ${{ env.CONDA_MINICONDA_VERSION }} + auto-update-conda: ${{ env.CONDA_AUTO_UPDATE_CONDA }} + environment-file: ${{ env.CONDA_ENVIRONMENT_FILE }} + activate-environment: ${{ env.CONDA_ENVIRONMENT_NAME }} + python-version: ${{ matrix.python-version }} + use-only-tar-bz2: ${{ env.CONDA_USE_ONLY_TAR_BZ2 }} + channels: ${{ env.CONDA_CHANNELS }} + conda-remove-defaults: ${{ env.CONDA_REMOVE_DEFAULTS }} - name: Install testing dependencies shell: bash -l {0} run: | - conda activate mhkit-env conda install -y pytest coverage coveralls - name: Install mhkit shell: bash -l {0} run: | - conda activate mhkit-env - pip install -e ".[all,dev]" --no-deps + pip install -e ".[all,dev]" --no-deps - name: Prepare Wave Hindcast data shell: bash -l {0} run: | - conda activate mhkit-env pytest mhkit/tests/wave/io/hindcast/test_hindcast.py - name: Upload Wave Hindcast data as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: wave-hindcast-data path: ~/.cache/mhkit @@ -156,49 +164,43 @@ jobs: if: (needs.check-changes.outputs.should-run-hindcast == 'true') steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup Miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: 'latest' - auto-update-conda: true - activate-environment: TEST - python-version: ${{ env.PYTHON_VER }} - use-only-tar-bz2: false - - - name: Create MHKiT Conda environment - shell: bash -l {0} - run: | - conda env create -f environment.yml - conda activate mhkit-env + miniconda-version: ${{ env.CONDA_MINICONDA_VERSION }} + auto-update-conda: ${{ env.CONDA_AUTO_UPDATE_CONDA }} + environment-file: ${{ env.CONDA_ENVIRONMENT_FILE }} + activate-environment: ${{ env.CONDA_ENVIRONMENT_NAME }} + python-version: ${{ matrix.python-version }} + use-only-tar-bz2: ${{ env.CONDA_USE_ONLY_TAR_BZ2 }} + channels: ${{ env.CONDA_CHANNELS }} + conda-remove-defaults: ${{ env.CONDA_REMOVE_DEFAULTS }} - name: Install testing dependencies shell: bash -l {0} run: | - conda activate mhkit-env conda install -y pytest coverage coveralls - name: Install mhkit shell: bash -l {0} run: | - conda activate mhkit-env - pip install -e ".[all,dev]" --no-deps + pip install -e ".[all,dev]" --no-deps - name: Prepare Wind Hindcast data shell: bash -l {0} run: | - conda activate mhkit-env pytest mhkit/tests/wave/io/hindcast/test_wind_toolkit.py - name: Upload Wind Hindcast data as artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: wind-hindcast-data path: ~/.cache/mhkit - conda-build: - name: conda-${{ matrix.os }}/${{ matrix.python-version }} + conda-forge-build: + name: conda-forge-${{ matrix.os }}/${{ matrix.python-version }} needs: [set-os, prepare-nonhindcast-cache] runs-on: ${{ matrix.os }} strategy: @@ -210,50 +212,50 @@ jobs: PYTHON_VER: ${{ matrix.python-version }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup Miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: 'latest' - auto-update-conda: true - environment-file: environment.yml - activate-environment: TEST + miniconda-version: ${{ env.CONDA_MINICONDA_VERSION }} + auto-update-conda: ${{ env.CONDA_AUTO_UPDATE_CONDA }} + environment-file: ${{ env.CONDA_ENVIRONMENT_FILE }} + activate-environment: ${{ env.CONDA_ENVIRONMENT_NAME }} python-version: ${{ matrix.python-version }} - use-only-tar-bz2: false - - - name: Create MHKiT Conda environment - shell: bash -l {0} - run: | - conda env create -f environment.yml - conda activate mhkit-env + use-only-tar-bz2: ${{ env.CONDA_USE_ONLY_TAR_BZ2 }} + channels: ${{ env.CONDA_CHANNELS }} + conda-remove-defaults: ${{ env.CONDA_REMOVE_DEFAULTS }} - name: Install testing dependencies shell: bash -l {0} run: | - conda activate mhkit-env conda install -y pytest coverage coveralls - name: Install mhkit shell: bash -l {0} run: | - conda activate mhkit-env - pip install -e . --no-deps + pip install -e ".[all,dev]" --no-deps + + - name: Download data from artifact + uses: actions/download-artifact@v4 + with: + name: data + path: ~/.cache/mhkit - name: Run pytest & generate coverage report shell: bash -l {0} run: | - conda activate mhkit-env coverage run --rcfile=.github/workflows/.coveragerc --source=./mhkit/ -m pytest -c .github/workflows/pytest.ini coverage lcov - name: Upload coverage data to coveralls.io - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} - flag-name: conda-${{ runner.os }}-py${{ matrix.python-version }} + flag-name: conda-forge-${{ runner.os }}-py${{ matrix.python-version }} parallel: true path-to-lcov: ./coverage.lcov + fail-on-error: ${{ env.COVERALLS_FAIL_ON_ERROR }} pip-build: name: pip-${{ matrix.os }}/${{ matrix.python-version }} @@ -266,15 +268,22 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - - uses: conda-incubator/setup-miniconda@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} + - name: Setup Python Virtual Environment + shell: bash + # The second command puts the venv bin directory on the PATH for + # subsequent steps in the job, which ensures that the correct Python + # version and installed packages are used. + run: | + python -m venv venv + echo "$PWD/venv/bin" >> $GITHUB_PATH + - name: Set up Git repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Download data from artifact uses: actions/download-artifact@v4 @@ -284,35 +293,43 @@ jobs: - name: Install system dependencies if: runner.os == 'Linux' - run: sudo apt-get install -y libhdf5-dev libnetcdf-dev + run: | + # Update apt-get cache + sudo apt-get update + sudo apt-get install -y libhdf5-dev libnetcdf-dev - name: Update and install packages - shell: bash -l {0} + shell: bash run: | python -m pip install --upgrade pip wheel - pip install -e ".[all,dev]" + pip install -e ".[all,dev]" - name: Reinstall h5py and netCDF4 with system libraries if: runner.os == 'Linux' - run: "pip install --force-reinstall --no-binary=:all: h5py netCDF4" + shell: bash + run: | + pip install --force-reinstall --no-binary=:all: h5py netCDF4 - name: Install setuptools for Python 3.12 if: matrix.python-version == '3.12' - run: pip install setuptools + shell: bash + run: | + pip install setuptools - name: Run pytest & generate coverage report - shell: bash -l {0} + shell: bash run: | coverage run --rcfile=.github/workflows/.coveragerc --source=./mhkit/ -m pytest -c .github/workflows/pytest.ini coverage lcov - name: Upload coverage data to coveralls.io - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} flag-name: pip-${{ runner.os }}-py${{ matrix.python-version }} parallel: true path-to-lcov: ./coverage.lcov + fail-on-error: ${{ env.COVERALLS_FAIL_ON_ERROR }} hindcast-calls: name: hindcast-${{ matrix.os }}/${{ matrix.python-version }} @@ -334,35 +351,29 @@ jobs: python-version: ['3.10', '3.11', '3.12'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Setup Miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: 'latest' - auto-update-conda: true - environment-file: environment.yml - activate-environment: TEST + miniconda-version: ${{ env.CONDA_MINICONDA_VERSION }} + auto-update-conda: ${{ env.CONDA_AUTO_UPDATE_CONDA }} + environment-file: ${{ env.CONDA_ENVIRONMENT_FILE }} + activate-environment: ${{ env.CONDA_ENVIRONMENT_NAME }} python-version: ${{ matrix.python-version }} - use-only-tar-bz2: false - - - name: Create MHKiT Conda environment - shell: bash -l {0} - run: | - conda env create -f environment.yml - conda activate mhkit-env + use-only-tar-bz2: ${{ env.CONDA_USE_ONLY_TAR_BZ2 }} + channels: ${{ env.CONDA_CHANNELS }} + conda-remove-defaults: ${{ env.CONDA_REMOVE_DEFAULTS }} - name: Install testing dependencies shell: bash -l {0} run: | - conda activate mhkit-env conda install -y pytest coverage coveralls - name: Install mhkit shell: bash -l {0} run: | - conda activate mhkit-env - pip install -e ".[all,dev]" --no-deps + pip install -e ".[all,dev]" --no-deps - name: Download Wave Hindcast data from artifact uses: actions/download-artifact@v4 @@ -386,25 +397,25 @@ jobs: - name: Run hindcast pytest shell: bash -l {0} run: | - conda activate mhkit-env coverage run --rcfile=.github/workflows/.coveragehindcastrc -m pytest -c .github/workflows/pytest-hindcast.ini coverage lcov - name: Upload coverage data to coveralls.io - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} flag-name: hindcast-${{ runner.os }}-py${{ matrix.python-version }} parallel: true path-to-lcov: ./coverage.lcov + fail-on-error: ${{ env.COVERALLS_FAIL_ON_ERROR }} test-wheel-packaging: name: Test Built Wheel runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.11' @@ -465,10 +476,10 @@ jobs: python-version: ['3.12'] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} @@ -480,7 +491,10 @@ jobs: - name: Install system dependencies if: matrix.module != 'river' || matrix.module != 'power' || matrix.module != 'utils' || matrix.module != 'loads' - run: sudo apt-get install -y libhdf5-dev libnetcdf-dev + run: | + # Update apt-get cache + sudo apt-get update + sudo apt-get install -y libhdf5-dev libnetcdf-dev - name: Install MHKiT with optional dependency run: | @@ -514,7 +528,7 @@ jobs: needs.prepare-nonhindcast-cache.result == 'success' && needs.prepare-wave-hindcast-cache.result == 'skipped' && needs.prepare-wind-hindcast-cache.result == 'skipped' && - needs.check-changes.outputs.should-run-hindcast == 'false' + needs.check-changes.outputs.should-run-hindcast == 'false' ) || ( needs.prepare-nonhindcast-cache.result == 'success' && @@ -527,10 +541,10 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v6 with: python-version: '3.11' @@ -556,35 +570,29 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - name: Set up Miniconda + - name: Setup Miniconda uses: conda-incubator/setup-miniconda@v3 with: - miniconda-version: 'latest' - auto-update-conda: true - python-version: '3.12' - channels: conda-forge - activate-environment: TESTconda - use-only-tar-bz2: false - - - name: Create MHKiT Conda environment - shell: bash -l {0} - run: | - conda env create -f environment.yml - conda activate mhkit-env + miniconda-version: ${{ env.CONDA_MINICONDA_VERSION }} + auto-update-conda: ${{ env.CONDA_AUTO_UPDATE_CONDA }} + environment-file: ${{ env.CONDA_ENVIRONMENT_FILE }} + activate-environment: ${{ env.CONDA_ENVIRONMENT_NAME }} + python-version: ${{ matrix.python-version }} + use-only-tar-bz2: ${{ env.CONDA_USE_ONLY_TAR_BZ2 }} + channels: ${{ env.CONDA_CHANNELS }} + conda-remove-defaults: ${{ env.CONDA_REMOVE_DEFAULTS }} - name: Install notebook testing dependencies shell: bash -l {0} run: | - conda activate mhkit-env conda install -y pytest coverage coveralls nbval jupyter utm folium - name: Install mhkit shell: bash -l {0} run: | - conda activate mhkit-env - pip install -e ".[all,dev]" --no-deps + pip install -e ".[all,dev]" --no-deps - name: Download non-hindcast data uses: actions/download-artifact@v4 @@ -622,7 +630,6 @@ jobs: - name: Run notebook shell: bash -l {0} run: | - conda activate mhkit-env if [[ "${{ matrix.notebook }}" == "examples/metocean_example.ipynb" || "${{ matrix.notebook }}" == "examples/WPTO_hindcast_example.ipynb" ]]; then if [[ "${{ needs.check-changes.outputs.should-run-hindcast }}" == 'true' ]]; then jupyter nbconvert --to notebook --execute --inplace --ExecutePreprocessor.timeout=${{ matrix.timeout }} "${{ matrix.notebook }}" @@ -639,7 +646,7 @@ jobs: [ prepare-wave-hindcast-cache, prepare-wind-hindcast-cache, - conda-build, + conda-forge-build, pip-build, hindcast-calls, ] @@ -647,14 +654,14 @@ jobs: always() && ( ( - needs.conda-build.result == 'success' && + needs.conda-forge-build.result == 'success' && needs.pip-build.result == 'success' && needs.prepare-wave-hindcast-cache.result == 'skipped' && needs.prepare-wind-hindcast-cache.result == 'skipped' && needs.hindcast-calls.result == 'skipped' ) || ( - needs.conda-build.result == 'success' && + needs.conda-forge-build.result == 'success' && needs.pip-build.result == 'success' && needs.prepare-wave-hindcast-cache.result == 'success' && needs.prepare-wind-hindcast-cache.result == 'success' && @@ -662,11 +669,10 @@ jobs: ) ) runs-on: ubuntu-latest - container: python:3-slim steps: - name: Coveralls Finished - uses: coverallsapp/github-action@master + uses: coverallsapp/github-action@v2 with: parallel-finished: true - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.GITHUB_TOKEN }} + fail-on-error: ${{ env.COVERALLS_FAIL_ON_ERROR }} diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 27d52e520..6c85b7c67 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -8,10 +8,10 @@ jobs: steps: - name: Check out code - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v6 with: python-version: '3.11' diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 9cc2d2e05..766251d91 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -19,11 +19,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.10' diff --git a/.github/workflows/test-wheel-build.yml b/.github/workflows/test-wheel-build.yml index 8186eff54..dd3ed5977 100644 --- a/.github/workflows/test-wheel-build.yml +++ b/.github/workflows/test-wheel-build.yml @@ -16,14 +16,14 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ['3.11', '3.12'] + python-version: ['3.10', '3.11', '3.12'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v6 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} diff --git a/LICENSE.md b/LICENSE.md index 5b6698e8b..009012157 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ BSD 3-Clause License -Copyright (c) 2019, Alliance for Energy Innovation, LLC under the terms of Contract DE-AC36-08GO28308, Battelle Memorial Institute under the terms of Contract DE-AC05-76RL01830, and National Technology & Engineering Solutions of Sandia, LLC under the terms of Contract DE-NA0003525. The U.S. Government retains certain rights in this software. +Copyright (c) 2026, Alliance for Energy Innovation, LLC under the terms of Contract DE-AC36-08GO28308, Battelle Memorial Institute under the terms of Contract DE-AC05-76RL01830, and National Technology & Engineering Solutions of Sandia, LLC under the terms of Contract DE-NA0003525. The U.S. Government retains certain rights in this software. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index a95595db5..8813a3041 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@

-MHKiT-Python is a Python package designed for marine renewable energy applications to assist in +MHKiT-Python is a Python package designed for marine energy applications to assist in data processing and visualization. The software package include functionality for: - Data processing @@ -33,55 +33,199 @@ See the [MHKiT documentation](https://mhkit-software.github.io/MHKiT) for more i ## Installation [MHKiT-Python](https://github.com/MHKiT-Software/MHKiT-Python) requires [Python (3.10-3.12)](https://www.python.org/). -It is recommended to use the [Anaconda Python Distribution](https://www.anaconda.com/distribution/) (a fully featured Python installer with a GUI) -or [Miniconda](https://docs.anaconda.com/miniconda/#quick-command-line-install) (a lightweight installer with the ``conda`` command line utility). -Both will include most of MHKiT-Python's package dependencies. +It is recommended to use the [Anaconda Python Distribution](https://www.anaconda.com/distribution/) (a fully featured Python installer with a GUI) +or [Miniconda](https://docs.anaconda.com/miniconda/#quick-command-line-install) (a lightweight installer with the `conda` command line utility). +Both will include most of MHKiT-Python's package dependencies. + MHKiT can be installed several ways: -### Option 1: Install from Python +### Option 1: Install With Anaconda/Miniconda + +This option is recommended for most MHKiT-Python users. +To install MHKiT-Python using `conda`, in an Anaconda Prompt: + +```bash +conda env create mhkit-env --python=3.11 +``` -This option is recommended as a fast installation for MHKiT-Python users. -To install MHKiT-Python using ``conda``, in an Anaconda Prompt: +```bash +conda activate mhkit-env +``` ```bash - conda install -c conda-forge mhkit +conda install -c conda-forge mhkit ``` +Optional: Installing dependencies to run mhkit examples and development dependencies: + +```bash +pip install mhkit["examples"] +``` + +Note: To use the above installed version of MHKiT-Python users must activate the `mhkit-env` environment each time, using `conda activate +mhkit-env` in each new shell/terminal to use MHKiT. +To avoid this, users can install MHKiT into their base conda environment, but this is not +recommended as it may cause dependency conflicts with other software. + +Visual Studio Code has [instructions for using Python environments in VS Code](https://code.visualstudio.com/docs/python/environments) that support conda environment discovery. + ### Option 2: Clone Repository from GitHub This option is recommended for MHKiT-Python users who want access to example notebooks and developers. + +#### Step 1: Clone Repository + Download and install your preferred version of [git](https://git-scm.com/). To clone MHKiT-Python: ```bash - git clone https://github.com/MHKiT-Software/MHKiT-Python - cd MHKiT-Python +git clone https://github.com/MHKiT-Software/MHKiT-Python +``` + +```bash +cd MHKiT-Python +``` + +#### Step 2: Setup a Virtual Environment + +A virtual environment is a self-contained directory that contains a Python installation for a +particular version of Python, plus a number of additional packages. Using a virtual environment +allows you to manage dependencies for different projects separately, avoiding conflicts between +packages and ensuring that your project has access to the specific versions of packages it needs. + +Use of a virtual environment is recommended to avoid dependency conflicts with other python +packages (this environment must be activated in each new shell/terminal before using MHKiT).: + +##### Option A: Python `venv` + +Using Python venv (built into python) to create a virtual environment: + +###### Windows + +Note: A supported version of Python ([see installation for supported versions](#installation)) must be installed and added to the system PATH for venv to work in Git Bash or WSL. Use of Git Bash or WSL is recommended for Windows users to avoid issues with activating the virtual environment in Command Prompt or PowerShell. + +```sh +python -m venv mhkit-env +``` + +```sh +.\mhkit-env\Scripts\activate +``` + +Linux/MacOS: + +```bash +python -m venv mhkit-env ``` +```bash +source mhkit-env/bin/activate +``` + +##### Option B: Conda (requires separate installation of Anaconda or Miniconda): + +```bash +conda create -n mhkit-env python=3.11 +``` + +```bash +conda activate mhkit-env +``` + +#### Step 3: Install MHKiT-Python with pip + To install a local, editable version of MHKiT-Python using [pip](https://pip.pypa.io/en/stable/): ```bash - pip install -e .["all"] +pip install -e .["all,dev"] ``` -An [environment YAML file](https://github.com/MHKiT-Software/MHKiT-Python/blob/main/environment.yml) is also provided that can create the base environment required by MHKiT. +An [environment YAML file](https://github.com/MHKiT-Software/MHKiT-Python/blob/main/environment.yml) is also provided that can create the base environment required by MHKiT. MHKiT can then be installed into that environment using any of the provided methods. ### Option 3: Module-specific Install from Python -A slim version of MHKiT-Python can be installed to reduce the number of dependencies and potential conflicts with other software. -This installation utilizes pip's optional dependencies installation. +A slim version of MHKiT-Python can be installed to reduce the number of dependencies and potential conflicts with other software. +This installation utilizes pip's optional dependencies installation. + +Note: Use of a virtual environment is recommended to avoid dependency conflicts with other python. +See Option 2 installation instructions for virtual environment setup. + To install a single MHKiT module, e.g. the wave module, and its dependencies, use: - pip install mhkit["wave"] +```bash +pip install mhkit["wave"] +``` + +Note that `pip install mhkit` does not install any MHKiT dependencies, so users must specify which +module(s) they want to install with the optional dependencies installation syntax, `pip install +mhkit "[]"` or multiple modules with `pip install mhkit "[,]"`. + +Supported modules are: + +- `dolfyn` +- `wave` +- `tidal` +- `river` +- `power` +- `loads` +- `mooring` +- `acoustics` +- `qc` +- `all` (includes the above modules and their dependencies) +- `dev` (includes development dependencies for contributing to MHKiT) +- `examples` (includes dependencies for running MHKiT example notebooks) -Note that ``pip install mhkit`` only installs the base MHKiT dependencies and not the entire software. To install all MHKiT dependencies use: - pip install mhkit["all"] +```bash +pip install mhkit["all"] +``` See [installation instructions](https://mhkit-software.github.io/MHKiT/installation.html) for more information. +### Development Installation + +For developers contributing to MHKiT, there are three development installation strategies after +cloning the repository locally: + +Note: Use of a virtual environment is recommended to avoid dependency conflicts with other python. +See Option 2 installation instructions for virtual environment setup. + +**Pip development** (no conda): + +Setup and activate virtual environment using Python venv using [the instructions above](#python-venv) and then: + +Note: Developers can create multiple virtual environments by changing the name, `python -m venv `, install isolated versions of MHKiT, and switch between them by activating the desired environment. This allows developers to test different versions of MHKiT and its dependencies without conflicts. + +```bash +pip install -e ".[all,dev]" +``` + +**Conda development** (minimal conda + pip resolves deps): + +```bash +conda env create -f environment.yml +``` + +```bash +conda activate mhkit-env +``` + +```bash +pip install -e ".[all,dev]" +``` + +**Conda-forge development** (all deps from conda-forge, mirrors production deployment): + +```bash +conda env create -f environment-dev.yml +conda activate mhkit-env +pip install -e ".[all,dev]" --no-deps +``` + +The conda-forge option mirrors how users install MHKiT via `conda install -c conda-forge mhkit`, ensuring all dependencies come from the conda-forge channel. The `--no-deps` flag prevents pip from resolving dependencies, relying entirely on the conda-forge packages for dependencies. The conda-forge build and deployment happens in separate repository: [https://github.com/conda-forge/mhkit-feedstock] which is updated with each MHKiT release. + ## Copyright and license MHKiT-Python is copyright through the National Laboratory of the Rockies, @@ -114,7 +258,7 @@ The GitHub platform has the Fork feature that facilitates code modification and ## Creating a branch -The GitHub platform has the branch feature that facilitates code contributions and collaboration amongst developers. A branch isolates development work without affecting other branches in the repository. Each repository has one default branch, and can have multiple other branches. To create a branch of your forked MHKiT-Python repository, follow the steps below. More information about GitHub branches can be found [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches) +The GitHub platform has the branch feature that facilitates code contributions and collaboration. A branch isolates development work without affecting other branches in the repository. Each repository has one default branch, and can have multiple other branches. To create a branch of your forked MHKiT-Python repository, follow the steps below. More information about GitHub branches can be found [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-branches) 1. Navigate to your fork of MHKiT-Python (see instructions above) 2. Above the list of files, click **Branches**. @@ -128,13 +272,13 @@ The GitHub platform has the pull request feature that allows you to propose chan 1. Navigate to the [MHKiT-Python main page](https://github.com/MHKiT-Software/MHKiT-Python) 2. Above the list of files, click **Pull request**. -3. On the compare page, click **Compare accross forks**. +3. On the compare page, click **Compare across forks**. 4. In the "base branch" drop-down menu, select the branch of the upstream repository you'd like to merge changes into. 5. In the "head fork" drop-down menu, select your fork, then use the "compare branch" drop-down menu to select the branch you made your changes in. 6. Type a title and description for your pull request. 7. If you want to allow anyone with push access to the upstream repository to make changes to your pull request, select **Allow edits from maintainers**. 8. To create a pull request that is ready for review, click **Create Pull Request**. To create a draft pull request, use the drop-down and select **Create Draft Pull Request**, then click **Draft Pull Request**. More information about draft pull requests can be found [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests) -9. MHKiT-Python adminstrators will review your pull request and contact you if needed. +9. MHKiT-Python administrators will review your pull request and contact you if needed. ## Code Formatting in MHKiT diff --git a/environment-dev.yml b/environment-dev.yml new file mode 100644 index 000000000..958ff6e2c --- /dev/null +++ b/environment-dev.yml @@ -0,0 +1,28 @@ +name: mhkit-dev-env +channels: + - conda-forge + - defaults +dependencies: + - python>=3.10,<3.13 + - pip + - numpy>=2.0.0 + - pandas>=2.2.2,<3.0 + - scipy>=1.14.0 + - xarray>=2024.6.0 + - scikit-learn>=1.5.1 + - h5py>=3.11.0 + - h5pyd>=0.18.0 + - netCDF4>=1.6.5 + - hdf5>=1.14.3,<1.14.5.0a0 + - statsmodels>=0.14.2 + - requests + - beautifulsoup4 + - numexpr>=2.10.0 + - lxml + - bottleneck + - pecos>=0.3.0 + - notebook + - matplotlib>=3.9.1 + - fatpack + - nrel-rex + - cartopy diff --git a/environment.yml b/environment.yml index 884a2e01d..d484f58a8 100644 --- a/environment.yml +++ b/environment.yml @@ -3,26 +3,5 @@ channels: - conda-forge - defaults dependencies: - - python>=3.10,<3.13 + - python=3.11 - pip - - numpy>=2.0.0 - - pandas>=2.2.2 - - scipy>=1.14.0 - - xarray>=2024.6.0 - - scikit-learn>=1.5.1 - - h5py>=3.11.0 - - h5pyd>=0.18.0 - - netCDF4>=1.6.5 - - hdf5>=1.14.3,<1.14.5.0a0 - - statsmodels>=0.14.2 - - requests - - beautifulsoup4 - - numexpr>=2.10.0 - - lxml - - bottleneck - - pecos>=0.3.0 - - notebook - - matplotlib>=3.9.1 - - fatpack - - nrel-rex - - cartopy diff --git a/examples/data/dolfyn/AQD_HR.PRF b/examples/data/dolfyn/AQD_HR.prf similarity index 100% rename from examples/data/dolfyn/AQD_HR.PRF rename to examples/data/dolfyn/AQD_HR.prf diff --git a/mhkit/utils/cache.py b/mhkit/utils/cache.py index c4897c12c..78744dbf8 100644 --- a/mhkit/utils/cache.py +++ b/mhkit/utils/cache.py @@ -119,7 +119,11 @@ def _load_cache(file_extension, cache_filepath): ) elif file_extension == ".pkl": with open(cache_filepath, "rb") as f: - data, metadata = pickle.load(f) + data_dict = pickle.load(f) # some format of 'data' and 'metadata' + if isinstance(data_dict, tuple): + # CDIP and WPTO hindcast call returns size 2 tuple + return data_dict[0], data_dict[1] + data, metadata = data_dict["data"], data_dict["metadata"] return data, metadata @@ -154,7 +158,8 @@ def _write_cache(data, metadata, file_extension, cache_filepath): if os.path.isfile(cache_filepath) and ( cache_content is None or cache_content["data"] is None ): - return _load_cache(file_extension, cache_filepath) + (cache_filepath,) + data, metadata = _load_cache(file_extension, cache_filepath) + return data, metadata, cache_filepath # Store data in cache if provided if cache_content and cache_content["data"] is not None: diff --git a/mhkit/utils/upcrossing.py b/mhkit/utils/upcrossing.py index 7ab06a0ed..c48e18cc5 100644 --- a/mhkit/utils/upcrossing.py +++ b/mhkit/utils/upcrossing.py @@ -93,7 +93,9 @@ def upcrossing(t: np.ndarray, data: np.ndarray) -> np.ndarray: # eliminate zeros zero_mask = data == 0 - data[zero_mask] = 0.5 * np.min(np.abs(data)) + if any(zero_mask): + # avoid double counting zeros by making them small and positive + data[zero_mask] = 0.5 * np.min(np.abs(data)) # zero up-crossings diff = np.diff(np.sign(data)) diff --git a/pyproject.toml b/pyproject.toml index c0c7df262..d30cf3751 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,10 +21,16 @@ classifiers = [ ] requires-python = ">=3.10" dependencies = [ + # MHKiT-Python uses xarray everywhere, which leverages netcdf and h5 behind the + # scenes. xarray, netcdf4, and h5 dependencies should all be synced to avoid + # compatibility issues with individual modules. + "xarray>=2024.6.0", + "netCDF4>=1.7.1.post1", + "h5py>=3.11.0", + "h5pyd>=0.18.0", "numpy>=2.0.0", - "pandas>=2.2.2", + "pandas>=2.2.2,<3.0", "scipy>=1.14.0", - "xarray>=2024.6.0", "matplotlib>=3.9.1", "pecos>=0.3.0", "requests", @@ -35,28 +41,22 @@ dependencies = [ wave = [ "scikit-learn>=1.5.1", "statsmodels>=0.14.2", - "netCDF4>=1.7.1.post1", "pytz", "NREL-rex>=0.2.63", "beautifulsoup4", "bottleneck", - "lxml" + "lxml", ] tidal = [ - "netCDF4>=1.7.1.post1", - "bottleneck" + "bottleneck", ] river = [ - "netCDF4>=1.7.1.post1", "bottleneck", ] dolfyn = [ - "h5py>=3.11.0", - "h5pyd>=0.18.0", - "netCDF4>=1.7.1.post1", "cartopy", ] @@ -91,11 +91,10 @@ dev = [ "pytest-cov", "pre-commit", "coverage", - "coveralls" + "coveralls", + "black", ] - - # Install all optional dependencies all = [ "mhkit[wave]", @@ -119,7 +118,6 @@ examples = [ "utm", "folium", "mhkit[all]", - ] [project.urls] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 83e60c9dd..000000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,4 +0,0 @@ -# requirements-dev.txt -black -pylint -pytest