Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
73c4a37
Actions: Migrate to newer versions of macos
simmsa Dec 18, 2025
fb077d5
Tests: Temporarily disable failing NOAA api tests
simmsa Dec 19, 2025
0e6bd97
Actions: Fix, use valid brew coretto version
simmsa Dec 19, 2025
a334ade
Tests: Temporarily disable noaa api multiple months test
simmsa Dec 19, 2025
b6f9347
Fix: Flatten capture length arrays before passing to MHKiT-Python
simmsa Dec 22, 2025
56bf419
Fix: Flatten additional python input 1d arrays
simmsa Dec 22, 2025
34d24a9
Actions: Add code to run OutOfProcess python startup before tests
simmsa Dec 22, 2025
9819ef3
Fix: Use bash on Windows to write to run.m
simmsa Dec 22, 2025
fd3027a
Actions: Set GLIBC correctly on Unix prior to starting MATLAB
simmsa Dec 22, 2025
03717c5
Utils: Flatten index in timeseries_to_pandas
simmsa Dec 22, 2025
d4b4112
Utils: Flatten arrays that may be 2d
simmsa Dec 22, 2025
de3cfca
Actions: Add python/matlab troubleshooting statements
simmsa Dec 23, 2025
9977e5b
Actions: Don't set PYTHONHOME on Windows
simmsa Dec 23, 2025
36cda4e
Actions: Use echo for windows matlab output
simmsa Dec 31, 2025
fb64b58
Actions: Tests against stable version of windows
simmsa Dec 31, 2025
2a37ecd
Actions: Use python to fix newline issues in output windows run.m
simmsa Dec 31, 2025
38af4df
Actions: Simplify windows mhkit python check
simmsa Dec 31, 2025
495eabf
Actions: Add ICU versioning for older versions of MATLAB
simmsa Dec 31, 2025
a58c6ee
Actions: Run numpy code on windows to verify operation
simmsa Dec 31, 2025
0d7759f
Actions: Use in process on windows for Python/MATLAB
simmsa Dec 31, 2025
55f7ce2
Actions: Allow duplicate OpenMp libraries on windows
simmsa Jan 1, 2026
20c756d
Actions: Pin scipy version on windows
simmsa Jan 6, 2026
794485b
Actions: Simplify run.m python calls
simmsa Jan 7, 2026
b714f32
Actions: Use MHKiT-Python 1.0.1 on unix
simmsa Jan 7, 2026
1ab2b0c
River: Update to use correct froude_number call
simmsa Jan 7, 2026
599e816
River: Extract exceedance_probability column
simmsa Jan 7, 2026
8efb7d9
River: Extract velocity column
simmsa Jan 7, 2026
6aacc10
Wave: Handle lazy loaded wave.io module
simmsa Jan 7, 2026
20afb21
Wave: Use mhkit-python capture_width_matrix
simmsa Jan 7, 2026
fbcb160
River: Extract power column
simmsa Jan 7, 2026
1f7be40
Actions: Use scipy 1.14
simmsa Jan 7, 2026
242f2a2
Actions: Use out of process for rex/h5py on Windows
simmsa Jan 7, 2026
b7468e4
Actions: Sync windows mods with cache_population mods
simmsa Jan 7, 2026
18e0909
Wave: Ensure capture width bins are valid before sending to python
simmsa Jan 7, 2026
f37256f
Wave: Offset mhkit-matlab bin edges to get same output as python v1.0.1
simmsa Jan 8, 2026
15ff91b
Wave: Use mhkit-python capture_length function
simmsa Jan 9, 2026
6109409
Tests: Standardize wave performance bin creation
simmsa Jan 9, 2026
45d50cf
Tests: Increase NaN count in capture_width matrix tests
simmsa Jan 9, 2026
29c6a86
Actions: Use conda forge to install mhkit on unix
simmsa Jan 9, 2026
d7909f2
Actions: Use windows findings to inform unix matlab/python setup
simmsa Jan 9, 2026
f63be55
Actions: Try only using conda-forge and not altering LD_* on unix
simmsa Jan 9, 2026
20cf02f
Actions: Use OutOfProcess with warmup on unix
simmsa Jan 9, 2026
8c3250f
Actions: Install matplotlib explicitly on unix
simmsa Jan 9, 2026
8078a66
Actions: Spec conda LD_* libraries for unix
simmsa Jan 9, 2026
0e104fe
Actions: Update unix sub jobs to use cache population python strategy
simmsa Jan 9, 2026
9e70414
Actions: Skip openssl mod on macos
simmsa Jan 9, 2026
8333db3
Actions: Use cache_population run.m for for matrix jobs on unix
simmsa Jan 9, 2026
ae217cb
Actions: Remove adhoc conda installs on unix jobs
simmsa Jan 9, 2026
6e9350a
Actions: Skip 2025a on Unix
simmsa Jan 9, 2026
582748e
Actions: Use conda to install scipy 1.14 on unix
simmsa Jan 9, 2026
17b494e
Actions: Move DYLD path manipulation after MATLAB setup on macos
simmsa Jan 12, 2026
cb8839e
Actions: Manipulate DYLD_* on macos inside of run.m
simmsa Jan 12, 2026
290c9dc
Actions: Use dyld fallback path on macos
simmsa Jan 12, 2026
ce7f79d
Actions: Use python to find system ssl on macos
simmsa Jan 12, 2026
195f199
Actions: Use a otool to output dependency chain on macos
simmsa Jan 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
419 changes: 322 additions & 97 deletions .github/workflows/unix_unit_tests.yml

Large diffs are not rendered by default.

196 changes: 135 additions & 61 deletions .github/workflows/windows_unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest]
os: [windows-2025]
python-version: ["3.11"]
matlab-version: [R2023b]
runs-on: ${{ matrix.os }}

env:
mhkit-python-dir: "MHKiT-Python"
MHKIT_PYTHON_VERSION: '0.9.0'
MHKIT_PYTHON_VERSION: '1.0.1'
steps:
- name: Install/Setup Miniconda
uses: conda-incubator/setup-miniconda@v3
Expand All @@ -43,7 +43,7 @@ jobs:
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
conda install mhkit==$MHKIT_PYTHON_VERSION
conda install -c conda-forge mhkit==$MHKIT_PYTHON_VERSION
- name: Check out MHKiT-MATLAB
uses: actions/checkout@v4

Expand All @@ -53,6 +53,15 @@ jobs:
conda activate mhkit_conda_env
pip install -e .

# mhkit 1.0.1 requires numpy 2.0+ (uses np.trapezoid)
# scipy 1.14+ is compatible with numpy 2.0
- name: Pin scipy version
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip uninstall -y scipy
pip install scipy==1.14.0

- name: List Installed Python Packages using Conda
shell: bash -l {0}
run: |
Expand Down Expand Up @@ -89,36 +98,68 @@ jobs:
with:
release: ${{ matrix.matlab-version }}

# Set Python environment variables BEFORE any MATLAB/Python interaction
# NOTE: Do NOT set PYTHONHOME - it causes "SRE module mismatch" errors
- name: Configure Python environment for MATLAB
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
# Add site-packages to Python's search path
echo "PYTHONPATH=$CONDA_PREFIX/Lib/site-packages" >> $GITHUB_ENV
# Add conda paths for DLL resolution
echo "$CONDA_PREFIX" >> $GITHUB_PATH
echo "$CONDA_PREFIX/Library/bin" >> $GITHUB_PATH
echo "$CONDA_PREFIX/Scripts" >> $GITHUB_PATH

- name: Set PYBIND11 GIL workaround
# if: ${{ matrix.matlab-version == 'R2025a' }}
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
export PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF=1
echo "PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF=1" >> $GITHUB_ENV
# Allow duplicate OpenMP libraries (MATLAB's libiomp5md.dll + numpy's libomp.dll)
echo "KMP_DUPLICATE_LIB_OK=TRUE" >> $GITHUB_ENV

- name: Configure MATLAB pyenv Version and ExecutionMode
if: ${{ matrix.os == 'windows-latest' }}
shell: pwsh
run: >
"pyenv(Version='{0}', ExecutionMode='OutOfProcess')" -f (python -c "import sys; print(sys.executable)") | Out-File -FilePath run.m

- name: Check Python System Path
# Generate run.m using Python to avoid shell encoding issues on Windows
- name: Generate run.m
shell: bash -l {0}
run: |
echo "py.sys.path" >> run.m

- name: Add MATLAB test commands
shell: bash
run: echo "version,
addpath(genpath('mhkit')),
import matlab.unittest.TestSuite,
import matlab.unittest.TestRunner,
testFolder = ['mhkit' filesep 'tests'],
suite = TestSuite.fromFolder(testFolder),
runner = TestRunner.withTextOutput,
results = runner.run(suite),
assertSuccess(results)" >> run.m
conda activate mhkit_conda_env
python -c "
import sys

python_exe = sys.executable.replace('\\\\', '/')

content = f'''pyenv(Version='{python_exe}', ExecutionMode='OutOfProcess')
% Verify Python environment
pe = pyenv;
disp(pe);
% Warm up numpy - must call function to force resolution
py.importlib.import_module('numpy');
dummy = py.numpy.array([1,2,3]);
disp(class(dummy));
clear dummy;
disp('py.numpy.array: resolved');
% Warm up mhkit submodules - force resolution by calling functions
py.importlib.import_module('mhkit');
py.importlib.import_module('mhkit.wave.io.ndbc');
py.importlib.import_module('mhkit.wave.io.swan');
py.importlib.import_module('mhkit.river.resource');
disp('mhkit submodules: imported');
version
pyenv
addpath(genpath('mhkit'))
import matlab.unittest.TestSuite
import matlab.unittest.TestRunner
testFolder = ['mhkit' filesep 'tests'];
suite = TestSuite.fromFolder(testFolder);
runner = TestRunner.withTextOutput;
results = runner.run(suite);
assertSuccess(results)
'''

with open('run.m', 'w', encoding='utf-8', newline='\\n') as f:
f.write(content)
"

- name: Output Run.m
shell: bash
Expand All @@ -143,11 +184,11 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [windows-latest]
os: [windows-2025]
python-version: ["3.10", 3.11, 3.12]
# matlab-version: [R2022b, R2023a, R2023b, R2024a, R2024b]
matlab-version: [R2023a, R2023b, R2024a, R2024b]
mhkit-python-version: ["0.9.0"]
mhkit-python-version: ["1.0.1"]
exclude:
# Cache population job
- matlab-version: R2023b
Expand Down Expand Up @@ -193,7 +234,7 @@ jobs:
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
conda install mhkit==$MHKIT_PYTHON_VERSION
conda install -c conda-forge mhkit==$MHKIT_PYTHON_VERSION
- name: Output python executable
shell: bash -l {0}
run: |
Expand All @@ -213,19 +254,19 @@ jobs:
conda activate mhkit_conda_env
pip3 install -e .

# - name: Pin scipy version
# shell: bash -l {0}
# run: |
# conda activate mhkit_conda_env
# pip uninstall -y scipy
# pip install scipy==1.10.1
#
# mhkit 1.0.1 requires numpy 2.0+ (uses np.trapezoid)
# scipy 1.14+ is compatible with numpy 2.0
- name: Pin scipy version
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
pip uninstall -y scipy
pip install scipy==1.14.0

# Some tests fail with:
# Python Error: ImportError: DLL load failed while importing pyexpat: The operating system cannot run %1.
# One potential fix is to install expat and lxml
- name: Install Windows XML dependencies
if: ${{ matrix.os == 'windows-latest' }}
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
Expand Down Expand Up @@ -265,41 +306,74 @@ jobs:
with:
release: ${{ matrix.matlab-version }}

- name: Set PYBIND11 GIL workaround
# if: ${{ matrix.matlab-version == 'R2025a' }}
# Set Python environment variables BEFORE any MATLAB/Python interaction
# NOTE: Do NOT set PYTHONHOME - it causes "SRE module mismatch" errors
- name: Configure Python environment for MATLAB
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
export PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF=1
echo "PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF=1" >> $GITHUB_ENV
# Add site-packages to Python's search path
echo "PYTHONPATH=$CONDA_PREFIX/Lib/site-packages" >> $GITHUB_ENV
# Add conda paths for DLL resolution
echo "$CONDA_PREFIX" >> $GITHUB_PATH
echo "$CONDA_PREFIX/Library/bin" >> $GITHUB_PATH
echo "$CONDA_PREFIX/Scripts" >> $GITHUB_PATH

- name: Add Python Dir to Path
- name: Set PYBIND11 GIL workaround
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
printf "getenv('path')\nsetenv('path', ['%s;', getenv('path')])\ngetenv('path')\n" $(python -c "import sys; import os; print(os.path.dirname(sys.executable))") >> run.m
# OutOfProcess works reliably on linux, macos, and windows
- name: Configure MATLAB pyenv Version and ExecutionMode
echo "PYBIND11_NO_ASSERT_GIL_HELD_INCREF_DECREF=1" >> $GITHUB_ENV
# Allow duplicate OpenMP libraries (MATLAB's libiomp5md.dll + numpy's libomp.dll)
echo "KMP_DUPLICATE_LIB_OK=TRUE" >> $GITHUB_ENV

# Generate run.m using Python to avoid shell encoding issues on Windows
- name: Generate run.m
shell: bash -l {0}
run: |
conda activate mhkit_conda_env
printf "pyenv(Version='%s', ExecutionMode='OutOfProcess')\n" $(python -c "import sys; print(sys.executable)") >> run.m
- name: Check Python System Path
shell: bash -l {0}
run: |
echo "py.sys.path" >> run.m
- name: Add MATLAB test commands
shell: bash
run: echo "version,
pyenv,
addpath(genpath('mhkit')),
import matlab.unittest.TestSuite,
import matlab.unittest.TestRunner,
testFolder = ['mhkit' filesep 'tests'],
suite = TestSuite.fromFolder(testFolder),
runner = TestRunner.withTextOutput,
results = runner.run(suite),
assertSuccess(results)" >> run.m
python -c "
import sys
import os

python_exe = sys.executable.replace('\\\\', '/')
python_dir = os.path.dirname(sys.executable).replace('\\\\', '/')

content = f'''getenv('path')
setenv('path', ['{python_dir};', getenv('path')])
getenv('path')
pyenv(Version='{python_exe}', ExecutionMode='OutOfProcess')
% Verify Python environment
pe = pyenv;
disp(pe);
% Warm up numpy - must call function to force resolution
py.importlib.import_module('numpy');
dummy = py.numpy.array([1,2,3]);
disp(class(dummy));
clear dummy;
disp('py.numpy.array: resolved');
% Warm up mhkit submodules - force resolution by calling functions
py.importlib.import_module('mhkit');
py.importlib.import_module('mhkit.wave.io.ndbc');
py.importlib.import_module('mhkit.wave.io.swan');
py.importlib.import_module('mhkit.river.resource');
disp('mhkit submodules: imported');
py.sys.path
version
pyenv
addpath(genpath('mhkit'))
import matlab.unittest.TestSuite
import matlab.unittest.TestRunner
testFolder = ['mhkit' filesep 'tests'];
suite = TestSuite.fromFolder(testFolder);
runner = TestRunner.withTextOutput;
results = runner.run(suite);
assertSuccess(results)
'''

with open('run.m', 'w', encoding='utf-8', newline='\\n') as f:
f.write(content)
"

- name: Output Run.m
shell: bash
Expand Down
4 changes: 2 additions & 2 deletions mhkit/river/resource/Froude_number.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if nargin == 3
Fr=py.mhkit.river.resource.Froude_number(v,h,pyargs('g',g));
Fr=py.mhkit.river.resource.froude_number(v,h,pyargs('g',g));
else
Fr=py.mhkit.river.resource.Froude_number(v,h);
Fr=py.mhkit.river.resource.froude_number(v,h);
end

9 changes: 6 additions & 3 deletions mhkit/river/resource/discharge_to_velocity.m
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,12 @@
for i=1:si(2)
test=string(py.str(vv{i}));
newname=split(test,",");

V.(newname(1))=vals(:,i);

% Map Python column names to expected MATLAB field names
if newname(1) == "velocity"
V.V=vals(:,i);
else
V.(newname(1))=vals(:,i);
end
end
V.time=double(py.array.array('d',py.numpy.nditer(Vdf.index)));

9 changes: 6 additions & 3 deletions mhkit/river/resource/exceedance_probability.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,12 @@
for i=1:si(2)
test=string(py.str(vv{i}));
newname=split(test,",");

F.(newname(1))=vals(:,i);

% Map Python column names to expected MATLAB field names
if newname(1) == "exceedance_probability"
F.F=vals(:,i);
else
F.(newname(1))=vals(:,i);
end
end
F.time=double(py.array.array('d',py.numpy.nditer(EPpd.index)));

9 changes: 6 additions & 3 deletions mhkit/river/resource/velocity_to_power.m
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,12 @@
for i=1:si(2)
test=string(py.str(vv{i}));
newname=split(test,",");

p.(newname(1))=vals(:,i);

% Map Python column names to expected MATLAB field names
if newname(1) == "power"
p.P=vals(:,i);
else
p.(newname(1))=vals(:,i);
end
end
p.time=double(py.array.array('d',py.numpy.nditer(Pdf.index)));

6 changes: 6 additions & 0 deletions mhkit/tests/Tidal_TestIO.m
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ function test_load_noaa_data(testCase)

function test_request_noaa_data(testCase)

assumeFail(testCase, "Temporarily disable NOAA api calls - failures need further investigation per @simmsa")

data = request_noaa_data('s08010', 'currents','20180101','20180102');

assertTrue(testCase, isfield(data,'s'));
Expand All @@ -29,6 +31,8 @@ function test_request_noaa_data(testCase)

function test_request_noaa_data_multiple_days(testCase)

assumeFail(testCase, "Temporarily disable NOAA api calls - failures need further investigation per @simmsa")

data = request_noaa_data('s08010', 'currents','20180101','20180105');

assertTrue(testCase, isfield(data,'s'));
Expand All @@ -41,6 +45,8 @@ function test_request_noaa_data_multiple_days(testCase)

function test_request_noaa_data_multiple_months(testCase)

assumeFail(testCase, "Temporarily disable NOAA api calls - failures need further investigation per @simmsa")

data = request_noaa_data('s08010', 'currents','20180101','20180331');

assertTrue(testCase, isfield(data,'s'));
Expand Down
Loading
Loading