Skip to content

Fixed a NumPy 2.x testing issue and added a NumPy 2.x build to the test workflow #1705

Fixed a NumPy 2.x testing issue and added a NumPy 2.x build to the test workflow

Fixed a NumPy 2.x testing issue and added a NumPy 2.x build to the test workflow #1705

# Run Tests
# Matrix Options
# DOCS: 2-Build and publish, 1-build only, 0-no docs
name: Dymos Tests
on:
# Trigger on push, pull request
push:
branches: [ master ]
pull_request:
branches: [ master, develop ]
# Allow running the workflow manually from the Actions tab
# All jobs are excluded by default, desired jobs must be selected
workflow_dispatch:
inputs:
run_name:
type: string
description: 'Name of workflow run as it will appear under Actions tab:'
required: false
default: ""
baseline:
type: boolean
description: "Include 'baseline' in test matrix"
required: false
default: false
no_pyoptsparse:
type: boolean
description: "Include 'no_pyoptsparse' in test matrix"
required: false
default: false
numpy2_no_pyoptsparse:
type: boolean
description: "Include 'numpy2_no_pyoptsparse' in test matrix"
required: false
default: false
no_snopt:
type: boolean
description: "Include 'no_snopt' in test matrix"
required: false
default: false
no_mpi:
type: boolean
description: "Include 'no_mpi' Baseline in test matrix"
required: false
default: false
latest:
type: boolean
description: "Include 'latest' in test matrix"
required: false
default: false
oldest:
type: boolean
description: "Include 'oldest' in test matrix"
required: false
default: false
run-name: ${{ inputs.run_name }}
jobs:
test_ubuntu:
runs-on: ubuntu-latest
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
include:
# baseline versions
- NAME: baseline
PY: '3.12'
NUMPY: 1.26
SCIPY: 1.13
PETSc: 3.21.0
PYOPTSPARSE: 'v2.11.0'
SNOPT: 7.7
OPENMDAO: 'latest'
PEP517: true
OPTIONAL: '[all]'
JAX: '0.4.28'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.baseline }}
# baseline versions except no pyoptsparse or SNOPT
- NAME: no_pyoptsparse
PY: '3.12'
NUMPY: 1.26
SCIPY: 1.13
PETSc: 3.21.0
OPENMDAO: 'latest'
OPTIONAL: '[test]'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.no_pyoptsparse }}
# numpy 2.x but no PETSc, pyoptsparse or SNOPT
- NAME: numpy2_no_pyoptsparse
PY: '3.12'
NUMPY: 2
SCIPY: 1.14
OPENMDAO: 'latest'
OPTIONAL: '[all]'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.numpy2_no_pyoptsparse }}
# baseline versions except with pyoptsparse but no SNOPT
- NAME: no_snopt
PY: '3.12'
NUMPY: 1.26
SCIPY: 1.13
PETSc: 3.21.0
PYOPTSPARSE: 'v2.11.0'
OPENMDAO: 'latest'
OPTIONAL: '[test]'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.no_snopt }}
# baseline versions except no MPI/PETSc
- NAME: no_mpi
PY: '3.12'
NUMPY: 1.26
SCIPY: 1.13
PYOPTSPARSE: 'v2.11.0'
OPENMDAO: 'latest'
OPTIONAL: '[test]'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.no_mpi }}
# try latest versions
- NAME: latest
PY: 3
NUMPY: 1
SCIPY: 1
PETSc: 3.21.0
PYOPTSPARSE: 'latest'
SNOPT: 7.7
OPENMDAO: 'dev'
OPTIONAL: '[test]'
JAX: 'latest'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.latest }}
# oldest supported versions
- NAME: oldest
PY: 3.9
NUMPY: 1.22
SCIPY: 1.7
OPENMPI: '4.0'
MPI4PY: '3.0'
PETSc: 3.13
PYOPTSPARSE: 'v2.9.0'
SNOPT: 7.2
OPENMDAO: 3.28.0
MATPLOTLIB: 3.5
OPTIONAL: '[test]'
EXCLUDE: ${{ github.event_name == 'workflow_dispatch' && ! inputs.oldest }}
steps:
- name: Display run details
run: |
echo "============================================================="
echo "Run #${GITHUB_RUN_NUMBER}"
echo "Run ID: ${GITHUB_RUN_ID}"
echo "Testing: ${GITHUB_REPOSITORY}"
echo "Triggered by: ${GITHUB_EVENT_NAME}"
echo "Initiated by: ${GITHUB_ACTOR}"
echo "============================================================="
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ "${{ matrix.EXCLUDE }}" == "false" ]]; then
echo "Triggered by 'workflow_dispatch', '${{ matrix.NAME }}' build was selected and will run."
else
echo "Triggered by 'workflow_dispatch', '${{ matrix.NAME }}' build not selected and will not run."
fi
fi
- name: Create SSH key
if: ${{ ! matrix.EXCLUDE }}
shell: bash
env:
SSH_PRIVATE_KEY: ${{secrets.SSH_PRIVATE_KEY}}
SSH_KNOWN_HOSTS: ${{secrets.SSH_KNOWN_HOSTS}}
run: |
mkdir -p ~/.ssh/
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
sudo chmod 600 ~/.ssh/id_rsa
echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- name: Checkout code
if: ${{ ! matrix.EXCLUDE }}
uses: actions/checkout@v4
- name: Setup conda
if: ${{ ! matrix.EXCLUDE }}
uses: conda-incubator/setup-miniconda@v3
with:
python-version: ${{ matrix.PY }}
channels: conda-forge
- name: Install Numpy/Scipy
if: ${{ ! matrix.EXCLUDE }}
shell: bash -l {0}
run: |
echo "Make sure we are not using anaconda packages"
conda config --remove channels defaults
echo "============================================================="
echo "Install Numpy/Scipy"
echo "============================================================="
conda install numpy=${{ matrix.NUMPY }} scipy=${{ matrix.SCIPY }} -q -y
- name: Install jax
if: ${{ ! matrix.EXCLUDE && matrix.JAX }}
shell: bash -l {0}
run: |
echo "============================================================="
echo "Install jax"
echo "============================================================="
if [[ "${{ matrix.JAX }}" == "latest" ]]; then
python -m pip install jaxlib jax
else
python -m pip install jaxlib==${{ matrix.JAX }} jax==${{ matrix.JAX }}
fi
- name: Install PETSc
if: ${{ ! matrix.EXCLUDE && matrix.PETSc }}
shell: bash -l {0}
run: |
echo "============================================================="
echo "Install PETSc"
echo "============================================================="
if [[ "${{ matrix.OPENMPI }}" && "${{ matrix.MPI4PY }}" ]]; then
conda install openmpi=${{ matrix.OPENMPI }} mpi4py=${{ matrix.MPI4PY }} petsc4py=${{ matrix.PETSc }} -q -y
elif [[ "${{ matrix.MPI4PY }}" ]]; then
conda install mpi4py=${{ matrix.MPI4PY }} petsc4py=${{ matrix.PETSc }} -q -y
else
conda install mpi4py petsc4py=${{ matrix.PETSc }} -q -y
fi
export OMPI_MCA_rmaps_base_oversubscribe=1
export OMPI_MCA_btl=^openib
echo "OMPI_MCA_rmaps_base_oversubscribe=1" >> $GITHUB_ENV
echo "OMPI_MCA_btl=^openib" >> $GITHUB_ENV
echo "-----------------------"
echo "Quick test of mpi4py:"
mpirun -n 2 python -c "from mpi4py import MPI; print(f'Rank: {MPI.COMM_WORLD.rank}')"
echo "-----------------------"
echo "Quick test of petsc4py:"
mpirun -n 2 python -c "import numpy; from mpi4py import MPI; comm = MPI.COMM_WORLD; import petsc4py; petsc4py.init(); x = petsc4py.PETSc.Vec().createWithArray(numpy.ones(5)*comm.rank, comm=comm); print(x.getArray())"
echo "-----------------------"
- name: Install pyOptSparse
id: build_pyoptsparse
if: ${{ ! matrix.EXCLUDE && matrix.PYOPTSPARSE }}
shell: bash -l {0}
run: |
echo "============================================================="
echo "Install pyoptsparse"
echo "============================================================="
if [[ "${{ matrix.PYOPTSPARSE }}" == "conda-forge" ]]; then
if [[ "${{ matrix.SNOPT }}" ]]; then
echo "SNOPT ${{ matrix.SNOPT }} was requested but is not available on conda-forge"
fi
conda install -c conda-forge pyoptsparse
else
pip install git+https://github.com/OpenMDAO/build_pyoptsparse
if [[ "${{ matrix.PYOPTSPARSE }}" == "latest" ]]; then
LATEST_URL=`curl -fsSLI -o /dev/null -w %{url_effective} https://github.com/mdolab/pyoptsparse/releases/latest`
LATEST_VER=`echo $LATEST_URL | awk '{split($0,a,"/tag/"); print a[2]}'`
BRANCH="-b $LATEST_VER"
else
BRANCH="-b ${{ matrix.PYOPTSPARSE }}"
fi
if [[ "${{ matrix.PAROPT }}" ]]; then
PAROPT="-a"
fi
if [[ "${{ matrix.SNOPT }}" == "7.7" && "${{ secrets.SNOPT_LOCATION_77 }}" ]]; then
echo " > Secure copying SNOPT 7.7 over SSH"
mkdir SNOPT
scp -qr ${{ secrets.SNOPT_LOCATION_77 }} SNOPT
SNOPT="-s SNOPT/src"
elif [[ "${{ matrix.SNOPT }}" == "7.2" && "${{ secrets.SNOPT_LOCATION_72 }}" ]]; then
echo " > Secure copying SNOPT 7.2 over SSH"
mkdir SNOPT
scp -qr ${{ secrets.SNOPT_LOCATION_72 }} SNOPT
SNOPT="-s SNOPT/source"
elif [[ "${{ matrix.SNOPT }}" ]]; then
echo "SNOPT version ${{ matrix.SNOPT }} was requested but source is not available"
fi
build_pyoptsparse $BRANCH $PAROPT $SNOPT
fi
- name: Install OpenMDAO
if: ${{ ! matrix.EXCLUDE && matrix.OPENMDAO }}
shell: bash -l {0}
run: |
echo "============================================================="
echo "Install OpenMDAO"
echo "============================================================="
if [[ "${{ matrix.OPENMDAO }}" == "dev" ]]; then
pip install git+https://github.com/OpenMDAO/OpenMDAO
elif [[ "${{ matrix.OPENMDAO }}" == "latest" ]]; then
echo "The latest version OpenMDAO will be installed from pypi per the Dymos dependency"
else
pip install openmdao==${{ matrix.OPENMDAO }}
fi
- name: Install Matplotlib
if: ${{ ! matrix.EXCLUDE && matrix.MATPLOTLIB }}
shell: bash -l {0}
run: |
echo "============================================================="
echo "Install requested version of Matpltlib"
echo "============================================================="
python -m pip install matplotlib==${{ matrix.MATPLOTLIB }}
- name: Install Dymos
if: ${{ ! matrix.EXCLUDE }}
shell: bash -l {0}
run: |
echo "============================================================="
echo "Install Dymos"
echo "============================================================="
if [[ "${{ matrix.PEP517 }}" == "true" ]]; then
pip wheel --no-deps --use-pep517 .
WHEEL=`find dymos-*.whl`
echo "-----------------------------------------------------------"
echo "Installing from wheel: $WHEEL"
echo "-----------------------------------------------------------"
python -m pip install $WHEEL${{ matrix.OPTIONAL }}
else
python -m pip install .${{ matrix.OPTIONAL }}
fi
- name: Install optional dependencies
if: ${{ ! matrix.EXCLUDE && matrix.OPTIONAL == '[all]' }}
run: |
echo "============================================================="
echo "Install additional packages for testing/coverage"
echo "============================================================="
echo "Pre-install playwright dependencies to avoid 'Playwright Host validation warning'"
pip install playwright
playwright install --with-deps
- name: Display environment info
id: env_info
if: ${{ ! matrix.EXCLUDE }}
shell: bash -l {0}
run: |
conda info
conda list
conda env export --name test --file ${{ matrix.NAME }}_environment.yml
echo "============================================================="
echo "Check installed versions of Python, Numpy and Scipy"
echo "============================================================="
python -c "ver='${{ matrix.PY }}'; import sys; assert str(sys.version).startswith(ver), \
f'Python version {sys.version} is not the requested version ({ver})'"
python -c "ver='${{ matrix.NUMPY }}'; import numpy; assert str(numpy.__version__).startswith(ver), \
f'Numpy version {numpy.__version__} is not the requested version ({ver})'"
python -c "ver='${{ matrix.SCIPY }}'; import scipy; assert str(scipy.__version__).startswith(ver), \
f'Scipy version {scipy.__version__} is not the requested version ({ver})'"
- name: Display dependency tree
if: failure() && steps.env_info.outcome == 'failure'
run: |
pip install pipdeptree
pipdeptree
- name: Upload environment artifact
if: ${{ ! matrix.EXCLUDE }}
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.NAME }}_environment
path: ${{ matrix.NAME }}_environment.yml
retention-days: 5
- name: Check NumPy 2.0 Compatibility
if: ${{ ! matrix.EXCLUDE }}
run: |
echo "============================================================="
echo "Check Dymos code for NumPy 2.0 compatibility"
echo "See: https://numpy.org/devdocs/numpy_2_0_migration_guide.html"
echo "============================================================="
python -m pip install ruff
ruff check . --select NPY201
- name: Perform linting with Ruff
if: ${{ ! matrix.EXCLUDE && matrix.NAME == 'baseline' }}
run: |
echo "============================================================="
echo "Lint Dymos code per settings in pyproject.toml"
echo "============================================================="
python -m pip install ruff
ruff check . --config pyproject.toml
- name: Run tests
if: ${{ ! matrix.EXCLUDE }}
env:
DYMOS_CHECK_PARTIALS: True
shell: bash -l {0}
run: |
echo "============================================================="
echo "Run Tests"
echo "Environment:"
echo " DYMOS_CHECK_PARTIALS: $DYMOS_CHECK_PARTIALS"
echo "============================================================="
testflo -n 1 docs/dymos_book/test --pre_announce
testflo -n 1 joss/test --pre_announce
testflo -b benchmark --pre_announce
cd $HOME
if [[ "${{ matrix.NAME }}" != "latest" ]]; then
testflo dymos -n 2 --pre_announce --show_skipped --durations 20 --coverage --coverpkg dymos
else
testflo dymos -n 2 --pre_announce --show_skipped --durations 20
fi
- name: Submit coverage
if: github.event_name != 'workflow_dispatch'
continue-on-error: true
shell: bash -l {0}
env:
COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
COVERALLS_SERVICE_NAME: "github"
COVERALLS_PARALLEL: True
run: |
echo "============================================================="
echo "Submit coverage"
echo "============================================================="
cp $HOME/.coverage .
pip install coveralls
SITE_DIR=`python -c 'import site; print(site.getsitepackages()[-1])'`
coveralls --basedir $SITE_DIR
coveralls:
name: Finish coveralls
if: github.event_name != 'workflow_dispatch'
continue-on-error: true
needs: test_ubuntu
runs-on: ubuntu-latest
steps:
- uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true