Dynamic Inflow Finite State — Robotics/Rotorcraft
Robotics/Rotorcraft/Dynamic_Inflow_Finite_State · 4 input / 1 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text
Description#
The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.
Dynamic Inflow Finite State
Robotics / Rotorcraft
The Peters–He finite-state dynamic inflow model: the induced velocity normal to a rotor disc as a set of ordinary differential equations, one per inflow state, where each state is a harmonic m around the azimuth and a radial shape n along the blade. The inflow seen by blade b at radial element r is λi = Σ anm·φnm(r)·{cos|sin}(mψb), with ψb = ψ + 2πb/Nb and ψ' = Ω, and the states follow K·a' = |Ω|·(F − V·L(X)−1·a): F is the blade load distribution projected onto each shape and harmonic, L the wake-skew matrix of the skew parameter X and V the mass-flow parameters.
- X = μ / (VT + |λt|), signed, with λt = μz + (8/(3√3))·a10.
- VT = √(μ² + λe²) on the first state and V = (μ² + λe(λe + ν))/VT on the rest, with λe = μz + ν and ν = (2/√3)·[L−1a]10.
Ports
- fz – the elemental force distribution, an [Nb,Nr] array of force per unit blade length (N/m, or lbf/ft in English units): one row per blade, one column per radial element. Its size sets the number of blades and elements.
- mu – the rotor advance ratio μ, a scalar [1,1], nondimensional.
- muz – the normal velocity ratio μz, a scalar [1,1], nondimensional.
- rho – the air density ρ > 0, a scalar [1,1] (kg/m³, or slug/ft³).
- lambda_i – the normal induced velocity ratio, the same [Nb,Nr] size as fz: the inflow at each blade's azimuth and each element's mid-radius, nondimensional.
Parameters
- Units – Metric (MKS) (the default) or English. The two are the same arithmetic; the choice only says which units the numbers are in.
- Rotor Rotational Speed – Ω in rad/s, a scalar. Defaults to 41.2596. A negative value turns the azimuth the other way; zero freezes the block.
- Rotor Radius – R, a scalar > 0 (m, or ft). Defaults to 5.08.
- Radial Locations Source – where the radial element edges come from:
- Uniform distribution based on force input (the default) – Nr equal elements spanning the whole blade, edges 0, 1/Nr, …, 1.
- Custom distribution – the edges come from the parameter below.
- Non-dimensional Radial Element Edges – the element edges under Custom
distribution: Nr+1 strictly increasing values in [0,1]. Defaults to
-1, which the uniform setting ignores. - Highest Harmonic Index – M, a whole number from 0 to 6. Defaults to 2.
- Compute Spatial Modes – how many radial shapes each harmonic carries:
- Consistently upgraded (the default) – harmonic m carries n = m+1, m+3, … up to M+1: (M+1)(M+2)/2 inflow states in all.
- Constant – every harmonic carries N shapes, n = m+1, m+3, …, m+2N−1: (2M+1)·N inflow states.
- Number Of Spatial Modes – N, a whole number from 1 to 6, used under Constant. Defaults to 2.
- Integration Substeps – how many fourth-order Runge-Kutta steps a sample is integrated with on the discrete solver and in exported code, a whole number of 1 or more. Defaults to 100. No Simulink counterpart.
- Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.
Code export
All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. A core holds the states, publishes the inflow from them, and integrates one sample with the inputs held and the substeps above. Everything the configuration fixes – the radial shapes, the Peters–He coefficients, the element weights – is folded to constants, and the wake-skew system is solved by straight-line elimination, so the emitted body grows with the number of states: exports are offered up to about 3000 elimination operations per derivative (every Consistently upgraded setting qualifies; the largest Constant ones do not, and asking for one logs why).
The three HDL targets are simulation-only real arithmetic,
quantized at the port: square roots, divisions by signals and sines of a state have no Q16.16
form. The cores simulate correctly and are not offered as synthesizable.
Simulink bridge
Import and export, mapped to Aerospace Blockset's
aerolibinflowmodels/Dynamic Inflow (Finite-State): Units →
units, Rotor Rotational Speed → omega, Rotor Radius
→ radius, Radial Locations Source → radDistrSrc,
Non-dimensional Radial Element Edges → radDistr, Highest Harmonic
Index → indexHarmonics, Compute Spatial Modes →
methodSpatialModes and Number Of Spatial Modes →
numSpatialModes, the enumerations 1:1 and therefore lossless. Always emitted with
omegaSrc = Dialog: taking the rotational speed from a port moves that
block's port list, and this block has one. "Sampling Time (s)" does not cross: the
Simulink block is continuous and defines no SampleTime parameter.
Notes
- Stateful, continuous and nonlinear: one continuous state per inflow state plus the azimuth, all starting at zero. The output reads the state alone, so the block has no direct feedthrough.
- The sign of μ matters: X is signed, unlike the 3-State block's. Measured against the Simulink block.
- The Simulink block limits μ and μz to [−1,4], refuses ρ ≤ 0 and a harmonic index or mode count above 6. This block refuses the last two at configuration and does not stop a run for the first three; it guards the divisions instead, so ρ ≤ 0 gives no forcing and a total flow of zero gives no damping.
- V is negative wherever μ² + λe(λe + ν) is, which is the model's own behaviour and makes the higher states unstable. Keep μz + ν clear of zero, as the Simulink block's own warning asks.
Code facts#
| Fact | Value |
|---|---|
| registered type | Robotics/Rotorcraft/Dynamic_Inflow_Finite_State |
| family | Robotics/Rotorcraft |
| solver environment class | ICoreBlock_0_Robotics_1_Rotorcraft_2_Dynamic_Inflow_Finite_State |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Rotorcraft/Dynamic_Inflow_Finite_State/ICoreBlock_0_Robotics_1_Rotorcraft_2_Dynamic_Inflow_Finite_State.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Robotics/Rotorcraft/Dynamic_Inflow_Finite_State/ICoreBlock_0_Robotics_1_Rotorcraft_2_Dynamic_Inflow_Finite_State.h |
| default size on canvas | 150 × 110 px |
| ports at insert | 4 in, 1 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreDouble | fz |
| 2 | in | ICoreDouble | mu |
| 3 | in | ICoreDouble | muz |
| 4 | in | ICoreDouble | rho |
| 5 | out | ICoreDouble | lambda_i |
Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.
Configuration variables#
| Config variable | Default | Simulink parameter |
|---|---|---|
CONFIG_UNITS (unresolved) | Metric (MKS)%~%English~~Metric (MKS) | — |
CONFIG_OMEGA (unresolved) | 41.2596 | — |
CONFIG_RADIUS (unresolved) | 5.08 | — |
CONFIG_RADSRC (unresolved) | std::string(RADSRC_UNIFORM)%~%RADSRC_CUSTOM~~RADSRC_UNIFORM | — |
CONFIG_EDGES (unresolved) | -1 | — |
CONFIG_HARM (unresolved) | 2 | — |
CONFIG_METHOD (unresolved) | std::string(METHOD_CONS)%~%METHOD_CONST~~METHOD_CONS | — |
CONFIG_MODES (unresolved) | 2 | — |
CONFIG_SUB (unresolved) | 100 | — |
Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.
Simulink bridge#
| support | Support::Both |
| Simulink path | aerolibinflowmodels/Dynamic Inflow (Finite-State) |
| port-count rule | PortsParam::None |
SampleTime parameter | no — the counterpart defines none; the rate stays on the ICore side |
| deliberately not crossed | Integration Substeps |
| always set | omegaSrc = Dialog |
| ICore config | Simulink parameter | Value translation |
|---|---|---|
Units | units | Metric (MKS) → Metric (MKS), English → English |
Rotor Rotational Speed | omega | passes through |
Rotor Radius | radius | passes through |
Radial Locations Source | radDistrSrc | Uniform distribution based on force input → Uniform distribution based on force input, Custom distribution → Custom distribution |
Non-dimensional Radial Element Edges | radDistr | passes through |
Highest Harmonic Index | indexHarmonics | passes through |
Compute Spatial Modes | methodSpatialModes | Consistently upgraded → Consistently upgraded, Constant → Constant |
Number Of Spatial Modes | numSpatialModes | passes through |
Caveat (shown to the user): aerolibinflowmodels/Dynamic Inflow (Finite-State) is continuous and has NO SampleTime parameter (verified against the R2026a block dialog). omegaSrc is pinned to Dialog because taking the rotor speed from a port moves that block's port list, and this block has one; "Integration Substeps" is how this block integrates a sample and has no counterpart
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.
The verdict above is
tools/docs/check_block_descriptions.py(P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.
File banner (developer view)#
The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).
Dynamic Inflow (Finite-State) -- the Peters-He finite-state inflow model, as ODEs Inflow states a_i, one per (harmonic m, spatial mode n): ALL cosine states (m ascending, n ascending), then all sine states (m >= 1), then the azimuth psi. "Consistently upgraded" gives harmonic m the modes n = m+1, m+3, ..., <= M+1; "Constant" gives n = m+1, m+3, ..., m+2N-1. The inflow at blade b, element r (mid-radius rm) is
lambda(b, r) = sum_i a_i phi_n^m(rm) {cos | sin}(m psi_b), psi_b = psi + 2 pi b / Nb
with the Peters-He radial functions phi_n^m, and each state follows
a_i' = |Omega| / K_i * ( F_i - Vd_i * [L(X)^-1 a]i ), psi' = Omega, K_i = (2/pi) H_n^m F_i = c_m * sum_b sum_r fz * dr * phi_n^m(rm) {cos | sin}(m psi_b) / (rho pi R^3 Omega^2) (c_0 = 1/4, c_m = 1/2) lt = muz + (8 / (3 sqrt 3)) a(0,1), X = mu / (sqrt(mu^2 + lt^2) + |lt|) (SIGNED) nu = (2 / sqrt 3) [L(X)^-1 a]_(0,1), le = muz + nu Vd = diag(VT, V, V, ...), VT = sqrt(mu^2 + le^2), V = (mu^2 + le (le + nu)) / VT L = the Peters-He wake-skew matrix, (X^|m-r| + s X^|m+r|) Gamma(r,j,m,n), halved on the r = 0 rows, s = (-1)^min(r,m) on the cosine block and its negative on the sine block
⚠ EVERY LINE ABOVE IS MEASURED: aerolibinflowmodels/Dynamic Inflow (Finite-State) is a compiled FiniteStateDI (no mask, no .m, no source), identified through Simulink's model API -- the derivative and the output at chosen states and inputs. The state layout, phi, K, the forcing and L were measured on 2026-09-11, leaving one function unnamed; it was named on 2026-09-21: the mass-flow parameters and the wake skew use DIFFERENT mean inflows -- the skew 8/(3 sqrt 3) a_(0,1), the mass flow (2/sqrt 3) [L^-1 a]_(0,1), the loading state. With that, the whole model matches the block to 1e-16 relative, derivative and output, over random states, loads, mu of both signs, two harmonic/mode configurations, custom element edges, a negative Omega and English units.
Two things it settled that the 3-State block does NOT share:
- THE SIGN OF mu MATTERS here: X is signed, so the odd powers of X in L flip with it. (The
3-State block's skew is |mu| / (VT + |lt|).)
- The mean inflow in V is not the one in X; in hover the two coincide only because
(2/sqrt 3) L(0)^-1_00 happens to equal 0.96 sqrt 3 at M = 2.
L(X) z = a is solved in each cosine/sine block by Gaussian elimination WITHOUT pivoting -- measured safe over every configuration (M, N <= 6, X in [-1, 1]): no pivot is below half its column's largest entry, and no block's condition number above 40.
Continuous, a pure output, no direct feedthrough. The discrete solver path and every exported
Sample results#
Plotted: impulse — Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)
Category dynamic · sample time 0.1 · 60 steps · commit 856865ebf9 · produced by docsSample --out <folder> --blocks Dynamic_Inflow_Finite_State --steps 60 · data docs/generated/samples/Robotics__Rotorcraft__Dynamic_Inflow_Finite_State.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).