Limitations
What pybmodes deliberately does not attempt to do, where
the modelling fidelity stops, and the specific edge cases
where the published numerics legitimately disagree with a
naive expectation.
Polynomial representation limits
ElastoDyn’s polynomial ansatz
\(\mathrm{SHP}(s) = \sum_{i=2}^{6} c_i s^{\,i}\) is a
constrained 6th-order form with SHP(0) = SHP'(0) = 0 baked
in. Three real-world consequences:
The IEA-15-UMaine TwSSM2Sh block
The constrained 6th-order polynomial cannot resolve the
section-property gradient of the IEA-15-240-RWT UMaine
VolturnUS-S tower for that specific mode. Even a fresh patch
from the structural inputs ends at Overall: WARN with
ratio > 100 on that block; every other block on every other
deck reaches PASS.
This is a representation limit of ElastoDyn’s polynomial form,
not a pyBmodes bug. The shipped
src/pybmodes/_examples/reference_decks/iea15mw_umainesemi/validation_report.txt
carries an auto-emitted footer documenting this.
Workaround: accept the WARN. The downstream OpenFAST simulation runs without complaint; the WARN is a quality flag for the polynomial form, not for the underlying FEM modes (which match BModes JJ to < 0.01 %).
Hybrid bending + twist modes
Some FEM modes are hybrid — a bending mode with appreciable twist content from elastic-centre / shear-centre offset, or twist modes with bending coupling near a 2-fold degenerate eigenspace.
The polynomial form cannot express twist at all (it’s a scalar single-axis form), so including hybrid modes in the FA / SS family selection produces silently-wrong coefficients.
_select_tower_family drops candidates whose modal-kinetic-
energy torsion fraction is \(\geq 0.10\). Rejected modes
travel through
TowerSelectionReport.rejected_fa_modes /
rejected_ss_modes so the user sees which were dropped.
Workaround: if you have a deliberately hybrid tower design (rare), accept that ElastoDyn’s polynomial form can’t represent it and use BeamDyn for the runtime simulation instead. pyBmodes’ FEM modes themselves are accurate; only the polynomial projection is constrained.
Free-free floating polynomials
ElastoDyn’s polynomial ansatz forces clamped-base modes
(SHP(0) = SHP'(0) = 0); the runtime adds rigid-body
platform motion as a separate sum.
So a floating-deck workflow that wants polynomials uses
Tower.from_elastodyn(...) (cantilever, hub_conn = 1),
not Tower.from_bmi(...) with hub_conn = 2. The
OpenFAST source-code audit behind this is in
cases/ECOSYSTEM_FINDING.md (the “Floating-deck
polynomials” section) and the per-deck
FLOATING_CASES.md.
For coupled frequency prediction (the 6 platform rigid-body
modes plus the tower bending modes referenced to the
platform), use Tower.from_elastodyn_with_mooring(...).
That’s a different workflow from polynomial generation; the
two coexist by design.
To reconcile the polynomial-basis cantilever frequency against
the coupled-system frequency an OpenFAST linearisation will
report on the same deck (the gap is typically 20-30 percent on
a floating platform and is expected rather than a bug), call
pybmodes.elastodyn.report_floating_frequency_gap(). It
runs both solves on the supplied deck and returns a
FloatingFrequencyGap with the
cantilever and coupled 1st FA / SS frequencies plus a
format_report() rendering suitable for stdout or a log.
The audit trail for the rejected projection-method
polynomial-generation proposal (which would have introduced
a Rayleigh-quotient bias on FreqTFA and double-counted
platform restoring) is recorded in
src/pybmodes/_examples/reference_decks/FLOATING_CASES.md.
Floating-platform fidelity
Tower.from_windio_floating is a two-tier API:
Industry-grade: with companion HydroDyn + MoorDyn + ElastoDyn decks supplied (or auto-discovered by the
pybmodes windioCLI scoped to the turbine root), the path is byte-identical tofrom_elastodyn_with_mooring, which reproduces OC3 Hywind to ≈ 0.0003 % across the first 9 modes.Screening preview: yaml-only — member-Morison hydrodynamics + RAFT-style end-cap added mass + catenary mooring from the yaml. Useful for design-space exploration, not for final-design certification numbers. Always emits one
UserWarningnaming the result asSCREENING-fidelity (NOT industry-grade).
This is intentional. Industry decks carry decades of WAMIT panel-mesh refinement that a member-Morison reduction can’t match for second-order quantities. Use the right tier for the right question.
Mooring physics
pybmodes.mooring.MooringSystem is a quasi-static
elastic catenary (Jonkman 2007 Appendix B). What it includes:
B-1 / B-2 fully-suspended branch
B-7 / B-8 with \(C_B = 0\) for the anchor-on-seabed branch
Per-line elastic stretch
Damped Newton on \((H, V_F)\) with analytical 2×2 Jacobian
Cross-coupling between bodies through shared anchors
What it does not include:
Dynamic effects — drag, added mass, vortex-induced vibration. Use MoorDyn in time-domain for those.
Bending stiffness — lines are assumed to bend freely.
Seabed friction —
C_B = 0by design (Jonkman’s B-7 / B-8 with friction can be added but isn’t standard).Soil-pile interaction below the anchor — the anchor is treated as a fixed reaction point.
The output is a linearised 6 × 6 stiffness matrix suitable
for feeding into the FEM platform-support block. If your
analysis needs dynamic mooring, drive MoorDyn in OpenFAST and
extract the linearised stiffness at the operating point, then
feed that into Tower.from_elastodyn_with_mooring via the
deck.
Numerical scope
Beam element only. Plates / shells / volume elements are out of scope. Section properties come in as a distributed 1-D table.
Linear modal analysis only. Geometric nonlinearity (large deflections), material nonlinearity (yield), and contact / impact are out of scope.
Centrifugal stiffening only on the blade. Tower centrifugal effects (negligible for fixed-base towers) are not modelled.
Rotor aerodynamics are not modelled. A Campbell sweep does not include aeroelastic damping or unsteady aerodynamics — those belong in OpenFAST.
pybmodesanswers the structural-frequency question; resonance checks against the per-rev family are read off the diagram.Single 1.x public API. Names listed in API contract are semver-frozen across 1.x minor releases. Numerical outputs may shift between minor releases when validation tightens or a modelling correction lands; every such shift is called out in Changelog under Fixed / Changed with magnitude and affected case.
Specific edge cases the validation matrix surfaces
NREL 5MW r-test polynomial drift
The polynomial blocks shipped in industry _ElastoDyn.dat
files are not reproducible from the structural-property
blocks in the same files. Worst block: TwSSM2Sh on the
NREL 5MW land deck (file_rms 5.90 vs pyBmodes 0.0023 → 2,529×
ratio).
This is documented as the coefficient-consistency story
(cases/ECOSYSTEM_FINDING.md). pybmodes validate
surfaces it as a per-block PASS / WARN / FAIL verdict;
pybmodes patch rewrites the blocks from the structural
inputs.
This is not a pyBmodes limitation — it’s an artefact of the industry decks themselves. pyBmodes’ role is to surface and fix the drift, not pretend it doesn’t exist.
OC3 Hywind asymmetric platform support
The OC3 Hywind cross-coupled
hydro_K + mooring_K is genuinely asymmetric after the
rigid-arm transformation. Symmetrising it (the pre-1.0 bug
that the validation matrix surfaced) biased the platform
rigid-body modes by ~ 3.7 % — fixed by routing asymmetric K
through scipy.linalg.eig() instead of
scipy.linalg.eigh().
The symmetry check now lives in
pybmodes.fem.solver.solve_modes() and the dispatch is
automatic. If you build a model that produces a symmetric
K you get the cheaper eigh path; the asymmetric path
is only taken when needed.
Pitch / roll coupling on IEA-15 UMaineSemi
pyBmodes’ coupled pitch / roll natural frequency lands ~ 9 % below the Allen 2020 free-decay value (Table 11). This is expected and not a bug — the two answer different questions:
Allen’s free-decay \(f_d\) measures the dominant period observed in a single-DOF excitation of pitch with the other DOFs damped out by nonlinear drag; close to the uncoupled diagonal estimate (0.0364 Hz).
pyBmodes’ eigenvalue is the true coupled linearised mode of the full 6-DOF rigid-body system; surge–pitch off-diagonal added mass \(A_\infty[0,4] = -1.20\times 10^8\) kg·m (and the symmetric sway–roll term) lowers the coupled eigenvalue.
See cases/iea15_deep_diagnostic.md for the full numerical
walk-through and bug audit (zero confirmed bugs in pyBmodes’
eigenproblem assembly).
NREL 5MW monopile soil compliance
The bundled monopile samples use hub_conn = 1 (cantilever
clamped at the SubDyn reaction joint, no soil flexibility) —
matching ElastoDyn’s clamped-base assumption for polynomial
generation. The resulting 1st-FA tower frequency is ~4 %
stiffer than Jonkman 2010’s published 0.275 Hz on OC3
monopile (pyBmodes reports 0.286 Hz).
For quantitative monopile modal analysis with soil included,
use CS_Monopile.bmi (hub_conn = 3 with populated
mooring_K and distributed Winkler distr_k) — bundled
at sample_inputs/reference_turbines/02_nrel5mw_oc3monopile/.
When you have pile geometry and soil properties but no
pre-computed mudline stiffness deck,
pybmodes.MudlineFoundation synthesises the three
coupled springs (K_hh, K_hr, K_rr) from
Shadlou and Bhattacharya (2016) per Yu and Amdahl (2023)
Table 1 (covers homogeneous, parabolic, and linear soil
profiles for both flexible and rigid piles) or from Psaroudakis
et al. (2021) per Yu Eq 25 (homogeneous soil only). The
as_mooring_K() accessor
returns a 6 x 6 block in OpenFAST DOF order that drops into
PlatformSupport.mooring_K of a hub_conn = 3 BMI.
Reproduces the Yu and Amdahl (2023) Table 9 DTU 10 MW anchors
to within 3 percent on K_hh, K_hr, K_rr for both flexible and
rigid reference cases.
The mudline stiffness affects the coupled-system frequency
only; ElastoDyn polynomial generation continues to use the
cantilever path (Tower.from_elastodyn) regardless of soil
flexibility — the same architectural reason the floating-
polynomial section above documents.
What this is not
Not a multi-body dynamics solver. Use OpenFAST + ElastoDyn for time-domain simulation.
Not a aeroelastic modal analysis tool. Use OpenFAST linearisation and modal analysis for linearised modes at the operating point.
Not a CFD or Potential flow code. Hydrodynamics come in as 6 × 6 matrices from WAMIT / HydroDyn potential-flow output.
Not a structural design tool. The supported workflow is analysis of a defined structure, not optimisation of one. Use WISDEM for design.
Not a validation-as-a-service product. Numerical accuracy claims are documented per case in Validation matrix; deltas vs published references are surfaced, not hidden.
Not a Fortran wrapper.
pybmodesis pure-Python,numpy + scipyruntime; it doesn’t call a BModes binary or any other compiled solver under the hood.
When to reach for a different tool
You need |
Use |
|---|---|
Time-domain aero-servo-hydro-elastic simulation |
OpenFAST |
High-fidelity 1-D beam with full nonlinearity |
BeamDyn (inside OpenFAST) |
3-D shell / volume element analysis |
A general FEA package (Abaqus, Ansys, etc.) |
Design optimisation |
WISDEM |
Time-domain dynamic mooring |
MoorDyn (inside OpenFAST) |
WAMIT panel-mesh generation / hydrodynamic database |
WAMIT / NEMOH / Capytaine |
First-order coupled modal frequencies + ElastoDyn polynomial round-trip + validation matrix |
pyBmodes (you are here) |