Skip to content

Commit e38287b

Browse files
committed
fix: project the hub radius through precone in the rotor lever
The polar lever left the hub radius unconed in the rotor plane and dropped it from the axial offset. WISDEM and ElastoDyn place the blade root at the hub radius along the coned pitch axis, so the whole (hub_r + span) length projects through the cone (WISDEM rotorR = Rtip*cos(precone) with Rtip = Rhub + L). Fold hub_r into the along-axis distance before projecting: radial = (hub_r + span)*cos(cone) - x*sin(cone), axial = (hub_r + span)* sin(cone) + x*cos(cone). For a coned rotor with a real hub radius this adds the Rhub*sin(cone) shaft-axis offset that was missing. Unconed rotors (cone = 0) are unchanged.
1 parent 6871e4f commit e38287b

2 files changed

Lines changed: 35 additions & 7 deletions

File tree

src/pybmodes/io/windio.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -891,10 +891,11 @@ def _blade_span_mass_inertia(
891891
892892
Returns the single-blade mass, the polar second moment about the rotor
893893
axis ``∫ (dm/ds) · (r² + t²) ds`` (in-plane radial
894-
``r = hub_r + z·cone_cos − x·cone_sin`` and tangential ``t = y`` so
895-
prebend / sweep are included), the axial second moment
896-
``∫ (dm/ds) · a(s)² ds`` (axial offset ``a = z·cone_sin + x·cone_cos``),
897-
and the axial first moment ``∫ (dm/ds) · a(s) ds``.
894+
``r = (hub_r + z)·cone_cos − x·cone_sin`` and tangential ``t = y`` so the
895+
coned hub radius, prebend and sweep are all included), the axial second
896+
moment ``∫ (dm/ds) · a(s)² ds`` (axial offset
897+
``a = (hub_r + z)·cone_sin + x·cone_cos``), and the axial first moment
898+
``∫ (dm/ds) · a(s) ds``.
898899
The caller builds the rotor's polar inertia from the second radial
899900
moment, the coned transverse (diametral) inertia from the radial and
900901
axial second moments, and places the rotor body at its coned centre of
@@ -980,9 +981,15 @@ def _blade_span_mass_inertia(
980981
# ``r² + t²`` (so sweep and prebend are not dropped, issue #130); the
981982
# axial offset feeds the coned transverse term.
982983
x_off, y_off, span = coords[0], coords[1], coords[2]
983-
radial = hub_r + span * cone_cos - x_off * cone_sin
984+
# Distance from the hub centre along the (coned) pitch axis: the blade
985+
# root sits at the hub radius and each section a further `span` outboard,
986+
# so the whole (hub_r + span) length projects through the cone, matching
987+
# WISDEM/ElastoDyn (rotorR = Rtip*cos(precone) = (Rhub + L)*cos(cone))
988+
# rather than leaving the hub radius unconed in the rotor plane.
989+
axis_dist = hub_r + span
990+
radial = axis_dist * cone_cos - x_off * cone_sin
984991
tangential = y_off
985-
axial = span * cone_sin + x_off * cone_cos
992+
axial = axis_dist * cone_sin + x_off * cone_cos
986993
polar_second_moment = _second_moment(mpl, radial, s) + _second_moment(
987994
mpl, tangential, s
988995
)
@@ -1035,7 +1042,7 @@ def read_windio_rna(
10351042
10361043
Blade inertia (issue #130): the rotor carries the diametral inertia
10371044
``N_bl · ∫ (dm/ds) · r² ds`` from the blade mass spread along the span
1038-
(``r = hub_radius + span·cos(cone)``), as ``diag([I_polar, I_polar/2,
1045+
(``r = (hub_radius + span)·cos(cone)``), as ``diag([I_polar, I_polar/2,
10391046
I_polar/2])`` about the hub (the same perpendicular-axis form as the
10401047
hub tensor). Only each blade's own *sectional* spin inertia is left out
10411048
(its parallel-axis / span contribution is captured). This intentionally

tests/test_windio_rna.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,27 @@ def _mk(y_off: float):
347347
assert swept.izz > straight.izz
348348

349349

350+
def test_rna_hub_radius_projected_through_cone(tmp_path: pathlib.Path) -> None:
351+
"""The hub radius is coned like the span (WISDEM Rtip*cos(precone)), so a
352+
coned rotor with a hub radius carries an extra Rhub*sin(cone) axial offset
353+
that raises the tower-top vertical CM through the shaft tilt, versus the
354+
same rotor with a zero hub radius (Codex review on #130)."""
355+
pytest.importorskip("yaml")
356+
from pybmodes.io.windio import read_windio_rna
357+
358+
def _mk(hub_d: float):
359+
o = _base_ontology()
360+
o["components"]["hub"]["cone_angle"] = 0.15
361+
o["components"]["hub"]["diameter"] = hub_d
362+
o["components"]["nacelle"]["drivetrain"]["uptilt"] = 0.10
363+
return read_windio_rna(_write(o, tmp_path, f"hr_{hub_d}.yaml"))
364+
365+
no_hub = _mk(0.0) # hub_r = 0
366+
with_hub = _mk(6.0) # hub_r = 3
367+
assert with_hub.mass == pytest.approx(no_hub.mass)
368+
assert with_hub.cm_axial > no_hub.cm_axial
369+
370+
350371
def test_rna_coned_cm_shift_with_uptilt(tmp_path: pathlib.Path) -> None:
351372
"""A coned rotor's CM sits off the hub apex along the (tilted) shaft, so
352373
with uptilt the tower-top vertical CM shifts by m_blades·offset·sin(uptilt)

0 commit comments

Comments
 (0)