Bug description
When BeamDyn is linearized with rotating states (RotStates = True, the default), the state
Jacobians it returns are expressed in a frozen, root-aligned inertial snapshot of the blade
frame, but the .lin file flags those rows/columns RotatingFrame = T. MBC3-based
postprocessors (openfast_toolbox / pyFAST Campbell workflows, ACDC) trust that flag and apply
their rotating-frame Ω-corrections, which injects spurious spin-softening: coupled mode
frequencies droop with rotor speed following f² = f₀² − αΩ² even for modes that should be
speed-independent.
Observable symptom: in a Campbell diagram of the NREL 5-MW with BeamDyn blades, the tower
side-side branch droops from 0.3221 Hz at standstill to 0.2915 Hz at 25 rpm (α ≈ 0.108,
fit residual 9e-4; 2.29 % droop already at 12 rpm). The ElastoDyn-blade reference is flat, and
a RotStates = False control shows only mild physical stiffening — no droop. The same defect
reproduces on the official IEA-15/3.4/10/22 reference models at v5.0.0 tags
(α = 0.143 / 0.267 / 0.124 / 0.234 respectively; raw droop up to ~8 % on IEA-22).
Mechanism: two frame effects are missing from the returned Jacobians —
- the states are anchored to
OtherState%GlbRot (frozen at initialization) rather than the
rotating root frame the RotatingFrame = T flag promises, and
- the transport (Coriolis-type) term of the frame change is absent.
The rotation-only part of this transform existed once (PR #2063) but was removed in
5fbd380f8; note the fix below is not a revert — the deleted code was ≈identity at the
operating point and lacked the transport term, so it never fixed the droop either.
The consistent similarity transform, applied per paired (q, dq/dt) state triplet
(pairing offset Nx/2), is
A_r = S·A·S⁻¹ + blkdiag(−ω̃) with S = [[I, 0], [−ω̃, I]],
B_r = S·B, C_r = C·S⁻¹
where ω is the full 3-vector root RotationVel mapped into the GlbRot basis (scalar Ω is
not sufficient — e.g. precone puts a z-component of −0.0548 rad/s at 12 rpm on the 5-MW). The
transform is exactly identity at standstill.
To Reproduce
- Compile
dev (double precision) at or near 0160d29a0.
- Run a NREL 5-MW
CompElast = 2 linearization sweep, 0–25 rpm (CalcSteady, 1 azimuth
snapshot suffices), with OutFmt "ES18.9E3" so the .lin write precision does not add a
second artifact (see the companion OutFmt issue).
- Post-process with any MBC3/Campbell tool and track the tower side-side branch.
- The branch follows
f² = f₀² − αΩ² instead of staying flat; repeating with ElastoDyn
blades or RotStates = False removes the droop.
Expected behavior
Jacobians flagged RotatingFrame = T are frame-consistent with that flag, so
speed-independent branches stay flat: with the fix below, 5-MW tower-SS flatness across
0–25 rpm improves from 2.29 % (at 12 rpm) to 0.015 %, and the IEA fleet flattens to
0.111 % / 0.029 % / 0.026 % / 0.379 % (IEA-15/3.4/10/22) — the IEA-22 residual is also present
in the independent python-side reference transform (0.361 %) and is a known second-order
effect (large-prebend blade / Wiener-Milenković first-order limit), not part of this defect.
Fix
Implemented and fleet-verified on branch
mayankchetan/openfast:f/bd-rotstates-framefix
— PR to follow. The transform is applied only at linearization snapshots (a new optional
IsLin flag threaded from the glue), so the tight-coupling solver and AeroMap keep the raw
frozen-frame Jacobians their Newton iterations require. Standstill .lin output is
bit-identical to unpatched.
Known limitation (documented, not addressed): floating platforms — platform/yaw rates fold
into the root ω, where MBC3's scalar-Ω assumption independently breaks down anyway.
OpenFAST Version
dev @ 0160d29a0 (also reproduced on v5.0.0 tags of the IEA reference models); double
precision, gfortran, RelWithDebInfo, macOS (arm64).
Additional context
Discovered while chasing anomalous tower frequencies in BD-blade Campbell diagrams. A second,
independent defect was isolated during the same investigation — .lin files are written with
the tabular OutFmt (~4 significant digits), which corrupts wide-dynamic-range BD Jacobians
on disk — filed separately with fix PR #3393. The 5-MW evidence above is immune to that
artifact by construction (diagonal sectional matrices) and was additionally re-baselined with
wide-format decks.
Developed with Anthropic Claude (Claude Code) under human direction.
Bug description
When BeamDyn is linearized with rotating states (
RotStates = True, the default), the stateJacobians it returns are expressed in a frozen, root-aligned inertial snapshot of the blade
frame, but the
.linfile flags those rows/columnsRotatingFrame = T. MBC3-basedpostprocessors (openfast_toolbox / pyFAST Campbell workflows, ACDC) trust that flag and apply
their rotating-frame Ω-corrections, which injects spurious spin-softening: coupled mode
frequencies droop with rotor speed following
f² = f₀² − αΩ²even for modes that should bespeed-independent.
Observable symptom: in a Campbell diagram of the NREL 5-MW with BeamDyn blades, the tower
side-side branch droops from 0.3221 Hz at standstill to 0.2915 Hz at 25 rpm (α ≈ 0.108,
fit residual 9e-4; 2.29 % droop already at 12 rpm). The ElastoDyn-blade reference is flat, and
a
RotStates = Falsecontrol shows only mild physical stiffening — no droop. The same defectreproduces on the official IEA-15/3.4/10/22 reference models at v5.0.0 tags
(α = 0.143 / 0.267 / 0.124 / 0.234 respectively; raw droop up to ~8 % on IEA-22).
Mechanism: two frame effects are missing from the returned Jacobians —
OtherState%GlbRot(frozen at initialization) rather than therotating root frame the
RotatingFrame = Tflag promises, andThe rotation-only part of this transform existed once (PR #2063) but was removed in
5fbd380f8; note the fix below is not a revert — the deleted code was ≈identity at theoperating point and lacked the transport term, so it never fixed the droop either.
The consistent similarity transform, applied per paired
(q, dq/dt)state triplet(pairing offset Nx/2), is
where ω is the full 3-vector root
RotationVelmapped into theGlbRotbasis (scalar Ω isnot sufficient — e.g. precone puts a z-component of −0.0548 rad/s at 12 rpm on the 5-MW). The
transform is exactly identity at standstill.
To Reproduce
dev(double precision) at or near0160d29a0.CompElast = 2linearization sweep, 0–25 rpm (CalcSteady, 1 azimuthsnapshot suffices), with
OutFmt "ES18.9E3"so the.linwrite precision does not add asecond artifact (see the companion OutFmt issue).
f² = f₀² − αΩ²instead of staying flat; repeating with ElastoDynblades or
RotStates = Falseremoves the droop.Expected behavior
Jacobians flagged
RotatingFrame = Tare frame-consistent with that flag, sospeed-independent branches stay flat: with the fix below, 5-MW tower-SS flatness across
0–25 rpm improves from 2.29 % (at 12 rpm) to 0.015 %, and the IEA fleet flattens to
0.111 % / 0.029 % / 0.026 % / 0.379 % (IEA-15/3.4/10/22) — the IEA-22 residual is also present
in the independent python-side reference transform (0.361 %) and is a known second-order
effect (large-prebend blade / Wiener-Milenković first-order limit), not part of this defect.
Fix
Implemented and fleet-verified on branch
mayankchetan/openfast:f/bd-rotstates-framefix— PR to follow. The transform is applied only at linearization snapshots (a new optional
IsLinflag threaded from the glue), so the tight-coupling solver and AeroMap keep the rawfrozen-frame Jacobians their Newton iterations require. Standstill
.linoutput isbit-identical to unpatched.
Known limitation (documented, not addressed): floating platforms — platform/yaw rates fold
into the root ω, where MBC3's scalar-Ω assumption independently breaks down anyway.
OpenFAST Version
dev@0160d29a0(also reproduced on v5.0.0 tags of the IEA reference models); doubleprecision, gfortran, RelWithDebInfo, macOS (arm64).
Additional context
Discovered while chasing anomalous tower frequencies in BD-blade Campbell diagrams. A second,
independent defect was isolated during the same investigation —
.linfiles are written withthe tabular
OutFmt(~4 significant digits), which corrupts wide-dynamic-range BD Jacobianson disk — filed separately with fix PR #3393. The 5-MW evidence above is immune to that
artifact by construction (diagonal sectional matrices) and was additionally re-baselined with
wide-format decks.
Developed with Anthropic Claude (Claude Code) under human direction.