Skip to content

Commit bf23bbe

Browse files
committed
fix: reject one- and two-bladed rotors in the auto-RNA inertia path
The rotor tensor uses the axisymmetric transverse split I_diam = I_polar/2 + N*integral(dm*a**2), which is exact only for three or more evenly spaced blades, whose mass ring has isotropic in-plane inertia. A one- or two- bladed rotor lies on a single diameter, so its transverse inertia is azimuth-dependent and no single static tower-top lump represents it (one in-plane axis carries almost all of I_polar, the orthogonal one almost none). Rather than emit a corrupted fore-aft / side-side inertia, reject those blade counts with a message pointing at an explicit tip_mass. Zero blades (bare tower) and the usual three-bladed rotors are unaffected.
1 parent 9029679 commit bf23bbe

2 files changed

Lines changed: 31 additions & 0 deletions

File tree

src/pybmodes/io/windio.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,21 @@ def _nac_geom(key: str) -> float:
10931093
f"assembly.number_of_blades must be a non-negative integer; got "
10941094
f"{n_blades!r}."
10951095
)
1096+
if n_blades in (1, 2):
1097+
# The rotor tensor uses the axisymmetric transverse split
1098+
# ``I_diam = I_polar/2 + …``, which is exact only for three or more
1099+
# evenly spaced blades (a symmetric mass ring has isotropic in-plane
1100+
# inertia). A one- or two-bladed rotor lies on a single diameter, so
1101+
# its transverse inertia is azimuth-dependent and no single static
1102+
# tower-top lump represents it. Reject rather than emit a corrupted
1103+
# fore-aft / side-side inertia (issue #130 review).
1104+
raise ValueError(
1105+
f"assembly.number_of_blades = {n_blades}: the auto-RNA rotor "
1106+
f"inertia assembly assumes an azimuthally symmetric rotor (three "
1107+
f"or more evenly spaced blades). A one- or two-bladed rotor has an "
1108+
f"azimuth-dependent transverse inertia that a single rigid tower-"
1109+
f"top lump cannot represent; pass an explicit tip_mass instead."
1110+
)
10961111
orientation = str(assembly.get("rotor_orientation", "upwind")).strip().lower()
10971112
if orientation not in {"upwind", "downwind"}:
10981113
raise ValueError(

tests/test_windio_rna.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,22 @@ def _mk(cone: float):
200200
assert coned.mass == pytest.approx(flat.mass)
201201

202202

203+
@pytest.mark.parametrize("n_bl", [1, 2])
204+
def test_rna_rejects_one_or_two_bladed_rotor(
205+
tmp_path: pathlib.Path, n_bl: int
206+
) -> None:
207+
"""A one- or two-bladed rotor is not azimuthally symmetric, so the
208+
``I_polar/2`` transverse split is invalid and the auto-RNA rejects it
209+
with a message pointing at an explicit tip_mass (Codex review on #130)."""
210+
pytest.importorskip("yaml")
211+
from pybmodes.io.windio import read_windio_rna
212+
213+
o = _base_ontology()
214+
o["assembly"]["number_of_blades"] = n_bl
215+
with pytest.raises(ValueError, match="azimuthally symmetric"):
216+
read_windio_rna(_write(o, tmp_path, f"nb_{n_bl}.yaml"))
217+
218+
203219
def test_rna_rotor_inertia_includes_sweep(tmp_path: pathlib.Path) -> None:
204220
"""Sweep (reference_axis.y) sets an in-plane tangential distance from the
205221
shaft axis, so it adds ``y²`` to the rotor polar lever. A constant

0 commit comments

Comments
 (0)