User-defined dynamics#
User-defined dynamics (UDD) is a powerful feature in Flow360 that allows you to implement custom control laws and coupled physics within your simulations. You define state variables that evolve according to mathematical expressions you specify, creating feedback loops where flow solution quantities influence the simulation in real-time.
Overview#
User Defined Dynamics enables you to:
Read from the flow solution: Access flow quantities such as forces, moments, pressure coefficients, or other solution variables
Compute custom quantities: Calculate derived values using state variables, constants, and input variables
Control simulation parameters: Influence boundary conditions, rotation rates, angles of attack, or other simulation inputs
This creates a feedback loop where the flow solution affects the dynamics, and the dynamics affect the flow solution, enabling simulations of complex coupled phenomena.
Web UI Implementation#
Important: Currently, User Defined Dynamics must be configured using JSON files in the Web UI. The Web UI does not yet provide a graphical interface for setting up UDD parameters. You will need to manually edit the case JSON file to add the User Defined Dynamics configuration.
Recommendation: We recommend using the Python API for implementing User Defined Dynamics, as it provides more flexibility, better debugging capabilities, and a more intuitive interface for complex dynamic scenarios.
Basic Structure#
A User Defined Dynamics system consists of the following components:
name: A descriptive identifier for the dynamics system. Results are saved in
results/udd_NAME_v2.csvwhereNAMEmatches this value.input_vars: List of flow solution variables that your dynamics system reads from the simulation. Available variables include:
Force and moment coefficients:
CL,CDForce components:
forceX,forceY,forceZMoment components:
momentX,momentY,momentZ(X/Y/Z moments with respect to the moment center defined in ReferenceGeometry)BET disk variables:
bet_NUM_torque,bet_NUM_thrust(where NUM is the index of the BET disk starting from 0)
constants: Named constant values used throughout your expressions. These can include physical constants (density, speed of sound), geometric parameters (rotation centers, axes), control parameters (proportional gain
Kp, integral gainKi, target values), structural parameters (stiffnessK, damping ratiozeta, natural frequencyomega_N, moment of inertiaI), or any other values needed for your calculations.state_vars_initial_value: Initial values for each state variable. State variables are referenced as
state[0],state[1],state[2], etc. in your expressions. Each initial value can be a numeric constant (e.g.,"0.0"), an expression using constants, or an expression referencing simulation variables (e.g.,"alphaAngle").update_law: List of C-syntax expressions that define how each state variable evolves over time. These expressions are evaluated at each pseudo-step and compute new values for the state variables based on the current state, input variables, constants, and simulation variables (
pseudoStep,physicalStep,timeStepSize,t, etc.).Important: The
update_lawis used exclusively to update state variables. It does not directly control input or output variables. State variables serve as internal memory for your dynamics system, enabling you to implement control laws, structural dynamics, accumulators, and other custom mathematical relationships.output_vars: Dictionary mapping output variable names to expressions that compute their values from the updated state variables. Output variables directly control simulation parameters. Available variables include:
alphaAngle: Angle of attack (global, no entity required)betaAngle: Sideslip angle (global, no entity required)theta: Rotation angle (in radians) for rotating volume zones (requiresoutput_target)omega: Angular velocity (in radians per second) for rotating volume zones (requiresoutput_target)omegaDot: Angular acceleration (in radians per second squared) for rotating volume zones (requiresoutput_target)bet_NUM_omega: Angular velocity for BET disk (NUM is the index of the BET disk starting from 0)actuatorDisk_<DISK_ENTITY_NAME>_thrustMultiplier: Thrust multiplier for actuator disk (where <DISK_ENTITY_NAME> is the name of the actuator disk entity)actuatorDisk_<DISK_ENTITY_NAME>_torqueMultiplier: Torque multiplier for actuator disk (where <DISK_ENTITY_NAME> is the name of the actuator disk entity)
Caution: Modifications to output variables directly affect the simulation.
input_boundary_patches: (Optional) List of surface boundaries where input variables should be computed. If specified, quantities like
CLorCDare computed only from these surfaces. For input variables that already specify their source in the name (likebet_NUM_torque), this parameter has no effect.output_target: (Optional) The target entity where output variables apply. Required for output variables that are associated with specific entities, such as
theta,omega, oromegaDotwhich control rotation of a volume zone. In these cases, specify the target entity (e.g., a rotating volume zone such as aCylinder). For global output variables likealphaAngleandbetaAngle,output_targetshould beNoneor omitted.
Note: All expressions and variables entered in User Defined Dynamics must be C-syntax compatible. For details on the syntax requirements and supported operations, see the Flow360 Python API documentation.
Creating a JSON Configuration#
The JSON structure mirrors the Python API, with the following key fields:
input_vars: Array of strings (e.g.,["momentY", "CL"])constants: Object mapping constant names to numeric valuesoutput_vars: Object mapping output variable names to expression stringsstate_vars_initial_value: Array of strings (expressions or numeric values)update_law: Array of expression stringsinput_boundary_patches: (Optional) Object containing entity referencesoutput_target: (Optional) Object containing entity reference
Example JSON Configuration#
The following example shows a complete JSON configuration for a spring-mass-damper system controlling rotation:
{
"input_vars": [
"momentY"
],
"constants": {
"I": 0.443768309310345,
"zeta": 0.005,
"K": 0.005,
"omegaN": 0.10614678867902483,
"theta0": 0.08726646259971647
},
"output_vars": {
"omegaDot": "state[0];",
"omega": "state[1];",
"theta": "state[2];"
},
"state_vars_initial_value": [
"-0.008849191649331902",
"0.0",
"0.8726646259971648"
],
"update_law": [
"(pseudoStep == 0) ? ((momentY - K * ( state[2] - theta0 ) - 2 * zeta * omegaN * I *state[1] ) / I) : (state[0]);",
"(pseudoStep == 0) ? (state[1] + state[0] * timeStepSize) : (state[1]);",
"(pseudoStep == 0) ? (state[2] + state[1] * timeStepSize) : (state[2]);"
],
"input_boundary_patches": {
"stored_entities": [
{
"name": "plateBlock/noSlipWall"
}
]
},
"output_target": {
"name": "plateBlock"
}
}
Corresponding Python Code#
The JSON configuration above corresponds to the following Python API code:
dynamic = fl.UserDefinedDynamic(
name="dynamicTheta",
input_vars=["momentY"],
constants={
"I": I,
"zeta": zeta,
"K": K,
"omegaN": omegaN,
"theta0": theta0,
},
output_vars={
"omegaDot": "state[0];",
"omega": "state[1];",
"theta": "state[2];",
},
state_vars_initial_value=[str(initOmegaDot), "0.0", str(initTheta)],
update_law=[
"if (pseudoStep == 0) (momentY - K * ( state[2] - theta0 ) - 2 * zeta * omegaN * I *state[1] ) / I; else state[0];",
"if (pseudoStep == 0) state[1] + state[0] * timeStepSize; else state[1];",
"if (pseudoStep == 0) state[2] + state[1] * timeStepSize; else state[2];",
],
input_boundary_patches=vm["plateBlock/noSlipWall"],
output_target=vm["plateBlock"],
)
Note: In the JSON version, the conditional expressions in update_law use ternary operator syntax (condition) ? (true_value) : (false_value) instead of the if-else statements shown in the Python code.
Best Practices#
Start with simple motions and gradually add complexity
Test your UDD implementation with coarse meshes before running full simulations
Ensure mesh rotation settings are appropriate for the expected range of motion
Monitor forces and moments during motion to ensure physical behavior
Use the Python API when possible for easier debugging and more intuitive configuration