4.2.2. Module Variables (ModVar)
The ModVar module (modules/nwtc-library/src/ModVar.f90) provides the data
structures and subroutines that allow each physics module to declare its
continuous states, inputs, and outputs in a way that the glue code can
manipulate generically—without knowing the internals of any particular module.
The complete type hierarchy—from DatLoc and ModVarType through
ModVarsType and ModDataType up to the top-level ModGlueType—is
illustrated in Fig. 4.1.
4.2.2.1. Data structures
4.2.2.1.1. DatLoc
A DatLoc (data location) is a small structure used to uniquely identify
where a particular variable lives inside a module’s derived-type hierarchy:
TYPE :: DatLoc
INTEGER :: Num ! Data identification number (from _Types.f90 file)
INTEGER :: i1 ! First index
INTEGER :: i2 ! Second index
INTEGER :: i3 ! Third index
INTEGER :: i4
INTEGER :: i5
END TYPE DatLoc
A DatLoc value is created once per variable by the registry-generated
DatLoc() constructor calls inside each module’s InitVars subroutine and
passed to MV_AddVar / MV_AddMeshVar. The glue code stores this value
inside each ModVarType and uses MV_EqualDL to match variables across
source and destination modules when setting up mesh mappings.
4.2.2.1.2. ModVarType
Describes a single variable (or group of variables for a mesh field):
TYPE :: ModVarType
INTEGER :: Field ! Field type (FieldForce, FieldTransDisp, ...)
INTEGER :: Nodes ! Number of nodes (mesh variables only)
INTEGER :: Num ! Total number of scalar values
INTEGER :: Flags ! Bit-mask of VF_* flags
INTEGER :: DerivOrder ! 0=disp/orientation, 1=velocity, 2=acceleration
INTEGER :: iLoc(2) ! [start, end] in module-local array
INTEGER :: iGlu(2) ! [start, end] in glue-level array
INTEGER :: iq(2) ! [start, end] in solver state (q) array
INTEGER :: iLB, iUB ! User array bounds (for array-valued scalars)
INTEGER :: j, k, m, n ! Additional user-defined indices
REAL(R8Ki) :: Perturb ! Perturbation size for Jacobian finite differences
TYPE(DatLoc) :: DL ! Data location
CHARACTER :: Name ! Human-readable variable name
CHARACTER(:), ALLOCATABLE :: LinNames(:) ! Per-value linearization labels
END TYPE ModVarType
4.2.2.1.3. ModVarsType
Holds all variables for one module, partitioned into three groups:
TYPE :: ModVarsType
INTEGER :: Nx, Nu, Ny
TYPE(ModVarType), ALLOCATABLE :: x(:) ! Continuous states
TYPE(ModVarType), ALLOCATABLE :: u(:) ! Inputs
TYPE(ModVarType), ALLOCATABLE :: y(:) ! Outputs
END TYPE ModVarsType
4.2.2.1.4. ModDataType
Top-level container that the glue code holds for each module instance:
TYPE :: ModDataType
CHARACTER :: Abbr ! Module abbreviation ("ED", "BD", ...)
INTEGER :: iMod ! Index in glue module array
INTEGER :: ID ! Module_ED, Module_BD, ...
INTEGER :: Ins ! Instance number
INTEGER :: iRotor ! Rotor index (0 = all rotors)
INTEGER :: SubSteps ! Module sub-steps per solver step
INTEGER :: Category ! Bit-mask of MC_* coupling flags
REAL(R8Ki) :: DT ! Module time step
TYPE(ModVarsType) :: Vars
TYPE(ModLinType) :: Lin
END TYPE ModDataType
4.2.2.2. Variable flags (VF_*)
Flags are combined via IOR and tested with MV_HasFlagsAll /
MV_HasFlagsAny.
Flag |
Value |
Meaning |
|---|---|---|
|
0 |
No flags set; used as a wildcard that matches any variable. |
|
1 |
Variable is a mesh field (set automatically by |
|
2 |
Mesh is a line mesh (loads per unit length); linearization labels get
|
|
4 |
Variable lives in the rotating reference frame. |
|
8 |
Variable is included in the full-system linearization. |
|
16 |
Variable is included in extended linearization output. |
|
32 |
Scalar angle with range [0, 2π] (e.g. generator azimuth). |
|
64 |
Output variable associated with a |
|
256 |
Variable participates in the tight-coupling Jacobian or input-output
convergence solve. Set automatically by |
|
512 |
Variable used in aeromap computation. |
|
1024 |
Variable participates in a module-to-module transfer mapping. |
|
8192 |
Explicitly excludes a variable from both linearization and the solver
(overrides |
4.2.2.3. Field types (Field*)
Used in the Field member of ModVarType and in MV_AddMeshVar’s
Fields argument:
Constant |
Meaning |
|---|---|
|
Nodal force (3 components per node, N) |
|
Nodal moment (3 components per node, N·m) |
|
Translational displacement (m) |
|
Orientation, stored internally as unit-quaternion parameters (rad) |
|
Translational velocity (m/s) |
|
Angular velocity (rad/s) |
|
Translational acceleration (m/s²) |
|
Angular acceleration (rad/s²) |
|
Angular displacement (rad) |
|
Generic scalar values |
Convenience arrays defined in ModVar.f90:
LoadFields=[FieldForce, FieldMoment]TransFields=[FieldTransDisp, FieldTransVel, FieldTransAcc]AngularFields``= ``[FieldOrientation, FieldAngularVel, FieldAngularAcc, FieldAngularDisp]MotionFields= all translational and angular motion fields
4.2.2.4. Adding module variables
Each module that participates in the glue code must implement an
InitVars (or equivalent) subroutine that populates a ModVarsType
structure by calling the MV_Add* subroutines documented below. This
subroutine is called during the module’s _Init routine and the resulting
Vars is passed immediately to MV_AddModule.
4.2.2.4.1. MV_AddVar
Adds a single (possibly multi-element) scalar variable to a variable array.
subroutine MV_AddVar(VarAry, Name, Field, DL, &
Num, iAry, jAry, kAry, &
Flags, DerivOrder, Perturb, LinNames, Active)
Argument |
Intent |
Description |
|---|---|---|
|
|
Allocatable array of |
|
|
Human-readable name used in debug output and linearization labels. |
|
|
Field type constant ( |
|
|
|
|
|
Number of scalar values. Defaults to 1. If 0, the call is a no-op. |
|
|
Starting lower-bound index if the data are stored in an array. |
|
|
Second and third array indices (for 2-D or 3-D arrays). |
|
|
Initial |
|
|
Override the automatically-inferred derivative order (0, 1, or 2). |
|
|
Finite-difference perturbation magnitude. A good default is roughly
1 % of the expected variable magnitude. For mesh fields the default
computed inside |
|
|
Array of per-value linearization channel labels (length = |
|
|
Set to |
Example – registering the generator torque input of ServoDyn:
call MV_AddVar(Vars%u, 'Generator torque command', FieldScalar, &
DatLoc(SrvD_u_GenTrq), &
Flags=VF_Linearize, &
Perturb=1.0e3_R8Ki, & ! N*m
LinNames=['SrvD GenTrq, N*m'])
4.2.2.4.2. MV_AddMeshVar
Adds all requested mesh fields for a single MeshType to a variable array.
It is a convenience wrapper around MV_AddVar that iterates over the
Fields argument and skips fields that are absent from the committed mesh.
subroutine MV_AddMeshVar(VarAry, Name, Fields, DL, Mesh, &
Flags, Perturbs, Active, iVar)
Argument |
Intent |
Description |
|---|---|---|
|
|
Allocatable array to append the new variable(s) to. |
|
|
Base name for all fields on this mesh. |
|
|
Integer array of field-type constants; use the |
|
|
|
|
|
The committed |
|
|
Extra |
|
|
Array of perturbation values, one per entry in |
|
|
Conditionally disable the entire mesh variable registration. |
|
|
Returns the assigned mesh |
Example – registering ElastoDyn’s blade-root output motion mesh:
call MV_AddMeshVar(Vars%y, 'BladeRootMotion', MotionFields, &
DatLoc(ED_y_BladeRootMotion, i), & ! blade i
p%BladeRootMotion(i), &
Flags=VF_Linearize + VF_RotFrame)
4.2.2.4.3. MV_AddModule
After a module’s InitVars subroutine is complete, the caller registers the
module with the glue code using MV_AddModule.
subroutine MV_AddModule(ModDataAry, ModID, ModAbbr, Instance, &
ModDT, SolverDT, Vars, Linearize, &
ErrStat, ErrMsg, iRotor)
Argument |
Intent |
Description |
|---|---|---|
|
|
Allocatable array of |
|
|
Module identifier constant ( |
|
|
Short abbreviation string used in output labels ( |
|
|
Instance number (1-based). Most modules have a single instance. |
|
|
Module time step (seconds). Must be an exact integer divisor of
|
|
|
Solver (global) time step. |
|
|
Populated |
|
|
Whether linearization is enabled. When |
|
|
Error status and message. |
|
|
Rotor number for multi-rotor turbines (0 = all rotors). |
Sub-stepping logic: if ModDT < SolverDT, MV_AddModule calculates
ModData%SubSteps = NINT(SolverDT/ModDT) and validates that the module DT
divides the solver DT exactly. An error is returned if ModDT > SolverDT.
Typical call sequence inside FAST_Subs.f90:
! Module computes its own Vars in Init
call ED_Init(InitInp, u, p, ..., InitOut, ErrStat, ErrMsg)
! Register with glue code
call MV_AddModule(m%ModData, Module_ED, 'ED', 1, p%DT, p_FAST%DT, &
InitOut%Vars, p_FAST%Linearize, ErrStat, ErrMsg, iRotor=1)
4.2.2.4.4. MV_InitVarsJac
Called inside each module’s InitVars after all MV_AddVar /
MV_AddMeshVar calls are complete. It assigns the module-local iLoc
index ranges to each variable and allocates the ModJacType working arrays
used during Jacobian calculations.
subroutine MV_InitVarsJac(Vars, Jac, Linearize, ErrStat, ErrMsg)
4.2.2.5. Perturbation values
Every variable carries a Perturb value used for central-difference
finite-differencing when building module-level Jacobians. The Perturb
argument to MV_AddVar / MV_AddMeshVar should be chosen so that the
resulting output change is large enough to distinguish from numerical noise
but small enough to stay in the linear regime. Typical values:
Translational displacement:
1.0e-4mRotational (orientation):
2.0e-5radTranslational velocity:
1.0e-3m/sAngular velocity:
2.0e-4rad/sTranslational acceleration:
1.0e-2m/s²Force:
1.0e1NMoment:
1.0e1N·mGeneric scalar: context-dependent
The UJacSclFact input parameter (see User input parameters) is a
global conditioning factor that the solver applies to load variables in the
Jacobian to improve matrix conditioning when force/moment magnitudes are very
different from state magnitudes.
4.2.2.6. Orientation representation
Orientations are not stored or manipulated as direction cosine matrices (DCMs) inside the glue-variable arrays. Instead, a compact three-component unit-quaternion parameterization is used:
This parameterization avoids the redundancy in a full DCM and enables
straightforward finite-differencing via quaternion composition
(quat_compose). Conversion utilities exported from ModVar include
dcm_to_quat, quat_to_dcm, quat_compose, quat_inv,
quat_to_rvec, rvec_to_quat, wm_to_quat, and quat_to_wm.
When computing orientation differences for Jacobian rows, MV_ComputeDiff
computes the relative rotation between the negative and positive-perturbation
quaternions and converts it to a rotation vector.