Python finite-element library for wind turbine blade and tower modal analysis (OpenFAST/ElastoDyn)
Project description
pyBmodes
pybmodes is a pure-Python finite-element library for wind-turbine blade and tower modal analysis. It reads OpenFAST (ElastoDyn / SubDyn / HydroDyn / MoorDyn), BModes .bmi, and WISDEM / WindIO ontology YAML inputs; solves the coupled flap–lag–torsion–axial vibration modes with a 15-DOF Bernoulli-Euler beam element; and emits ElastoDyn-compatible mode-shape polynomials, MAC-tracked Campbell diagrams, and bundled Markdown / HTML / CSV reports.
Validated against the BModes Fortran reference solver on six benchmark cases (NREL 5MW land + OC3 monopile + OC3 Hywind floating spar, IEA-3.4-130-RWT, BModes CertTest 03 / 04) to better than 0.01 % on every comparison. As of 1.8.0 the dedicated Validation workflow (weekly cron + workflow_dispatch) enforces the strict tolerance in CI for every case whose external data is clonable from a public GitHub upstream — the NREL 5MW r-test family (via OpenFAST/r-test) and the IEA-Task-37 reference turbines (via IEAWindTask37/IEA-*-RWT). The two BModes CertTest cases (Test03, Test04) depend on external/BModes, which is a NREL download (not a GitHub repo) and is not redistributable in CI; those tests skip cleanly when the data is absent and remain a maintainer-local enforcement until the BModes archive is mirrored somewhere CI can clone. The per-PR ci.yml continues to run the self-contained suite (synthetic + closed-form-referenced) and tolerates "no tests collected" on the integration step when the runner has no upstream data. See VALIDATION.md for the full per-case matrix with external-data flags and external/MANIFEST.toml for the pinned SHAs + file hashes you can verify against.
Documentation
📖 The rendered documentation lives at pybmodes.readthedocs.io. Sphinx-specific roles (
:math:,:doc:,:func:,:class:) inside the.rstsource files only render correctly through the deployed site; browsing the raw source on GitHub will show them as literal text. Always link readers to the RTD URL, not the source.(If the URL 404s, the Read the Docs project hasn't been imported yet — see
docs/deployment.rstfor the one-time maintainer setup.)
| Page | What's there |
|---|---|
| Installation | PyPI / source install, extras matrix, Windows + conda quickstart, troubleshooting |
| Quickstart | Nine worked recipes — synthetic tower, OpenFAST deck, monopile + SubDyn, floating coupled, Campbell sweep, WindIO one-click, MAC, batch, persistence |
| Theory | Eigenproblem maths, 15-DOF beam element, four boundary conditions, polynomial ansatz, solver dispatch, citable references |
| Data sources | Every input format — BModes .bmi, ElastoDyn / SubDyn / HydroDyn / MoorDyn .dat, WAMIT .1 / .hst, WindIO .yaml — with snippet examples |
| Units | SI conventions, conversion tables, mode-shape normalisation, OpenFAST DOF order, common pitfalls |
| Limitations | Polynomial-representation limits, four specific validation-matrix edge cases, "when to reach for a different tool" |
| Validation matrix | Per-case cross-checks against published references (cross-references VALIDATION.md) |
| API reference | Autodoc-generated module reference |
| API contract | Semver-frozen public surface + deprecation policy + stability tiers |
| Changelog | Versioning policy + full release history (cross-references CHANGELOG.md) |
| Contributing | Welcome scope, pre-commit, PR checklist, no-AI-attribution rule |
| Release checklist | 11-step pre-tag sequence (maintainer) |
| Deployment | One-time RTD setup + versioning policy (maintainer) |
To build locally:
pip install -e ".[docs]"
make -C docs html
# then open docs/_build/html/index.html through a real web server
# (file:// blocks MathJax CDN in some browsers):
python -m http.server -d docs/_build/html
Install
PyPI status: pre-release. The
pybmodesdistribution is not yet published to PyPI — that's tracked as a 1.x release-gate item. Until the first PyPI release lands, install from source:
git clone https://github.com/SMI-Lab-Inha/pyBModes.git
cd pyBModes
pip install -e ".[dev,plots]"
Once published, the canonical install will be the standard one:
pip install pybmodes # (post-PyPI-release; not available yet)
Take care that pybmodes is a different project from pyModeS (an ADS-B / Mode-S decoder). When the PyPI release lands the project name on PyPI will be pybmodes (lowercase, no S); double-check the package name + the GitHub SMI-Lab-Inha/pyBModes repo URL before installing.
See https://pybmodes.readthedocs.io/en/latest/installation.html for the full Windows + conda quickstart and the optional-extras matrix ([plots], [windio], [notebook], [docs]).
Quick example
from pybmodes.models import Tower
from pybmodes.elastodyn import compute_tower_params, patch_dat
# Reads ElastoDyn main + tower from one path; lumps the rotor mass.
tower = Tower.from_elastodyn("NRELOffshrBsline5MW_Onshore_ElastoDyn.dat")
modal = tower.run(n_modes=4)
# Constrained 6th-order fit, FA/SS family selection with torsion-contamination filter.
params = compute_tower_params(modal)
# Rewrite the polynomial blocks (use --dry-run / --diff via the CLI for safety).
patch_dat("NRELOffshrBsline5MW_Onshore_ElastoDyn.dat", params)
More — Campbell sweeps, WindIO one-click, mode-by-mode MAC comparison, bundled reports — in https://pybmodes.readthedocs.io/en/latest/quickstart.html.
CLI
Seven subcommands surfaced as the pybmodes console script:
| Subcommand | Purpose |
|---|---|
pybmodes validate <main.dat> |
Coefficient-consistency report on one ElastoDyn deck. |
pybmodes patch <main.dat> |
Regenerate polynomial blocks. --dry-run / --diff / --backup / --output-dir for safety. |
pybmodes campbell <input> |
Rotor-speed sweep → Campbell diagram PNG + CSV. |
pybmodes batch ROOT |
Walk a directory of decks; per-deck validate + patch + summary CSV. |
pybmodes report <main.dat> |
Bundled Markdown / HTML / CSV analysis report. |
pybmodes windio <yaml | dir> |
One-click WISDEM / WindIO → composite blade + tubular tower + coupled platform + Campbell. |
pybmodes examples --copy DIR |
Vendor sample_inputs/ and / or reference_decks/ out of the installed wheel. |
Development
pytest # default — self-contained, no external data
pytest -m integration # integration — needs upstream decks under external/
ruff check src/ tests/ scripts/
mypy src/pybmodes
python scripts/audit_validation_claims.py # gates "claim ahead of test" drift
The default pytest run is self-contained and works on a fresh clone with no external data. Tests that need locally-checked-out OpenFAST r-test, BModes CertTest, or IEA-RWT decks are gated behind the integration marker; CI tolerates exit code 5 ("no tests collected") on a runner without the data. Full developer guide in CONTRIBUTING.md.
Citation
If you use pyBmodes in academic work, please cite it via the CITATION.cff file. GitHub's Cite this repository widget reads it automatically; Zenodo and most reference managers pick it up too.
License
Released under the Apache License 2.0.
Copyright 2024-2026 Jae Hoon Seo, Marine Structural Mechanics and Integrity Lab (SMI Lab), Inha University.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pybmodes-1.8.0.tar.gz.
File metadata
- Download URL: pybmodes-1.8.0.tar.gz
- Upload date:
- Size: 675.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0870b883ceb44b48ea1e4d082111a745e7a7f7a5a1e03f0d68dc0cfb1fb10d6
|
|
| MD5 |
9fbaf03b308da44299b9663303ef7aa4
|
|
| BLAKE2b-256 |
d6231389514f2a5b0de78e8e3d3df9b756e8a06d453c664b5513fdde75ceb174
|
Provenance
The following attestation bundles were made for pybmodes-1.8.0.tar.gz:
Publisher:
publish.yml on SMI-Lab-Inha/pyBModes
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pybmodes-1.8.0.tar.gz -
Subject digest:
a0870b883ceb44b48ea1e4d082111a745e7a7f7a5a1e03f0d68dc0cfb1fb10d6 - Sigstore transparency entry: 1592064869
- Sigstore integration time:
-
Permalink:
SMI-Lab-Inha/pyBModes@1ab2c7376d3124c1b7de01b441ecd7cb2dc4496b -
Branch / Tag:
refs/tags/v1.8.0 - Owner: https://github.com/SMI-Lab-Inha
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1ab2c7376d3124c1b7de01b441ecd7cb2dc4496b -
Trigger Event:
push
-
Statement type:
File details
Details for the file pybmodes-1.8.0-py3-none-any.whl.
File metadata
- Download URL: pybmodes-1.8.0-py3-none-any.whl
- Upload date:
- Size: 544.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c60717c91f284811c156b7e189f889561f077134a1fb184eca56dcfc0b95f96
|
|
| MD5 |
404cd14226ccfc586c14e4235fbf1aea
|
|
| BLAKE2b-256 |
a7de4551bab19b54f5e165bda547196d0763dd612f6449b8eb34c1926c770c06
|
Provenance
The following attestation bundles were made for pybmodes-1.8.0-py3-none-any.whl:
Publisher:
publish.yml on SMI-Lab-Inha/pyBModes
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pybmodes-1.8.0-py3-none-any.whl -
Subject digest:
3c60717c91f284811c156b7e189f889561f077134a1fb184eca56dcfc0b95f96 - Sigstore transparency entry: 1592064894
- Sigstore integration time:
-
Permalink:
SMI-Lab-Inha/pyBModes@1ab2c7376d3124c1b7de01b441ecd7cb2dc4496b -
Branch / Tag:
refs/tags/v1.8.0 - Owner: https://github.com/SMI-Lab-Inha
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1ab2c7376d3124c1b7de01b441ecd7cb2dc4496b -
Trigger Event:
push
-
Statement type: