|
| 1 | +.. _glue-code-linearization: |
| 2 | + |
| 3 | +Linearization |
| 4 | +============= |
| 5 | + |
| 6 | +OpenFAST can linearise the full multi-physics system about a periodic (or |
| 7 | +static) operating point to produce continuous-time, first-order state-space |
| 8 | +matrices of the form |
| 9 | + |
| 10 | +.. math:: |
| 11 | +
|
| 12 | + \dot{\mathbf{x}} &= A\,\mathbf{x} + B\,\mathbf{u} \\ |
| 13 | + \mathbf{y} &= C\,\mathbf{x} + D\,\mathbf{u} |
| 14 | +
|
| 15 | +together with the coupling matrices *dUdu* (input-to-input feed-through) and |
| 16 | +*dUdy* (output-to-input coupling). The linearization engine lives in |
| 17 | +``modules/openfast-library/src/FAST_ModGlue.f90``. |
| 18 | + |
| 19 | +.. contents:: |
| 20 | + :local: |
| 21 | + :depth: 2 |
| 22 | + |
| 23 | +User inputs for linearization |
| 24 | +------------------------------ |
| 25 | + |
| 26 | +The following parameters appear in the main OpenFAST input file (``*.fst``) |
| 27 | +under the **Linearization** section. |
| 28 | + |
| 29 | +.. list-table:: |
| 30 | + :header-rows: 1 |
| 31 | + :widths: 25 12 63 |
| 32 | + |
| 33 | + * - Parameter |
| 34 | + - Type |
| 35 | + - Description |
| 36 | + * - ``Linearize`` |
| 37 | + - logical |
| 38 | + - Master switch. Set to ``True`` to enable all linearization |
| 39 | + functionality. When ``False`` all other linearization parameters are |
| 40 | + ignored. |
| 41 | + * - ``CalcSteady`` |
| 42 | + - logical |
| 43 | + - When ``True``, OpenFAST first runs the simulation forward until the |
| 44 | + outputs at each target azimuth converge from one rotor revolution to the |
| 45 | + next (steady-state trimming), then performs linearization at each |
| 46 | + azimuth. When ``False``, linearization is performed at user-specified |
| 47 | + absolute simulation times (``LinTimes``). |
| 48 | + * - ``TrimCase`` |
| 49 | + - integer |
| 50 | + - Controller degree of freedom trimmed during ``CalcSteady`` to achieve |
| 51 | + periodic steady state. |
| 52 | + |
| 53 | + * ``1`` – yaw |
| 54 | + * ``2`` – generator torque |
| 55 | + * ``3`` – collective blade pitch |
| 56 | + * - ``TrimTol`` |
| 57 | + - real |
| 58 | + - RMS convergence tolerance on normalised output error across one |
| 59 | + rotor revolution. Trimming stops when the error falls below this |
| 60 | + value. Typical value: ``1.0e-5``. |
| 61 | + * - ``TrimGain`` |
| 62 | + - real |
| 63 | + - Proportional gain used by the built-in trim controller. |
| 64 | + Units are rad/(rad/s) for yaw/pitch cases and N·m/(rad/s) for |
| 65 | + the torque case. |
| 66 | + * - ``Twr_Kdmp`` |
| 67 | + - real |
| 68 | + - Artificial tower damping coefficient (N/(m/s)) added during the |
| 69 | + ``CalcSteady`` run to help damp transients and reach steady state |
| 70 | + faster. Set to 0 to disable. |
| 71 | + * - ``Bld_Kdmp`` |
| 72 | + - real |
| 73 | + - Artificial blade damping coefficient (N/(m/s)) during ``CalcSteady``. |
| 74 | + * - ``NLinTimes`` |
| 75 | + - integer |
| 76 | + - Number of linearization time points per rotor revolution (or number of |
| 77 | + equally spaced absolute time instants when ``CalcSteady=False``). |
| 78 | + Must be ≥ 1. For a periodic model at least 12 azimuths are typically |
| 79 | + needed to resolve the per-revolution variation. |
| 80 | + * - ``LinTimes`` |
| 81 | + - real array |
| 82 | + - Absolute simulation times (seconds) at which to linearise when |
| 83 | + ``CalcSteady=False``. Length must equal ``NLinTimes``. Ignored when |
| 84 | + ``CalcSteady=True``. |
| 85 | + * - ``LinInputs`` |
| 86 | + - integer |
| 87 | + - Controls which input variables appear in the **B** and **D** matrices. |
| 88 | + |
| 89 | + * ``0`` (``LIN_NONE``) – no inputs; produces state matrix only. |
| 90 | + * ``1`` (``LIN_STANDARD``) – inputs flagged ``VF_Linearize`` by the |
| 91 | + module (default set by each module's ``InitVars``). |
| 92 | + * ``2`` (``LIN_ALL``) – all module inputs including debug ones. |
| 93 | + * - ``LinOutputs`` |
| 94 | + - integer |
| 95 | + - Controls which output variables appear in the **C** and **D** matrices. |
| 96 | + |
| 97 | + * ``0`` (``LIN_NONE``) – no outputs. |
| 98 | + * ``1`` (``LIN_STANDARD``) – ``WriteOutput`` channels only |
| 99 | + (``VF_WriteOut`` flag). |
| 100 | + * ``2`` (``LIN_ALL``) – all module outputs. |
| 101 | + * - ``LinOutJac`` |
| 102 | + - logical |
| 103 | + - When ``True`` (requires ``LinInputs=LinOutputs=2``), the full module |
| 104 | + Jacobian matrices are written to the linearization output file for |
| 105 | + debugging. |
| 106 | + * - ``LinOutMod`` |
| 107 | + - logical |
| 108 | + - When ``True``, per-module ``.lin`` files are written in addition to the |
| 109 | + full-system file. |
| 110 | + |
| 111 | +Module support for linearization |
| 112 | +---------------------------------- |
| 113 | + |
| 114 | +Modules that appear in the linearization variable ordering (set in |
| 115 | +``ModGlue_Init``) are: |
| 116 | + |
| 117 | +InflowWind → SeaState → ServoDyn → ElastoDyn → BeamDyn → AeroDyn → |
| 118 | +HydroDyn → SubDyn → MAP++ → MoorDyn |
| 119 | + |
| 120 | +A module that is not in this ordered list causes a fatal error if |
| 121 | +``Linearize=True``. |
| 122 | + |
| 123 | +Variable selection |
| 124 | +------------------ |
| 125 | + |
| 126 | +During ``ModGlue_Init``, the ``VF_Linearize`` flag is applied to variables |
| 127 | +according to the ``LinInputs`` and ``LinOutputs`` settings: |
| 128 | + |
| 129 | +* **States (x)**: the ``VF_Linearize`` flag is always set on all continuous |
| 130 | + state variables of every participating module. |
| 131 | +* **Inputs (u)**: |
| 132 | + |
| 133 | + * ``LIN_NONE`` → flag cleared on all input variables. |
| 134 | + * ``LIN_STANDARD`` → keeps whatever ``VF_Linearize`` flag was set in the |
| 135 | + module's ``InitVars``; module developers choose the *standard* input set. |
| 136 | + * ``LIN_ALL`` → flag set on all input variables. |
| 137 | + * Variables with ``VF_NoLin`` always have ``VF_Linearize`` cleared, |
| 138 | + regardless of the above setting. |
| 139 | + |
| 140 | +* **Outputs (y)**: |
| 141 | + |
| 142 | + * ``LIN_NONE`` → flag cleared on all output variables. |
| 143 | + * ``LIN_STANDARD`` → flag set only on outputs that also carry ``VF_WriteOut``. |
| 144 | + * ``LIN_ALL`` → flag set on all output variables. |
| 145 | + * Variables with ``VF_NoLin`` are always excluded. |
| 146 | + |
| 147 | +The combined variable set is assembled into a ``ModGlueType`` named ``Lin`` |
| 148 | +via ``ModGlue_CombineModules``. |
| 149 | + |
| 150 | +Steady-state trimming (``CalcSteady``) |
| 151 | +--------------------------------------- |
| 152 | + |
| 153 | +When ``CalcSteady=True``, ``ModGlue_CalcSteady`` is called at each time step |
| 154 | +to detect periodicity: |
| 155 | + |
| 156 | +1. The module outputs tagged ``VF_Linearize`` (excluding ``VF_WriteOut``) are |
| 157 | + collected into a buffer indexed by azimuth angle. |
| 158 | +2. After each complete revolution the outputs at each of the ``NLinTimes`` |
| 159 | + azimuth targets are compared against the previous revolution via the |
| 160 | + normalised RMS error: |
| 161 | + |
| 162 | + .. math:: |
| 163 | +
|
| 164 | + \varepsilon = \sqrt{\frac{1}{N} \sum_{i=1}^{N} |
| 165 | + \left(\frac{y_i^{\rm current} - y_i^{\rm previous}}{r_i}\right)^2} |
| 166 | +
|
| 167 | + where :math:`r_i = \max(y_{i,\rm max} - y_{i,\rm min},\, 0.01)` is the |
| 168 | + output range from the current revolution (with a floor to avoid division |
| 169 | + by near-zero). |
| 170 | + |
| 171 | +3. When :math:`\varepsilon < \texttt{TrimTol}`, ``FoundSteady=True`` and |
| 172 | + linearization at all ``NLinTimes`` azimuths proceeds automatically. |
| 173 | + |
| 174 | +4. If the simulation reaches within approximately two revolutions of ``TMax`` |
| 175 | + without converging, a warning is issued and linearization is forced. |
| 176 | + |
| 177 | +The azimuth interpolation between buffer samples uses the extrapolation |
| 178 | +routines from ``MV_ExtrapInterp`` (supports constant, linear, and quadratic |
| 179 | +schemes depending on the number of available samples). |
| 180 | + |
| 181 | +linearization at an operating point |
| 182 | +------------------------------------- |
| 183 | + |
| 184 | +``ModGlue_Linearize_OP`` assembles the full-system matrices at a single |
| 185 | +operating point (time / azimuth): |
| 186 | + |
| 187 | +1. **Module Jacobians**: for each module, |
| 188 | + ``FAST_JacobianPInput`` and ``FAST_JacobianPContState`` are called to |
| 189 | + compute the per-module sub-matrices *dYdu*, *dXdu*, *dYdx*, *dXdx* by |
| 190 | + central-difference finite differentiation. The perturbation magnitudes are |
| 191 | + taken from each variable's ``Perturb`` field (see :ref:`glue-code-modvar`). |
| 192 | + |
| 193 | +2. **Operating point extraction**: ``FAST_GetOP`` packs the current states, |
| 194 | + inputs, and outputs into the linearization arrays |
| 195 | + (``ModGlue%Lin%x``, ``%u``, ``%y``). |
| 196 | + |
| 197 | +3. **Coupling matrices**: the input-output coupling matrices *dUdu* and *dUdy* |
| 198 | + are assembled from the mesh-mapping Jacobians to account for the fact that |
| 199 | + some module inputs are functions of other modules' outputs. |
| 200 | + |
| 201 | +4. **Full-system assembly**: the per-module sub-matrices are placed into the |
| 202 | + combined glue-level matrices using the ``iGlu`` index ranges stored in |
| 203 | + each ``ModVarType``. |
| 204 | + |
| 205 | +5. **Output**: ``ModGlue_CalcWriteLinearMatrices`` writes the ``.lin`` file |
| 206 | + containing: |
| 207 | + |
| 208 | + * Operating point values (**x_op**, **u_op**, **y_op**) |
| 209 | + * linearization channel names (from ``LinNames``) |
| 210 | + * Derivative order indicators (``VF_DerivOrder1``, ``VF_DerivOrder2``) |
| 211 | + * Rotating-frame flags (``VF_RotFrame``) |
| 212 | + * Full-system matrices **A**, **B**, **C**, **D**, **dUdu**, **dUdy** |
| 213 | + * Per-module matrices (if ``LinOutMod=True``) |
| 214 | + * Full Jacobians (if ``LinOutJac=True``) |
| 215 | + |
| 216 | +Output file format |
| 217 | +------------------- |
| 218 | + |
| 219 | +Each linearization call produces a file named |
| 220 | +``<RootName>.<N>.lin`` where *N* is the linearization index (1 … ``NLinTimes``). |
| 221 | +The file is a plain-text ASCII file that can be read by the |
| 222 | +`openfast_io <https://github.com/OpenFAST/openfast_io>`_ Python library or the |
| 223 | +`pyFAST <https://github.com/OpenFAST/pyFAST>`_ post-processing tools. |
| 224 | + |
| 225 | +Key fields in the file header: |
| 226 | + |
| 227 | +* ``Rotor_Speed`` – rotor speed at linearization time (RPM) |
| 228 | +* ``Azimuth`` – blade-1 azimuth at linearization time (deg) |
| 229 | + |
| 230 | +Variable naming conventions |
| 231 | +---------------------------- |
| 232 | + |
| 233 | +In linearization output files each channel label follows the pattern: |
| 234 | + |
| 235 | +``<ModAbbr> <MeshName> <Field> [, component [, node [, unit]]]`` |
| 236 | + |
| 237 | +Examples: |
| 238 | + |
| 239 | +* ``ED BlPitch1, rad`` – ElastoDyn individual blade-1 pitch state |
| 240 | +* ``AD B1N001Fx force, node 1, N`` – AeroDyn blade 1 node 1 X-force input |
| 241 | +* ``BD_1 B1TipTDxr translation displacement, node 10, m`` – BeamDyn instance 1 |
| 242 | + |
| 243 | +Module developers should ensure that the ``Name`` argument to ``MV_AddVar`` / |
| 244 | +``MV_AddMeshVar`` and the entries in ``LinNames`` follow this convention for |
| 245 | +consistency with post-processing tools. |
| 246 | + |
| 247 | +Module developer responsibilities |
| 248 | +----------------------------------- |
| 249 | + |
| 250 | +To participate in linearization a module must: |
| 251 | + |
| 252 | +1. Call ``MV_AddVar`` / ``MV_AddMeshVar`` with appropriate ``VF_Linearize`` |
| 253 | + flags and supply ``LinNames`` for all variables that may appear in the |
| 254 | + standard linearization set. |
| 255 | + |
| 256 | +2. Implement ``<Mod>_JacobianPInput`` and ``<Mod>_JacobianPContState`` |
| 257 | + subroutines (or supply analytical Jacobians through the registry). The |
| 258 | + glue code calls these via the ``FAST_JacobianPInput`` / |
| 259 | + ``FAST_JacobianPContState`` wrappers in ``FAST_Funcs.f90``. |
| 260 | + |
| 261 | +3. Implement ``<Mod>_GetOP`` (via the registry) to extract the operating-point |
| 262 | + values of states, inputs, and outputs. |
| 263 | + |
| 264 | +4. Mark variables that should **not** participate in linearization with |
| 265 | + ``VF_NoLin``. |
| 266 | + |
| 267 | +5. Mark variables in the rotating reference frame with ``VF_RotFrame`` so that |
| 268 | + multi-blade coordinate (MBC) transformations applied by post-processing |
| 269 | + tools are aware of these variables. |
0 commit comments