This document covers shared BEM infrastructure: coordinate system, kinematics, the BEM force kernel, QS BEM solver, VRS correction, servo-flap, output assembly, blade flapping, the inflow integrator, numerical floors, model comparison, and validation summary.
Model-specific design notes live in:
- PITT_PETERS_DESIGN.md -- Pitt-Peters 3-state dynamic inflow
- OYE_DESIGN.md -- Oye 2-stage annular dynamic inflow
- VPM_DESIGN.md -- VPM free-wake solver
The architecture is a blade-element momentum (BEM) solver with replaceable dynamic-inflow sub-models, all sharing a common azimuth-radial sweep kernel.
The model is a map from rotor inputs and the current inflow state to the aerodynamic loads and the time-derivative of the inflow state. The derivative is integrated externally (the trim/envelope drivers), closing the dynamic-inflow loop across time steps.
flowchart TD
IN["Inputs u + inflow state lambda"]
IN --> KIN["Kinematics<br/>v_climb, mu, V_mf"]
KIN --> PITCH["Blade pitch theta(psi)<br/>cyclic / servo-flap"]
PITCH --> SWEEP["psi x r sweep<br/>alpha to cl, cd to dT, dQ"]
KIN --> SWEEP
SWEEP --> INTEG["Disk integrals<br/>T, Q, Mx_hub, My_hub"]
INTEG --> LOADS["Loads (F, M, Q)"]
INTEG -- "dynamic inflow only" --> DERIV["Inflow update<br/>Pitt-Peters / Oye<br/>to d lambda/dt"]
LOADS --> OUT["AeroResult"]
DERIV --> OUT
QuasiStatic and the dynamic-inflow models split at the disk integrals.
QuasiStatic has no inflow state: each element's inflow ratio
NED (North-East-Down) throughout, without exception:
| Axis | Direction |
|---|---|
| +X | North |
| +Y | East |
| +Z | Down |
Gravity acts in the +Z direction. Rotor thrust (upward) is
F_world[2] < 0. The rotor spins counter-clockwise (CCW) when viewed
from above (American convention: Bell / Sikorsky / Boeing).
Azimuth psi is measured from the +X (hub forward) axis, increasing in the direction of blade motion (CCW from above):
The advancing side is at +Y (East) in forward flight along +X.
Called once per compute_forces invocation. Shared by all three models
(bem_common::kinematics).
Given rotor speed
The mass-flow speed at the disk (Glauert mass-flow scalar
where
Symbol note.
$V_\text{mf}$ is the resultant flow speed through the disk used to scale the dynamic-inflow time constants. It is not the blade tip speed$\Omega R$ and not the blade-element tangential velocity$v_t = \Omega r + v_{t,\text{extra}}$ of Section 6. In the Rust source it isv_mass_flow_disk(localsv_mf); the tangential velocity staysv_t. The induced velocity$v_0$ is dimensional (m/s); its non-dimensional counterpart is$\lambda_0 = v_0 / \Omega R$ .
Swashplate tilts (tilt_lon, tilt_lat) map to blade-pitch Fourier
harmonics via cyclic_coeffs (dynbem_rs/src/cyclic.rs). With
swashplate phase tilt_lon and tilt_lat):
Helicopter-standard sign convention: tilt_lon > 0 produces nose-down
disk tilt (forward stick); tilt_lat > 0 produces roll-right.
Per-azimuth blade pitch:
where
Both tabulated and analytical polars expose a unified Polar trait
(dynbem_rs/src/polar.rs). The inner loop calls polar.cl_cd(alpha),
which is a binary-search linear interpolation over sampled angle-of-attack
tables (PolarTable). Analytical polars (e.g. flat-plate) are
pre-sampled to 4001 points over
Applied in the quasi-static BEM iteration and Oye's quasi_static_bem.rs):
Combined loss: MIN_LOSS_FACTOR =
Here
element_force (bem_common.rs, #[inline(always)]): given prescribed
axial velocity
The in-plane tangential wind from forward flight:
The disk loads are obtained by integrating the element forces over the
rotor disk:
and the tangential velocity at element
Hub-frame moments from the thrust distribution:
These conventions are shared by all three models — the
prescribed-inflow models (Pitt-Peters, Oye) feed
The internal axial freestream is taken as
| Flow through disk | Regime | |
|---|---|---|
| downward | helicopter climb / normal inflow | |
| — | hover | |
| upward | autorotation / flying wind turbine |
The torque sign follows automatically from the inflow sign, with no
special-casing. In autorotation (upward wind, so the net axial inflow
A negative aerodynamic torque drives the rotor: the external integrator
Description. The classical blade-element momentum model with no inflow
memory: at every call the local inflow ratio
Advantages. Numerically robust across the whole envelope (hover, climb, descent, windmill) thanks to the inflow-ratio formulation and the dedicated Ning/Buhl turbine branch; no state to integrate, so no time-step stability constraint and no transient to settle; the only model that resolves the high-induction turbulent-wake state physically.
Disadvantages. No inflow dynamics -- it cannot represent the lag between a control input and the wake's response, so it is wrong during fast transients and gives no dynamic-inflow phase shift; per-element iteration makes it the most expensive per call; inflow is azimuthally resolved only through the loading, with no explicit harmonic feedback.
BEMModel / solve_bem_element (quasi_static_bem.rs). Each element's
inflow ratio
The local solidity:
The momentum-BEM quadratic (helicopter mode,
Rearranged to standard form and solved explicitly; root selected by sign
of
Symbol note.
$\lambda_\text{climb} = v_\text{climb}/\Omega R$ is the non-dimensional climb inflow ratio (Rust locallambda_climb). Do not confuse it with the Pitt-Peters cosine harmonic$\lambda_c$ of Section 10 (Rust state fieldlambda_c) -- a different quantity that happens to share the bare letter$c$ . The two never appear in the same model.
For the energy-extracting regime (upward axial flow,
where
Buhl turbulent-wake correction. Classical momentum theory predicts a
decreasing thrust for
Equating this to the blade-element thrust
the physical root being the smaller one in
If no sign-changing
In the QuasiStatic model the per-element momentum balance is converged
inside the azimuth sweep, so the inflow is self-consistent at every
The Ning 2014 windmill formulation, as used in OpenFAST AeroDyn, was
derived for axial (turbine) operation where in-plane wind is negligible.
In a helicopter or autorotating kite rotor descending at an oblique angle,
however, each blade element has significant tangential velocity from
forward flight:
The problem with the axial-only residual. The standard Brent residual
implicitly assumes
Corrected residual. The extended windmill residual used in dynbem carries the extra tangential velocity explicitly. Defining a local speed ratio that includes in-plane wind:
the Brent residual becomes:
and the element force kernel receives the true tangential velocity
Natural bracket-existence filter. Instead of an artificial guard
(v_edge < |v_climb| threshold), the corrected solver simply checks
whether the residual changes sign across the bracket
- At azimuths where forward flight adds to tangential velocity (advancing side), the effective speed ratio increases and the windmill root persists.
- At azimuths where
$v_{t,\text{extra}}$ opposes$\Omega r$ (retreating side, reverse flow), the bracket may close -- the element naturally reverts to the helicopter solution.
This extension is unique to dynbem -- OpenFAST's AeroDyn BEMT uses the windmill solver only in the axial flow path and does not run it inside an azimuth-resolved psi-loop with in-plane wind. The correction is necessary for any rotor that windmills in oblique descent (autorotating kites, autogyros in forward flight, descent with crosswind).
The standard wind-turbine BEM iterates on the induction factor
At hover (
so a single code path covers hover, climb, descent, and the windmill/turbine regime without switching variables.
The momentum quadratic
has two roots:
The physical root is selected by the sign of
-
Helicopter / hover (
$\lambda_\text{climb} \ge 0$ ): want$\lambda_r > 0$ . Prefer the root that is already positive; fall back to the other if neither is. -
Turbine / autorotation (
$\lambda_\text{climb} < 0$ ): want$\lambda_r < 0$ . Prefer the root that is already negative; fall back to the other if neither is.
The fall-back is not just defensive — it is load-bearing near the autorotation crossing. At the instant the operating point crosses through hover, both roots are near zero and neither satisfies the sign predicate cleanly; the fall-back picks the one that is geometrically correct for the current side. Without it the solver locks onto the wrong branch and produces a thrust/torque discontinuity as the rotor passes through hover.
Degenerate cases handled separately in the code:
-
$k \approx 1$ (denominator$2(k-1) \to 0$ ): the quadratic becomes linear; the root is$\lambda_r = -kx^2/\lambda_\text{climb}$ (valid when$|\lambda_\text{climb}| > 0$ ). - Hover,
$\lambda_\text{climb} \approx 0$ and$k \approx 1$ : the linear formula also degenerates; the code falls back to$\lambda_r = x\sqrt{k}$ , which is the hover actuator-disk result at the linear-momentum limit.
See PITT_PETERS_DESIGN.md for state equations, L-matrix, ODE, and steady-state targets, plus implementation notes (sign translation, mass-flow parameter, wind-axis rotation, VRS notes).
See OYE_DESIGN.md for per-annulus filter states, W_qs momentum target, two-stage ODE, and what Oye cannot model.
makes Pitt-Peters stiff at high advance ratios and in descent + edgewise wind. For implementation notes (W sign convention, why the axial-momentum form was rejected, what Oye cannot model) see OYE_DESIGN.md.
Applied when:
Leishman empirical polynomial (Castles-Gray TN-2474 fit):
In the VRS regime
This correction is part of the dynamic-inflow steady-state target, so it applies only to Pitt-Peters and Oye. The QuasiStatic BEM has no VRS model -- its momentum/windmill solve runs unmodified through the recirculating-wake regime, where its results are not trustworthy.
Source: dynbem_rs/src/servoflap.rs. Applies when
PitchActuation::ServoFlap is active. The blade feathering angle
where primes denote
Frequency ratio:
For
In servo-flap mode,
assemble_result (bem_common.rs), called by all three models:
F_world is the aerodynamic force in NED world frame; M_orbital is the
hub-frame aero moment rotated to world frame (hub rolling/pitching moments);
M_spin is the reaction torque about the hub axis; Q_spin is the scalar
shaft torque.
When blade flapping is configured (Section 14a), the phase-correct 1/rev flap solve sets the hub moments transmitted to the airframe and adds a flapping-tilt contribution to the in-plane hub force before rotation to world frame:
Otherwise
Source: FlapProperties in dynbem_rs/src/rotor_definition.rs,
apply_flap_dynamics in dynbem_rs/src/bem_common.rs.
A flexible blade (or one with a flap hinge) responds to the 1/rev cyclic
aerodynamic moment with an out-of-plane flap beta(psi). Only the
fraction set by the blade's centrifugal flap stiffness reaches the
airframe as a hub moment; the tilted disk also contributes an in-plane
hub force (the flapping-tilt "H-force"). Both are solved phase-correctly
after the psi-loop integration and before output assembly.
The blade's out-of-plane flexibility is represented as a virtual hinge with a torsional spring. The rotating flap frequency ratio:
where
Near
with aerodynamic flap damping (Lock number over 8), integrated over the radial grid to allow taper:
(mx_hub = N_b*M1s/2,
my_hub = N_b*M1c/2, and at 1/rev (
Transmitted hub moment (Johnson):
Flapping-tilt H-force (hub frame,
| Configuration | Transmitted moment | Flapback H-force | ||
|---|---|---|---|---|
| Freely hinged (teetering) | 0 | 1.0 | 0 (a=0) | present (pure 90 deg lag) |
| Typical hingeless | 1.02-1.08 | small fraction of aero moment | present | |
| Very stiff / rigid |
|
Note the flapback H-force is nonzero even for a freely hinged blade that
transmits zero hub moment: aerodynamic damping alone gives flapback case in
validation_rs/src/checks/h_force.rs.
The flap solve acts only on the output to the airframe. The Pitt-Peters
inflow ODE (Section 10) still uses the full aerodynamic moments
flap: section only the flapping-tilt term is omitted.
This is physically correct: the blade flaps to redistribute the aerodynamic reaction between blade inertia (centrifugal restoring) and hub structure, but the total aerodynamic moment on the disk -- which drives the wake -- is unchanged.
Source: dynbem_rs/src/trim.rs. Generic over any AeroModel.
The dynamic-inflow models return a state derivative
-
Numerical stability. The inflow time constants span a wide range:
$\tau_0 = 8R/(3\pi V_\text{mf})$ for the uniform state versus the much smaller$\tau_{c,s} = 16R/(45\pi V_\text{mf})$ for the harmonics, and both shrink as$V_\text{mf}$ grows. At the time steps used by the envelope sweep, explicit Euler on the fast states is unstable (the update overshoots and rings). A semi-implicit step damps each state by its own$1/(1 + \Delta t/\tau_i)$ factor, which is unconditionally stable for a first-order relaxation regardless of$\Delta t/\tau$ , while still reducing to explicit Euler for the quasi-static states ($\tau = \infty$ ). This is what lets one fixed-step driver cover hover through high-advance- ratio descent without per-regime step tuning. -
Steady-state trim. Most validation and envelope points are equilibrium conditions: the rotor is asked to hold a commanded attitude (zero net hub moment, or a prescribed
$M_x, M_y$ ). That requires finding the cyclic inputs$(\eta_\text{lon}, \eta_\text{lat})$ that null the hub moments after the inflow has settled. Because the inflow and the moments are mutually coupled (cyclic changes the moments, which through the L-matrix change the inflow, which changes the moments), the trim solver must relax the inflow to quasi-steady state at each cyclic guess before measuring the residual — hence the integrator and the trim solver live together.
Each inflow state inflow_taus. The step damps the update by the implicit factor:
Quasi-static states (
solve_trim_cyclic iterates on (tilt_lon, tilt_lat) to drive hub-frame
moments
- Relax inflow to quasi-steady state at current cyclic guess.
- Probe numerical Jacobian:
$\partial M_y / \partial \eta_\text{lon}$ and$\partial M_x / \partial \eta_\text{lat}$ via forward-difference with stepprobe_rad. - Newton update (50% under-relaxation) on each axis independently.
- Repeat until
$|M_x|, |M_y| <$ tolerance.
The axes are treated as decoupled (diagonal Jacobian) because the
dominant coupling is
All constants in dynbem_rs/src/common.rs. These are empirical, tuned to
keep the full operating envelope (hover, climb, descent, VRS, autorotation)
stable in one code path without mode-switching.
| Constant | Value | Role |
|---|---|---|
EPS_DENOM |
Generic denominator / ratio guard | |
EPS_OMEGA_R |
Not-spinning threshold | |
MIN_LOSS_FACTOR |
Prandtl tip+hub loss floor | |
MASS_FLOW_HOVER_FLOOR_FRAC |
|
|
VRS_DESCENT_THRESHOLD |
VRS detection guard against hover chattering | |
MU_T_FLOOR |
L-matrix denominator floor |
| Property | QuasiStatic BEM | Pitt-Peters | Oye |
|---|---|---|---|
| Inflow states | 0 (converged each call) | 3 global ( |
|
| Inflow dynamics | None | 3 time constants | Per-annulus |
| Cyclic inflow feedback | No | Yes (L-matrix coupling) | No |
| Wake-skew coupling | No | Yes ( |
No |
| VRS correction | No (momentum/windmill only) | Yes (Leishman) | Yes (Leishman) |
| Numerical stiffness | Low | High at high |
Low |
| Hub moment harmonics | Averaged |
Averaged + feedback to inflow | Averaged, no feedback |
| State size | 0 | 3 |
Which model to reach for in each regime. "Preferred" marks the model best suited to that regime; the others still run but with the caveat noted.
| Regime | QuasiStatic BEM | Pitt-Peters | Oye |
|---|---|---|---|
| Hover | OK | OK | OK |
| Axial climb | OK | OK | OK |
| Forward flight, low |
OK (no inflow lag) | Preferred (cyclic feedback) | OK (no cyclic feedback) |
| Forward flight, high |
OK | Stiff (small |
Preferred (stable) |
| Descent + edgewise wind | OK | Stiff / can destabilise | Preferred (stable) |
| Vortex-ring state | Not modelled (no VRS override) | Leishman empirical | Leishman empirical |
| Autorotation / windmill | Preferred (Ning/Buhl solver) | Sign-of- |
Sign-of- |
| Fast control transients | Not modelled (no inflow state) | Preferred (3-state lag) | OK (annular lag, no cyclic) |
Notes:
-
VRS: only Pitt-Peters and Oye apply the Leishman empirical
$\lambda_1$ override (Section 12). QuasiStatic has no recirculating-wake model -- its momentum/windmill solve is used unmodified, so results in the vortex-ring state are not trustworthy. - Autorotation / windmill: only QuasiStatic has the dedicated Ning 2014 / Buhl 2005 turbine solver (Section 9.1) that resolves the high-induction turbulent-wake state. Pitt-Peters and Oye reach the energy-extracting regime purely through the sign of their prescribed inflow (Section 8.2) -- correct in torque sign but without the turbulent-wake thrust correction.
- Transients: QuasiStatic carries no inflow state, so it cannot represent the lag between a control input and the wake response; use a dynamic-inflow model when the time history matters.
The models are validated on two fronts: against published experimental
rotor data (absolute accuracy and qualitative correctness), and against
independent open-source BEM / dynamic-inflow codes (implementation
cross-checks). Each dataset has a verifier script under
verification/ and a regression test under
tests/; the source-paper extractions live under Research/.
Full provenance, per-dataset bias analysis, and convention-reconciliation
notes are in EMPIRICAL_VALIDATION.md.
All four experimental datasets are hover/axial or autorotation rotors;
the Level-1 BEM here is inviscid and incompressible with a tabulated
polar, so the consistent picture is correct structure (monotonicity,
sign flips, autorotation crossing, inflow shape) with a systematic
high bias on absolute
| Dataset (paper) | Regime | Quantity | BEM error | Test |
|---|---|---|---|---|
| Castles-Gray, NACA TN-2474 | Hover (Table I, 11 pts) | +11% mean, 11% RMSE | test_castles_gray.py | |
| Castles-Gray, NACA TN-2474 | Hover (Table I, 11 pts) | -1.5% mean, 14% RMSE | test_castles_gray.py | |
| Castles-Gray, NACA TN-2474 | Vertical descent |
|
sign correct | test_castles_gray.py |
| Castles-Gray, NACA TN-2474 | Windmill-brake (Fig 12) |
|
within 20% | test_castles_gray.py |
| Caradonna-Tung, NASA TM-81232 | Hover | +30-45% | test_bem_components.py | |
| Caradonna-Tung, NASA TM-81232 | Hover |
|
within ~10% | test_bem_components.py |
| Caradonna-Tung, NASA TM-81232 | Hover (151-pt sweep) | spanwise |
median 31% (tip ~10-20%) | test_caradonna_spanwise.py |
| Harrington, NACA TN-2318 | Hover (full-scale Re) | +30-45% | test_bem_components.py | |
| Wheatley-Hood, NACA TR-515 (PCA-2) | Forward-flight autorotation ( |
|
mean |
test_wheatley_autorotation.py |
The Wheatley-Hood PCA-2 set is the only forward-flight autorotation
benchmark and the most extensive (286 validated operating points). The
persistent negative
Origin of the systematic
These cross-checks isolate the algorithm from the data: dynbem and the
reference code are given the same geometry, polar, and operating point,
so agreement to a few percent confirms the BEM implementation itself.
The reference codes run in Docker (see the *_docker/ folders under
verification/) and their outputs are committed as CSVs
so the tests stay deterministic.
| Reference code | Rotor / regime | Quantity | Agreement | Test / verifier |
|---|---|---|---|---|
| CCBlade (NREL/WISDEM) | NREL Phase VI HAWT, 21 pts, |
|
CT max 2.3%, CQ max 2.9% | test_dynbem_vs_ccblade.py |
| CCBlade (NREL/WISDEM) | Beaupoil RAWES rotor, 25 pts |
|
CT max 1.8%, CQ max 11% (at autorotation crossing) | test_dynbem_vs_ccblade.py |
| XROTOR | Caradonna-Tung, pure hover (5/8/12 deg) | thrust | three-way vs paper + XROTOR | dynbem_qsbem_vs_xrotor_caradonna_tung.py |
| OpenFAST AeroDyn (DBEMT_Mod=1) | NREL Phase VI, Oye dynamic inflow |
|
same-algorithm cross-check | dynbem_oye_vs_openfast_nrel_phase_vi.py |
| AeroDyn (polar lookup) | NREL Phase VI, per-element |
|
to a few ULPs | dynbem_polar_vs_aerodyn_nrel_phase_vi.py |
Notes:
- CCBlade / NREL Phase VI is the cleanest cross-check — that rotor is exactly the envelope CCBlade was built for. Both BEMs agree within ~3% at every operating point. dynbem reaches the windmill regime through its own Ning-2014 Brent's-method solver with Buhl turbulent-wake correction (Section 9.1), not a fixture flag.
-
CCBlade / Beaupoil stresses a cambered-airfoil helicopter-style
rotor at zero pitch; the only large relative error (11%
$C_Q$ ) is at the autorotation crossing where the absolute torque is ~$2\times10^{-5}$ and relative error inflates near zero. -
XROTOR provides the hover peer check that AeroDyn cannot (its
wind-turbine BEM is singular at
$V_\infty = 0$ ); XROTOR's free-tip potential formulation handles static thrust natively. -
OpenFAST AeroDyn DBEMT and dynbem's Oye model implement the same
2-stage annular dynamic-inflow algorithm, so residual differences
isolate implementation choices (mass-flow term,
$W_{qs}$ linearisation, hover floor, tip-loss form) rather than physics. - Convention reconciliation (pitch-to-stall vs pitch-to-feather sign, NED wind direction, torque-sign in energy-extraction mode) is handled inside each verifier and documented in its header.
- Peters, D.A. (2009). How Dynamic Inflow Survives in the Competitive
World of Rotorcraft Aerodynamics: The Alexander Nikolsky Honorary
Lecture. JAHS 54(1):011001. (
Research/Peters_Nikolsky_2008/) - Oye, S. (1990). A simple vortex model. IEA Symposium on the Aerodynamics of Wind Turbines.
- Snel, H. & Schepers, J.G. (1995). Joint investigation of dynamic inflow effects and implementation of an engineering method. ECN-C--94-107.
- OpenFAST AeroDyn Theory v3.5, Section 6.3.4 (DBEMT).
- Leishman, J.G. (2000). Principles of Helicopter Aerodynamics. Cambridge University Press.
- Castles, W. & Gray, R.B. (1951). Empirical relation between induced velocity, thrust, and rate of descent of a helicopter rotor. NACA TN-2474.
- Ning, A. (2014). A simple solution method for the blade element momentum equations with guaranteed convergence. Wind Energy 17(9):1327-1345.
- Buhl, M.L. (2005). A new empirical relationship between thrust coefficient and induction factor for the turbulent windmill state. NREL/TP-500-36834.
- Bramwell, A.R.S. (1976). Helicopter Dynamics. Arnold.