Releases: SMI-Lab-Inha/pyBModes
Release list
pyBmodes 1.17.0
Highlights
A WindIO tower-ecosystem minor, driven by user feedback. Three additive, non-breaking features.
Added
-
Material / outfitting overrides on the WindIO tower paths (#133).
Tower.from_windioandTower.from_windio_with_monopiletake optionalE,rho,nuandoutfitting_factor. Each defaults toNone(use the ontology value); pass a number to override it, so a material or outfitting sensitivity sweep runs straight off a WindIO file without editing the yaml. On the combined monopile+tower path the override applies to both segments. -
Per-mode generalised mass and stiffness on
ModalResult(#134). A solve now attachesgeneralized_mass(kg) andgeneralized_stiffness(N/m) arrays, one entry per mode, normalised to unit tip lateral displacement, so a mode's modal mass and stiffness can be read off and compared against another tool's modal report.stiffness = (2*pi*f)^2 * massby construction; a rigid-body platform mode (tip barely moves) yieldsNaN. -
Integrated soil-pile interaction on the WindIO monopile path (#118).
Tower.from_windio_with_monopilegains asoilkeyword (a pre-builtMudlineFoundation) and asoil_Eauto-build path. When given, the rigid mudline clamp is replaced by the coupled-spring soil foundation and the model switches to a soft monopile (hub_conn=3), lowering the coupled frequency relative to the rigid clamp. A newMudlineFoundation.from_windio(yaml, soil_E=...)extracts the pile diameter, embedded length and EI at the mudline from the ontology, so only the soil is specified. Reuses the validatedMudlineFoundation(#97). Fully distributed Winklerdistr_ksprings remain a separate higher-fidelity follow-up.
All additive; no public API removed or renamed.
pyBmodes 1.16.1
Highlights
Patch release correcting the WindIO auto-RNA rotor inertia (#130). The auto-RNA (read_windio_rna / lumped_rna_cal=True) previously lumped the blades as a bare point mass at the rotor apex, dropping the rotor's own diametral inertia from the blade mass spread along the span. On IEA-22 that missing term is ~2.5x the value the point-mass lump produced, so tower fore-aft / side-side frequencies came out too high against a rigid-rotor reference. The rotor is now assembled as a proper rigid body about its coned centre of mass.
Total RNA mass is unchanged; the inertia and, for coned rotors, the centre of mass change relative to 1.16.0.
Added
angle_unitscontrol on the WindIO auto-RNA.read_windio_rnagainsangle_units("auto"/"rad"/"deg"), forwarded fromTower.from_windioandTower.from_windio_with_monopileasrna_angle_units. The WindIO v2 schema annotatescone_angle/uptiltas degrees while the IEA reference ontologies store radians;"auto"(default) disambiguates by magnitude, and"rad"/"deg"take the file at its word for the rare all-sub-degree case"auto"cannot resolve.
Fixed
- WindIO auto-RNA now carries the rotor inertia from the spanwise blade mass (#130). The rotor is assembled as a rigid body,
diag([I_polar, I_diam, I_diam])about the rotor centre of mass withI_polar = N_bl · ∫ (dm/ds) · r² dsand the in-plane leverr = (hub_radius + span)·cos(cone)(the coned pitch-axis distance, matching WISDEM/ElastoDyn, with prebend and sweep folded in), using the hub diameter and cone angle when present. A coned rotor's mass sits off the hub plane, so it is placed at its true centre of mass before the parallel-axis shift, which also moves the tower-top centre of mass slightly (by the precone term) for a non-zerocone_angle. The span is measured from the blade root, and a hub tensor's off-diagonal terms are rotated with the correct WindIO hub-frame handedness. Only each blade's own sectional spin inertia is still excluded. - The auto-RNA rejects one- and two-bladed rotors. The axisymmetric
I_polar/2transverse split holds only for three or more evenly spaced blades; a one- or two-bladed rotor has an azimuth-dependent transverse inertia that a single rigid tower-top lump cannot represent, soread_windio_rnanow raises a clearValueErrorpointing at an explicittip_mass.
pyBmodes 1.16.0
Highlights
- WindIO auto-RNA (#82) —
Tower.from_windio(..., lumped_rna_cal=True)andfrom_windio_with_monopile(..., lumped_rna_cal=True)derive the tower-top rotor-nacelle assembly from an IEA-22-class ontology'selastic_properties_mbblocks automatically, so you no longer hand-computetip_mass. Mass and CM match the IEA-22 ElastoDyn deck to better than 0.1 %. - Monopile mudline fix (#121) —
from_windio_with_monopilenow takes awater_depthargument and clamps at the true seabed, dropping the embedded pile below it. Previously an embedded monopile (e.g. IEA-15, axis −75→+15 m, mudline −30 m) was modelled with the buried length as a free cantilever, dragging the frequency far too low. - conda-forge —
conda install -c conda-forge pybmodesnow works alongsidepip install pybmodes.
Install with pip install pybmodes==1.16.0 or conda install -c conda-forge pybmodes (once the feedstock updates).
Added
- Auto-derive the tower-top RNA from a WindIO ontology (#82).
Tower.from_windio(..., lumped_rna_cal=True)andTower.from_windio_with_monopile(..., lumped_rna_cal=True)assemble the hub, nacelle and blades of an IEA-22-class ontology (theelastic_properties_mbschema) into the tower-top lumped mass automatically, so the RNA no longer has to be hand-computed and passed astip_mass. Backed by the newpybmodes.io.windio.read_windio_rna, which mirrors the ElastoDyn tower-top assembler. Mass and centre of mass reproduce the matching IEA-22 ElastoDyn deck to better than 0.1 percent; the rotary inertia is the ontology's own (WindIO-native) value, which is not expected to byte-match a separately authored deck. Ontologies without the hub and nacelle lumped blocks (IEA-15) raise a clear error, so passtip_massexplicitly there.lumped_rna_calis mutually exclusive withtip_mass. - conda-forge availability. pyBmodes is now packaged on conda-forge, so
conda install -c conda-forge pybmodesworks alongsidepip install pybmodes. The feedstock builds the noarch package from each PyPI sdist, so the conda channel tracks PyPI releases. The README and installation guide cover the conda path and how to add the optional dependencies (matplotlib,pyyaml) that the pip extras would otherwise pull in.
Fixed
from_windio_with_monopileclamped the embedded pile instead of the mudline (#121). When a WindIO monopilereference_axis.zruns below the seabed (for example IEA-15, whose axis spans −75 m to +15 m with the mudline at −30 m), the combined cantilever was clamped at the pile tip and the whole embedded length was modelled as a free cantilever, so the fundamental frequency came out far too low. The constructor now takes awater_depthargument (also read from the ontology'senvironment.water_depthwhen present) and clamps at the mudline, dropping the embedded stations below it. With no water depth available the monopile base is still taken as the clamp, so ontologies whose axis already begins at the mudline are unchanged.read_windio_monopile_towergains the samewater_depthkeyword.
pyBmodes 1.15.1
Highlights
A focused ergonomic patch addressing the only piece of feedback on 1.15.0. Adds a one-call wiring from MudlineFoundation into a clamped monopile model so users can convert a from_windio_with_monopile or from_elastodyn_with_subdyn tower to the hub_conn = 3 soft monopile path without hand-building a PlatformSupport or mutating private BMI fields.
Added
-
Tower.attach_mudline_foundation(foundation)wires apybmodes.MudlineFoundationinto the tower's BMI in one call. Creates a freshPlatformSupportcarrying the foundation's 6 x 6mooring_Kblock (with zero hydro, zero platform inertia, and empty distributed arrays), setstow_support = 1, and flipshub_connto3. Returnsselffor chaining, so the canonical pattern is one expression:modal = ( Tower.from_windio_with_monopile("ontology.yaml", tip_mass=991000.0) .attach_mudline_foundation(foundation) .run(n_modes=4) )
Refuses to wire onto a free-base floating model (
hub_conn = 2) or a pinned-free cable model (hub_conn = 4) with a clearValueError. The mudline stiffness affects the coupled-system frequency only; ElastoDyn polynomial coefficient generation continues to use the cantilever path regardless of soil flexibility, the same architectural reasonsrc/pybmodes/_examples/reference_decks/FLOATING_CASES.mdrecords for floating platforms.
Documentation
- Quickstart's soft-monopile recipe now demonstrates the canonical
Tower.from_windio_with_monopile(...).attach_mudline_foundation(f)pattern as the primary path, withas_mooring_K()kept as the compose-it-yourself option for callers wiring into an existingPlatformSupport(theCS_Monopile.bmideck pattern).
Related
- Closes #117 (broken close-comment snippet on #97).
- Partially addresses #118: the ergonomic half (one-call attach) is shipped here; the distributed Winkler distribution along the embedded length stays scoped for a future minor release.
- Merged via #119.
Install
pip install --upgrade pybmodes==1.15.1The package is published via PyPI Trusted Publishing on a green run of the Validation (external data) workflow.
pyBmodes 1.15.0
Highlights
Two additive features on the soft-monopile and floating coupling story. A new geotechnical building block for soil-pile interaction at a soft monopile foundation, plus a diagnostic that reconciles pyBmodes-generated ElastoDyn polynomial coefficients against the coupled-system frequency an OpenFAST linearisation reports. No behaviour change on any existing model path; the new entry points are purely additive.
Added
pybmodes.MudlineFoundationcomputes the coupled-spring soil-pile interaction stiffness (K_hh,K_hr,K_rr) at the mudline of a monopile foundation and returns a 6 x 6 matrix that drops straight intoPlatformSupport.mooring_Kof ahub_conn = 3soft monopile BMI. The classmethodfrom_soil_propertiesaccepts pile geometry and soil properties, applies Randolph (1981) pile classification, and dispatches to either the Shadlou and Bhattacharya (2016) formulas (Yu Table 1, covers homogeneous, parabolic, and linear soil profiles for both flexible and rigid piles) or the Psaroudakis et al. (2021) closed form (Yu Eq 25, homogeneous soil only). 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 new module emits the 6 x 6mooring_Kcontribution only and so affects coupled-system frequencies onhub_conn = 3solves; ElastoDyn polynomial coefficient generation continues to use the cantilever path regardless of soil flexibility, for the same architectural reasonsrc/pybmodes/_examples/reference_decks/FLOATING_CASES.mdrecords for floating platforms.pybmodes.elastodyn.report_floating_frequency_gapruns both a cantilever and a coupled solve on the same floating ElastoDyn deck and returns aFloatingFrequencyGapdataclass naming the gap between the polynomial-basis cantilever 1st FA / SS and the coupled-system 1st FA / SS that an OpenFAST linearisation reports. The two numbers differ by 20-30 percent on a typical floating platform, and the new diagnostic surfaces the gap so users reconciling pyBmodes-generated polynomial coefficients against OpenFAST linearisation output do not have to re-derive the architecture from scratch.format_report()on the result produces a short text block suitable for stdout or a log.
Documentation
src/pybmodes/_examples/reference_decks/FLOATING_CASES.mdnow carries an FAQ section explaining the cantilever-vs-coupled frequency gap and recording the audit trail for the rejected projection-method polynomial-generation proposal (which would have introduced Rayleigh-quotient bias onFreqTFAand double-counted platform restoring against the runtimeSg/Sw/Hv/R/P/YDOFs inElastoDyn.f90:7485-7544). The next person to raise the proposal finds the answer in-tree.- The docstring on
Tower.from_elastodyn_with_mooringnow points atreport_floating_frequency_gapso the diagnostic is discoverable from the constructor users actually call.
Related
- Closes #97 (geotechnical building block for soil-pile interaction;
MudlineFoundationdelivers the coupled-spring foundation surface). - Merged via #114 (feature implementation) and #115 (version bump).
Install
pip install --upgrade pybmodes==1.15.0The package is published via PyPI Trusted Publishing on a green run of the Validation (external data) workflow.
pyBmodes 1.14.1
Fixed
pybmodes --helpcrashed on a non-UTF-8 Windows console. Thewindiosubcommand help carried a rightwards-arrow glyph, so argparse raisedUnicodeEncodeErrorwhen writing the formatted help to a legacy Windows console (cp1252 / cp437) where that character has no mapping. Linux and macOS use UTF-8, so it only surfaced on Windows. The printed CLI help and description strings are now plain ASCII, and a new test asserts every parser's formatted help is ASCII-encodable (and encodes on cp1252 / cp437) so it prints on any code page.
A patch release with no API or numerical change. Caught by the conda-forge Windows build during the conda-forge submission.
pyBmodes 1.14.0
Highlights
An engineering-hardening pass that makes the library fail closed on non-physical input, surface what it could not model, and report the numerical health of every solve. One behaviour change (ERROR-severity pre-solve checks now raise by default), everything else additive.
Changed
- Pre-solve ERROR findings now fail closed.
Tower.run/RotatingBlade.runraisepybmodes.checks.ModelValidationError(aValueErrorsubclass) on any ERROR-severity finding instead of warning and feeding the eigensolver non-physical input. Newon_error="raise"|"warn"keyword (defaultraise). Passon_error="warn"for the pre-1.14.0 behaviour, orcheck_model=Falseto skip. WARN findings still warn. This only affects models that were already producing meaningless output.
Added
ModalResult.diagnostics(SolverDiagnostics). Path taken, sparse-to-dense fallback and reason, mode-count guarantee, per-mode backward-error residuals, and a mass-matrix conditioning estimate. The general path warns when it recovers fewer valid modes than requested. Transient telemetry, excluded from equality and not serialised.ModalResult.ignored_physics. Names parsed-but-not-modelled physics (distributed added massdistr_mtoday) so a result is honest about its fidelity. Persisted when non-empty and shown in the bundled report.- Report completeness stamp.
generate_reportgains astatusargument;run_windiosetscomplete/partial/screeningand carries it onWindioResult.report_status.
Fixed
- WindIO discovery is a structured parse, not a text scan. Candidates are parsed as YAML and checked structurally, with the missing-PyYAML install hint preserved, non-UTF-8 sidecars skipped, and deck discovery scoped by the enclosing project (
.git) boundary so a deeply-nested ontology resolves its decks without climbing into a broad workspace. - BMI parser errors are a first-class diagnostic. The
.bmireader raisespybmodes.io.errors.BMIParseErrorcarrying the file, 1-based line, offending text, and section, instead of a bareValueError/IndexError/EOFError. Still aValueErrorsubclass.
Internal
- Strict mypy ratchet gains
checks,coords,io.errors,workflows._base. - Enforced coverage floor (
fail_under = 85) replaces the informational-only report. pybmodes.campbellno longer re-exports its private helpers at the package root.- README documentation links now point at the rendered Read the Docs pages.
Full detail in the changelog.
pyBmodes 1.13.1
Highlights
Fixes an asymmetric-FOWT labelling bug where the Campbell diagram could name a floating platform's surge/sway rigid-body modes as "1st tower fore-aft/side-to-side", while the mode-shape plots and reports named them correctly (issue #57). No numerical change to any model. This is figure and label text only.
Fixed
- Campbell diagram mislabelled floating-platform rigid-body modes as flexible tower bending modes. On a floating turbine the Campbell sweep could name a low-frequency rigid-body mode (surge or sway near 0.01 Hz)
"1st tower FA"or"1st tower SS", the same name a flexible bending mode near 0.5 Hz carries. So a user reading "1st tower fore-aft" off the diagram (for example to feedplot_environmental_spectra) got the platform frequency instead of the bending frequency. The mode-shape plots and reports were unaffected. There were three root causes, all fixed.- Near-degenerate rigid-body pairs were not recognised as degenerate. A real floater's surge/sway (and roll/pitch) pair is rarely exactly degenerate, because a slightly asymmetric mooring or hull splits it by a fraction of a percent. The eigensolver still returns it in an arbitrary mixed basis that varies run to run. The classifier's degeneracy window was a strict
1e-4, so a sub-percent split fell through un-aligned and was left unnamed in one solve while a sister solve named it. The window is widened to2e-2, so a near-degenerate pair is rotated onto its platform axes and named deterministically, and the report, mode-shape plot and Campbell now agree. The accept-gate that requires a clean two-DOF separation keeps genuinely distinct close modes untouched. - The Campbell tower path solved only
n_tower_modesmodes, so when fewer than six were requested (the default is four) the rigid-block assignment was truncated. The floating tower is now always classified over the full six-mode rigid block, then sliced back, so the Campbell labels match a directTower(...).run().mode_labels. - The Campbell fallback that names modes the classifier still leaves
Nonehad no rigid-versus-flexible distinction. ANonemode inside the rigid-body block is now drawn in the red Platform family, never as flexible"Nth tower FA/SS". Verified across all eleven bundled reference turbines.
- Near-degenerate rigid-body pairs were not recognised as degenerate. A real floater's surge/sway (and roll/pitch) pair is rarely exactly degenerate, because a slightly asymmetric mooring or hull splits it by a fraction of a percent. The eigensolver still returns it in an arbitrary mixed basis that varies run to run. The classifier's degeneracy window was a strict
Changed
- Bending-mode names are spelled out in full on the Campbell and environmental diagrams (
"flapwise bending","edgewise bending","fore-aft bending","side-to-side bending"). Figure text only.CampbellResult.labelskeeps the terse"1st flap"and"1st tower FA"tokens for CSV and API stability.
Docs
- Installation guide gained an "Updating to a new release" section, including how to upgrade a conda-environment install (it is a
pipoperation inside the env, andconda updatedoes not apply).
pyBmodes 1.13.0
Off-axis floating support (#100), a clearer Campbell diagram (#57), and docs-build/README fixes. Additive and backward-compatible — no numerical change to any existing model.
Added (#100)
PlatformSupport.ref_x/ref_y— horizontal position of the hydro/mooring reference (PtfmRefxt/PtfmRefyt) from the tower axis. The rigid-arm transform now carrieshydro_M/hydro_K/mooring_Khorizontally to the tower base (previously only the structural inertia), so an off-axis floater (tower on an off-centre column) can be modelled consistently. Settable on a hand-builtPlatformSupportand round-tripped through the.bmiformat (ref_msl_xyzline). Defaults0.0→ standard on-axis decks byte-identical.PlatformSupport.tower_base_z— positive-up alias fordraft(tower_base_z == -draft).
Changed (#57)
- Campbell diagram — mode-frequency labels in a clean, de-overlapped right-margin column (kept inside the axes for caller-supplied subplots); per-line dashing within each colour band.
Fixed
- Docs build — switched the Sphinx theme from
furo(failing to provision) tosphinx_rtd_theme; added a Read the Docs status badge. - README — repointed documentation links from the 404ing Read the Docs URLs to the GitHub
docs/source; added the conventions guide; added an Updating to a new release section (pip upgrade, version pinning, source-checkout and conda-environment refresh).
Full changelog: https://github.com/SMI-Lab-Inha/pyBModes/blob/master/CHANGELOG.md
pyBmodes 1.12.1
Documentation release — clarifies the reference-frame conventions that were tripping users up. No code-behaviour change (frequencies, shapes and labels are identical to 1.12.0).
Added
- Coordinate-systems / conventions guide (
docs/conventions.rst): a per-case reference (land / monopile / floating / blade) for the single origin (tower base), axis directions, 6-DOF order, boundary conditions (hub_conn), the tower-top/RNA frame, and — for floaters — the MSL vertical datum with the exact signs ofdraft/cm_pform/ref_msl, the inertia-only horizontalcm_pform_x/cm_pform_yarm, and the static-equilibrium assumption. Includes a worked OC3 Hywind example and a common-pitfalls list. - Field-level docstrings on
PlatformSupport,TipMassPropsandBMIFilecarrying the same conventions. Notably documents thatdraftis the signed tower-base elevation relative to MSL (negative = above) — a name inherited from the BModes.bmiformat, not the naval-architecture draft.
Full changelog: https://github.com/SMI-Lab-Inha/pyBModes/blob/master/CHANGELOG.md