Skip to content

Commit b790030

Browse files
authored
Merge branch 'develop' into avg-profile-bugfixes
2 parents 6437869 + 323e741 commit b790030

File tree

9 files changed

+192
-22
lines changed

9 files changed

+192
-22
lines changed

.github/workflows/main.yml

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ on:
1010
- main
1111
- develop
1212

13-
concurrency:
14-
group: ${{ github.event.pull_request.number }} # Use the pull request number as the concurrency group
15-
cancel-in-progress: true # Cancel any in-progress jobs for the same group. Prevents duplicate workflows on PRs from develop into main or from main into develop.
16-
1713
jobs:
1814
set-os:
1915
runs-on: ubuntu-latest
@@ -286,12 +282,20 @@ jobs:
286282
name: data
287283
path: ~/.cache/mhkit
288284

285+
- name: Install system dependencies
286+
if: runner.os == 'Linux'
287+
run: sudo apt-get install -y libhdf5-dev libnetcdf-dev
288+
289289
- name: Update and install packages
290290
shell: bash -l {0}
291291
run: |
292292
python -m pip install --upgrade pip wheel
293293
pip install -e ".[all,dev]"
294294
295+
- name: Reinstall h5py and netCDF4 with system libraries
296+
if: runner.os == 'Linux'
297+
run: "pip install --force-reinstall --no-binary=:all: h5py netCDF4"
298+
295299
- name: Install setuptools for Python 3.12
296300
if: matrix.python-version == '3.12'
297301
run: pip install setuptools
@@ -394,10 +398,67 @@ jobs:
394398
parallel: true
395399
path-to-lcov: ./coverage.lcov
396400

401+
test-wheel-packaging:
402+
name: Test Built Wheel
403+
runs-on: ubuntu-latest
404+
steps:
405+
- uses: actions/checkout@v4
406+
407+
- uses: actions/setup-python@v5
408+
with:
409+
python-version: '3.11'
410+
411+
- name: Install build dependencies
412+
run: python -m pip install build --user
413+
414+
- name: Build wheel
415+
run: python -m build --wheel --outdir dist/ .
416+
417+
- name: Install MHKiT from built wheel
418+
run: |
419+
# Install from build wheel in dist/ directory, not from PyPI
420+
# -f, --find-links <url> If a URL or path to an html file, then parse for links to archives such as sdist (.tar.gz) or wheel (.whl) files.
421+
# If a local path or file:// URL that's a directory, then look for archives in the directory listing.
422+
pip install 'mhkit[all]' --find-links dist/
423+
424+
- name: Test mhkit submodule imports
425+
run: |
426+
python -c "
427+
import mhkit
428+
print(f'Version: {mhkit.__version__}')
429+
430+
# Test all submodules can be imported
431+
modules = ['wave', 'river', 'tidal', 'dolfyn', 'power', 'loads', 'mooring', 'acoustics', 'qc', 'utils']
432+
for mod in modules:
433+
exec(f'from mhkit import {mod}')
434+
print(f'Successfully imported mhkit.{mod}')
435+
436+
print('All submodules imported successfully!')
437+
"
438+
439+
- name: Verify mhkit package structure
440+
run: |
441+
python -c "
442+
import mhkit
443+
import os
444+
445+
mhkit_path = os.path.dirname(mhkit.__file__)
446+
expected_dirs = ['wave', 'river', 'tidal', 'dolfyn', 'power', 'loads', 'mooring', 'acoustics', 'qc', 'utils']
447+
448+
for d in expected_dirs:
449+
dir_path = os.path.join(mhkit_path, d)
450+
assert os.path.isdir(dir_path), f'Missing submodule directory: {d}'
451+
init_file = os.path.join(dir_path, '__init__.py')
452+
assert os.path.isfile(init_file), f'Missing __init__.py in {d}'
453+
454+
print('Package structure verified!')
455+
"
456+
397457
test-optional-pip-dependencies:
398458
needs: [set-os, prepare-nonhindcast-cache]
399459
runs-on: ubuntu-latest
400460
strategy:
461+
fail-fast: false
401462
matrix:
402463
module:
403464
[wave, tidal, river, dolfyn, power, loads, mooring, acoustics, utils]
@@ -417,12 +478,20 @@ jobs:
417478
name: data
418479
path: ~/.cache/mhkit
419480

481+
- name: Install system dependencies
482+
if: matrix.module != 'river' || matrix.module != 'power' || matrix.module != 'utils' || matrix.module != 'loads'
483+
run: sudo apt-get install -y libhdf5-dev libnetcdf-dev
484+
420485
- name: Install MHKiT with optional dependency
421486
run: |
422487
python -m pip install --upgrade pip
423-
pip install "mhkit[${{ matrix.module }}]"
488+
pip install -e ".[${{ matrix.module }}]"
424489
pip install pytest
425490
491+
- name: Reinstall h5py and netCDF4 with system libraries
492+
if: matrix.module != 'river' || matrix.module != 'power' || matrix.module != 'utils' || matrix.module != 'loads'
493+
run: "pip install --force-reinstall --no-binary=:all: h5py netCDF4"
494+
426495
- name: Run tests for ${{ matrix.module }}
427496
env:
428497
MPLBACKEND: Agg

.github/workflows/pypi.yml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
push:
88
branches:
99
- main
10+
pull_request:
11+
branches:
12+
- main
1013
release:
1114
types: [published]
1215

@@ -16,19 +19,28 @@ jobs:
1619
runs-on: ubuntu-latest
1720

1821
steps:
19-
- uses: actions/checkout@v2
22+
- uses: actions/checkout@v4
2023
with:
2124
fetch-depth: 0
2225

23-
- uses: actions/setup-python@v2
26+
- uses: actions/setup-python@v5
2427
with:
25-
python-version: 3.8
28+
python-version: '3.10'
2629

2730
- run: python -m pip install build --user
2831
- run: python -m build --sdist --wheel --outdir dist/ .
2932

30-
- name: Upload to Test PyPI
31-
if: github.event_name != 'release' && github.repository_owner == 'MHKiT-Software'
33+
- name: Test wheel contents before upload
34+
run: |
35+
# Install the built wheel from dist/ to verify it was packaged correctly before upload
36+
# --find-links dist/ tells pip to look in dist/ directory first and prefer the wheel
37+
# Dependencies (numpy, pandas, etc.) are fetched from PyPI as normal
38+
# This verifies the wheel contains all necessary files and can be installed successfully
39+
pip install 'mhkit[all]' --find-links dist/
40+
python -c "from mhkit import wave, river, tidal, dolfyn, power, loads, mooring, acoustics, qc, utils; print('All modules imported successfully')"
41+
42+
- name: Upload to Test PyPI (commits to main only)
43+
if: github.event_name == 'push' && github.repository_owner == 'MHKiT-Software'
3244
uses: pypa/gh-action-pypi-publish@release/v1
3345
with:
3446
user: __token__
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Tests that the package can be built and installed from a wheel
2+
# Runs on PRs to catch build issues before merging to main
3+
# Does NOT publish to PyPI
4+
5+
name: Test PIP Package Build
6+
7+
on:
8+
pull_request:
9+
branches:
10+
- main
11+
- develop
12+
13+
jobs:
14+
test-build:
15+
name: Test wheel build
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
python-version: ['3.11', '3.12']
20+
21+
steps:
22+
- uses: actions/checkout@v2
23+
with:
24+
fetch-depth: 0
25+
26+
- uses: actions/setup-python@v2
27+
with:
28+
python-version: ${{ matrix.python-version }}
29+
30+
- name: Install build tools
31+
run: python -m pip install build --user
32+
33+
- name: Build wheel and source distribution
34+
run: python -m build --sdist --wheel --outdir dist/ .
35+
36+
- name: Test wheel installation
37+
run: |
38+
# Install the built wheel from dist/ to verify it was packaged correctly
39+
# --find-links dist/ tells pip to look in dist/ directory first and prefer the wheel
40+
# Dependencies (numpy, pandas, etc.) are fetched from PyPI as normal
41+
# This verifies the wheel contains all necessary files and can be installed successfully
42+
pip install 'mhkit[all]' --find-links dist/
43+
python -c "from mhkit import wave, river, tidal, dolfyn, power, loads, mooring, acoustics, qc, utils; print('All modules imported successfully')"

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2025, Alliance for Sustainable Energy, 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.
3+
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.
44
All rights reserved.
55

66
Redistribution and use in source and binary forms, with or without

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ See [installation instructions](https://mhkit-software.github.io/MHKiT/installat
8484

8585
## Copyright and license
8686

87-
MHKiT-Python is copyright through the National Renewable Energy Laboratory,
87+
MHKiT-Python is copyright through the National Laboratory of the Rockies,
8888
Pacific Northwest National Laboratory, and Sandia National Laboratories.
8989
The software is distributed under the Revised BSD License.
9090
See [copyright and license](LICENSE.md) for more information.

mhkit/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111

1212
configure_warnings()
1313

14-
__version__ = "v1.0.0"
14+
__version__ = "v1.0.1"
1515

1616
__copyright__ = """
17-
Copyright 2019, Alliance for Sustainable Energy, LLC under the terms of
17+
Copyright 2019, Alliance for Energy Innovation, LLC under the terms of
1818
Contract DE-AC36-08GO28308, Battelle Memorial Institute under the terms of
1919
Contract DE-AC05-76RL01830, and National Technology & Engineering Solutions of
2020
Sandia, LLC under the terms of Contract DE-NA0003525. The U.S. Government

mhkit/dolfyn/velocity.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,10 +421,10 @@ def E_coh(
421421
"""
422422
Coherent turbulent energy
423423
424-
Niel Kelley's 'coherent turbulence energy', which is the
424+
Neil Kelley's 'coherent turbulence energy', which is the
425425
root-mean-square of the Reynold's stresses.
426426
427-
See: NREL Technical Report TP-500-52353
427+
See: NLR Technical Report TP-500-52353
428428
"""
429429
E_coh = (self.upwp_**2 + self.upvp_**2 + self.vpwp_**2) ** (0.5)
430430

mhkit/wave/__init__.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
1+
import importlib
2+
13
from mhkit.wave import resource
2-
from mhkit.wave import io
34
from mhkit.wave import graphics
45
from mhkit.wave import performance
56
from mhkit.wave import contours
7+
8+
9+
def __getattr__(name):
10+
"""
11+
Lazy load the wave.io submodule using PEP 562 module-level __getattr__.
12+
13+
This defers importing heavy wave.io dependencies (rex, netCDF4, etc,) until
14+
they are actually accessed, improving import time for users who don't need
15+
all wave submodules, and avoiding import errors for users who have specified
16+
module level installs that need wave module functions, but not wave.io functions.
17+
"""
18+
if name == "io":
19+
# This uses importlib.import_module() here, not "from mhkit.wave import io"
20+
# because when Python executes getattr(), it looks for 'io' as an attribute of
21+
# mhkit.wave. At this point in the module loading code 'io' doesn't exist yet and
22+
# Python calls __getattr__('io') again. This triggers the same "from" statement,
23+
# which calls __getattr__('io') again yielding a RecursionError.
24+
#
25+
# To fix this uses importlib.import_module("mhkit.wave.io") which loads the module directly
26+
# using the absolute path without doing attribute lookup on the parent.
27+
#
28+
# The statement "from mhkit.wave import io" is equivalent to:
29+
# io = getattr(mhkit.wave, 'io')
30+
#
31+
io = importlib.import_module("mhkit.wave.io")
32+
33+
# Cache the module so subsequent accesses bypass __getattr__ entirely
34+
globals()[name] = io
35+
return io
36+
37+
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

pyproject.toml

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ dependencies = [
2727
"xarray>=2024.6.0",
2828
"matplotlib>=3.9.1",
2929
"pecos>=0.3.0",
30+
"requests",
3031
]
3132

3233
[project.optional-dependencies]
@@ -38,20 +39,17 @@ wave = [
3839
"pytz",
3940
"NREL-rex>=0.2.63",
4041
"beautifulsoup4",
41-
"requests",
4242
"bottleneck",
4343
"lxml"
4444
]
4545

4646
tidal = [
4747
"netCDF4>=1.7.1.post1",
48-
"requests",
4948
"bottleneck"
5049
]
5150

5251
river = [
5352
"netCDF4>=1.7.1.post1",
54-
"requests",
5553
"bottleneck",
5654
]
5755

@@ -66,7 +64,9 @@ power = [
6664
]
6765

6866
loads = [
69-
"fatpack"
67+
"fatpack",
68+
"statsmodels>=0.14.2",
69+
"scikit-learn>=1.5.1",
7070
]
7171

7272
mooring = [
@@ -126,8 +126,22 @@ examples = [
126126
Homepage = "https://github.com/MHKiT-Software/mhkit-python"
127127
Documentation = "https://mhkit-software.github.io/MHKiT"
128128

129+
[tool.setuptools.packages.find]
130+
# This section controls how this python package is built for pip installations
131+
# Getting this section wrong results in an incomplete package that is missing submodules
132+
# This can be tested by simply running python -m build in the root directory
133+
# and then inspecting the generated .tar.gz and .whl files in the dist/ directory
134+
#
135+
# https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#finding-simple-packages
136+
# What this does: (per the above docs):
137+
# - `where = ["."]`: Search for packages starting from the root directory
138+
# - `include = ["mhkit*"]`: Only include packages that match the pattern `mhkit*`
139+
# - this includes `mhkit`, `mhkit.wave`, `mhkit.river`, etc.
140+
# - Setuptools will automatically discover all directories containing `__init__.py` files
141+
where = ["."]
142+
include = ["mhkit*"]
143+
129144
[tool.setuptools]
130-
packages = ["mhkit"]
131145
zip-safe = false
132146
include-package-data = true
133147

0 commit comments

Comments
 (0)