Data sources

Every input format pybmodes reads, the bundled examples that ship with the wheel, and the convention for staging upstream third-party data under external/.

Supported input formats

Format

Read by

BModes .bmi main + section-properties .dat

pybmodes.io.bmi.read_bmi(); also Tower(bmi_path) and RotatingBlade(bmi_path) for the high-level path.

OpenFAST ElastoDyn main + tower + blade .dat

pybmodes.io.elastodyn_reader; pybmodes.models.Tower.from_elastodyn() and pybmodes.models.RotatingBlade.from_elastodyn().

OpenFAST SubDyn .dat (monopile pile geometry)

pybmodes.io.subdyn_reader; pybmodes.models.Tower.from_elastodyn_with_subdyn().

OpenFAST HydroDyn .dat + WAMIT .1 / .hst potential-flow output

pybmodes.io.wamit_reader.HydroDynReader (A_inf, A_0, C_hst 6 × 6 matrices, SI re-dimensionalised).

OpenFAST MoorDyn .dat (v1 CONNECTION and v2 POINT layouts)

pybmodes.mooring.MooringSystem.from_moordyn().

BModes .out reference output

pybmodes.io.out_parser.read_out() — tolerant by default; strict=True raises with file / line / mode context on short, non-numeric, non-finite, duplicate, or empty content.

WISDEM / WindIO ontology .yaml — tubular tower / monopile

pybmodes.io.windio.read_windio_tubular(); pybmodes.models.Tower.from_windio().

WISDEM / WindIO ontology .yaml — composite blade

pybmodes.io.windio_blade.read_windio_blade(); pybmodes.models.RotatingBlade.from_windio().

WISDEM / WindIO ontology .yaml — floating substructure

pybmodes.io.windio_floating.read_windio_floating(); pybmodes.models.Tower.from_windio_floating().

BModes .bmi — the canonical input

A .bmi file is line-ordered ASCII with comment lines starting at column 1. The library is tolerant of trailing comments after data lines. Header fields drive the FEM configuration; the section-properties table comes from a separate _sec_props.dat referenced by the .bmi header.

A minimal header (rotating uniform blade, beam_type = 1, hub_conn = 1, no platform support):

======================  pyBmodes test deck  ======================
Title:    rotating uniform blade

beam_type      1     1 = blade, 2 = tower
romg_g       0.5     gravity acceleration (g)
rot_rpm       7.5    rotor speed (rpm)
precone       0.0
bl_p_ang      0.0    blade pitch angle (deg)
bl_length    61.5
radius       63.0    radius to blade tip (m)
hub_rad       1.5    radius to blade root (m)
n_secs       50      number of FE section nodes

hub_conn      1      1 = cantilever, 2 = free-free, 3 = monopile, 4 = pinned-free
...

For tower decks (beam_type = 2) the floating-platform block follows the structural section. See the bundled sample_inputs/ reference turbines for full canonical layouts.

OpenFAST ElastoDyn — the industry path

Tower.from_elastodyn reads three files reached from one path:

  1. The main .dat (tower height, RNA mass, platform 6-DOF parameters, polynomial blocks).

  2. The TwrFile it points at (distributed tower properties).

  3. The BldFile(1) it points at — read only to lump the rotor mass into the tower-top assembly, not solved as a blade.

The polynomial blocks the validator and patcher work on:

--- TOWER FORE-AFT MODE 1 SHAPE COEFFICIENTS ---
     0.7004     TwFAM1Sh(2) - Mode 1, coefficient of x^2 term
     2.1963     TwFAM1Sh(3) - Mode 1, coefficient of x^3 term
    -5.6202     TwFAM1Sh(4) - Mode 1, coefficient of x^4 term
     6.1209     TwFAM1Sh(5) - Mode 1, coefficient of x^5 term
    -2.3974     TwFAM1Sh(6) - Mode 1, coefficient of x^6 term

Same shape for TwFAM2Sh (FA mode 2), TwSSM1Sh, TwSSM2Sh, and the four blade Bld... blocks. The coefficients are \(c_2, c_3, c_4, c_5, c_6\) of \(\mathrm{SHP}(s) = \sum_{i=2}^{6} c_i\, s^{\,i}\) with the constraint \(\sum c_i = 1\) (mode shape unit at tip in the same nondim). pyBmodes’s writer re-fits and rewrites these blocks in place; the validator scores the file’s blocks against pyBmodes’s own fit and emits a per-block PASS / WARN / FAIL verdict with the file-RMS / pyBmodes-RMS ratio.

OpenFAST SubDyn — monopile pile geometry

The SubDyn parser handles the joints + members + base-reaction- joint layout of an OC3-style monopile. The supported model is a fixed-base combined cantilever (clamped at the SubDyn reaction joint, no soil flexibility): the pile is structurally spliced below the ElastoDyn tower into a single beam.

For soft-pile soil compliance ( hub_conn = 3 with a populated mooring_K and a distributed distr_k along the embedded section), use the CS_Monopile.bmi reference deck pattern directly — the corresponding BMI is bundled in sample_inputs/reference_turbines/02_nrel5mw_oc3monopile.

WAMIT / HydroDyn output

HydroDyn .dat carries a PotFile pointer to a WAMIT output filename stem:

"IEA-15-240-RWT-UMaineSemi"   PotFile

The reader chains through to <stem>.1 (frequency-domain added-mass + radiation damping) and <stem>.hst (hydrostatic restoring), parses both, applies the WAMIT v7 re-dimensionalisation (ρ · L^k / ρ · g · L^k, \(k \in \{2..5\}\) per DOF-pair type), and returns a WamitData with SI A_inf, A_0, and C_hst 6 × 6 matrices ready for Tower.from_elastodyn_with_mooring.

Fortran-style D / d exponent notation is handled; upper-triangle-only output files are mirrored symmetric.

OpenFAST MoorDyn — both layouts

The MoorDyn parser handles both version layouts:

  • v1 uses CONNECTION blocks with positional columns.

  • v2 uses POINT blocks (renamed); the parser detects the version by header keyword.

The point-ID-validated column-order detection prevents a silent swap when v1 and v2 deck conventions differ in column ordering. MooringSystem.from_moordyn(dat_path) returns a fully-formed mooring system; MooringSystem.stiffness_matrix(body_r6=None) returns the linearised 6 × 6 stiffness in OpenFAST DOF order.

WISDEM / WindIO ontology

WindIO .yaml ontologies are the WISDEM-side schema for reference wind-turbine definitions. pybmodes reads three sub-trees:

Tubular tower / monopile

A pure circular-tube reduction is applied to the outer_shape + structure (modern dialect) or outer_shape_bem + internal_structure_2d_fem (older dialect) blocks. Both are tolerated; WISDEM’s duplicate-anchor files (IEA-10) parse via a custom SafeLoader.

Composite blade

A PreComp-class thin-wall multi-cell Bredt–Batho reduction of the layup, with classical-lamination theory at each station:

  • Airfoil nd_arc spine + anchor-registry-resolved sectoral coordinates per ply.

  • Per-ply Qbar from material orientation + the lamina Q-matrix.

  • Cell-by-cell A, B, D matrices and the Bredt–Batho torsion compliance.

  • Decoupled output: per-station EA, EI_flap, EI_edge, GJ, plus mass distribution.

Validated against the turbine’s own BeamDyn 6 × 6 on IEA-3.4 / 10 / 15 / 22 ontologies (mass and EA PreComp-class; GJ and EI diagonal-reduction approximate, documented).

When a elastic_properties or elastic_properties_mb block is present, the published distributed properties are preferred by default (elastic="auto") — this minimises deltas against the reference model. elastic="precomp" forces the layup reduction; elastic="file" requires the published properties and raises if absent.

Floating substructure

For a components.floating_platform block, Tower.from_windio_floating is two-tier:

  • Industry-grade when companion HydroDyn + MoorDyn + ElastoDyn decks are supplied (or auto-discovered by the pybmodes windio CLI scoped to the turbine root) — byte-identical to Tower.from_elastodyn_with_mooring.

  • Screening preview when only the yaml is supplied — member-Morison hydrodynamics, RAFT-style end-cap added mass, catenary mooring from the yaml. Emits one UserWarning naming the result as SCREENING-fidelity (NOT industry-grade).

BModes .out — the reference output format

For validation comparisons against BModes JJ runs, the .out reader is tolerant by default — it returns NaN for unparseable rows so a partial output doesn’t crash the whole read. For automated validation in CI use strict=True:

from pybmodes.io.out_parser import read_out

modes = read_out("ref.out", strict=True)

Strict mode raises with file / line / mode context on short rows, non-numeric fields, non-finite values, duplicate mode numbers, or empty content.

Bundled examples (ship in every wheel)

The _examples package vendors two redistributable trees as setuptools.package-data:

src/pybmodes/_examples/sample_inputs/

pyBmodes-authored, Apache 2.0-licensed .bmi and section- properties .dat files. Four analytical-reference cases at the top level:

  • 01_uniform_blade/ — uniform isotropic cantilever blade vs. Bernoulli-Euler closed form.

  • 02_tower_topmass/ — uniform tower with concentrated top mass vs. Blevins.

  • 03_rotating_uniform_blade/ — rotating uniform blade vs. Wright 1982 / Bir 2009 Table 3a.

  • 04_pinned_free_cable/ — pinned-free rotating cable vs. Bir 2009 Eq. 8 (the hub_conn = 4 BC).

Plus eleven reference-turbine samples under reference_turbines/:

  • 01_nrel5mw_land, 02_nrel5mw_oc3monopile, 03_iea34_land, 04_iea10_monopile, 05_iea15_monopile, 06_iea22_monopile, 07_nrel5mw_oc3hywind_spar, 08_nrel5mw_oc4semi, 09_iea15_umainesemi, 10_iea22_semi, 11_upscale25_centraltower

verify.py runs all four analytical references at <1 % RMS vs. their closed form; reference_turbines/build.py regenerates each sample from upstream ElastoDyn decks.

src/pybmodes/_examples/reference_decks/

Six pre-patched ElastoDyn decks — every coefficient block reaches PASS or WARN; no FAIL after patching:

  • Fixed-base: nrel5mw_land/, nrel5mw_oc3monopile/, iea34_land/.

  • Floating: nrel5mw_oc3spar/ (OC3 Hywind), nrel5mw_oc4semi/ (OC4 DeepCwind semi), iea15mw_umainesemi/ (UMaine VolturnUS-S).

The floating decks use the cantilever path (Tower.from_elastodyn, not Tower.from_bmi with hub_conn=2) because ElastoDyn’s polynomial ansatz assumes clamped-base modes regardless of platform configuration — see Theory and Limitations.

Vendor either tree out:

pybmodes examples --copy ./my_examples            # both trees
pybmodes examples --copy ./decks --kind decks     # decks only
pybmodes examples --copy ./samples --kind samples # samples only

Works whether you installed from PyPI or in editable mode from source — the resolver finds _examples/ relative to pybmodes.__file__.

Local-only upstream data (external/)

The integration-marked tests and several cases/ walk-throughs read upstream OpenFAST / BModes / WindIO data that is not bundled — typically because it lives in a different GitHub repository whose licence does not permit redistribution. The convention is to clone each under external/:

external/
  OpenFAST_files/
    r-test/                 OpenFAST regression-test suite
    IEA-3.4-130-RWT/        github.com/IEAWindTask37/IEA-3.4-130-RWT
    IEA-10.0-198-RWT/       github.com/IEAWindTask37/IEA-10.0-198-RWT
    IEA-15-240-RWT/         github.com/IEAWindTask37/IEA-15-240-RWT
    IEA-22-280-RWT/         github.com/IEAWindTask37/IEA-22-280-RWT
    WISDEM/                 github.com/WISDEM/WISDEM
  BModes/
    CertTest/               BModes CertTest reference outputs
    docs/examples/          CS_Monopile.bmi + OC3Hywind.bmi
  MoorPy/                   github.com/NREL/MoorPy (cross-checks)
  RAFT/                     github.com/WISDEM/RAFT (cross-checks)
  references/               PDF / TR references (Jonkman 2010 etc.)

All external/ paths are gitignored. A fresh clone runs the self-contained test suite without any of this data; the integration marker gates the data-dependent subset and skips cleanly when the data is absent.

Independence stance

The default pytest run is self-contained — every test in it runs from numbers either constructed inline in the test or validated against published closed-form formulas. No third-party reference data is bundled in the repo for this default run. Any commit that re-introduces a .bmi / .dat / .out file under tests/data/ for a default-run test should be questioned.

CI runs both the default and the integration suite; the integration step tolerates pytest exit code 5 (“no tests collected”) so the job stays green on a runner without the upstream data, but fails on any other non-zero exit so a custom workflow run that does have the data surfaces real failures immediately.