# User Defined Dynamics

## Contents

# 7.10. User Defined Dynamics#

In Flow360, users are able to specify arbitrary dynamics. This section provides an example for a Proportional-Integral (PI) controller and an example for Aero-Structural Interaction (ASI).

## 7.10.1. PI Controller for Angle of Attack to Control Lift Coefficient#

In this example, we add a controller to the om6Wing case study. Based on the entries of the configuration file in this case study, the resulting value of the lift coefficient, *CL*, obtained from the simulation is ~0.26. Our objective is to add a controller to this case study such that for a given target value of *CL* (e.g., 0.4), the required angle of attack, `alphaAngle`

, is estimated. To this end, a PI controller is defined under `userDefinedDynamics`

in the configuration file as:

```
"userDefinedDynamics": [
{
"dynamicsName": "alphaController",
"inputVars": [
"CL"
],
"constants": {
"CLTarget": 0.4,
"Kp": 0.2,
"Ki": 0.002
},
"outputVars": {
"alphaAngle": "if (pseudoStep > 500) state[0]; else alphaAngle;"
},
"stateVarsInitialValue": [
"alphaAngle",
"0.0"
],
"updateLaw": [
"if (pseudoStep > 500) state[0] + Kp * (CLTarget - CL) + Ki * state[1]; else state[0];",
"if (pseudoStep > 500) state[1] + (CLTarget - CL); else state[1];"
],
"inputBoundaryPatches": [
"1"
]
}
]
```

More details for some of the parameters are explained below:

**dynamicsName**: This is a name given by the user for the dynamics. The results of user-defined dynamics are saved to “udd_dynamicsName_v2.csv”.**constants**: This includes a list of all the constants related to this UDD. These constants will be used in the equations for`updateLaw`

and`outputVars`

. In the above-mentioned example,`CLTarget`

,`Kp`

, and`Ki`

are the target value of the lift coefficient, the proportional gain of the controller, and the integral gain of the controller, respectively.**outputVars**: Each`outputVars`

consists of the variable name and the relations between the variable and the state variables. For this example, we set the output variable equal to the angle of attack calculated by the controller after the first 500 pseudoSteps (i.e.,`state[0]`

).**stateVarsInitialValue**: There are two state variables for this controller. The first one,`state[0]`

, is the angle of attack computed by the controller, and the second one,`state[1]`

, is the summation of error`(CLTarget - CL)`

over iterations. The initial values of`state[0]`

and`state[1]`

are set to`alphaAngle`

and`0`

, respectively.

The equation for the control system can be thus written as:

where the superscript \(n\) denotes pseudo step, `alphaAngle`

(\(\alpha\)) is `state[0]`

and the integral of error (\(e\)) is `state[1]`

.

**updateLaw**: The expressions for the state variables are specified here. The first and the second expressions correspond to equations for`state[0]`

and`state[1]`

, respectively. The conditional statement forces the controller to not be run for the first 500 pseudoSteps. This ensures that the flow field is established before the controller is initialized.**inputBoundaryPatches**: The related boundary conditions for the inputs of the user-defined dynamics are specified here. In this example, the noSlipWall boundary conditions are where`CL`

is calculated. As seen in the configuration file, the boundary name for the No-Slip boundary condition is`"1"`

.

The following figure shows the values of lift coefficient, `CL`

, and angle of attack, `alphaAngle`

, versus the steps, `pseudoStep`

. As specified in the configuration file, the initial value of angle of attack is `3.06`

. With the specified user-defined dynamics, `alphaAngle`

is adjusted to a final value of `4.61`

so that `CL = 0.4`

is achieved.

## 7.10.2. Dynamic Grid Rotation using Structural Aerodynamic Load#

In this example, we present an illustrative case where a flat plate rotates while being subjected to a rotational spring moment and damper as well as aerodynamic loads.

As you can see below, the flate plate mesh consists of two zones separated by a circular sliding interface. The inner zone will rotate while the outer zone remains stationary. This is the same mesh topology as what is used for rotating propeller simulations.

Below is a video showing the flutter motion of the plate.

The `userDefinedDynamics`

and `slidingInterfaces`

section of the case JSON is presented below:

```
1{
2 "slidingInterfaces": [
3 {
4 "stationaryPatches": [
5 "farFieldBlock/rotIntf"
6 ],
7 "rotatingPatches": [
8 "plateBlock/rotIntf"
9 ],
10 "axisOfRotation": [
11 0,
12 1,
13 0
14 ],
15 "centerOfRotation": [
16 0,
17 0,
18 0
19 ],
20 "volumeName": "plateBlock",
21 "theta": [
22 "0",
23 "0",
24 "0"
25 ]
26 }
27 ],
28 "userDefinedDynamics": [
29 {
30 "dynamicsName": "dynamicTheta",
31 "inputVars": [
32 "momentY"
33 ],
34 "constants": {
35 "I": 0.443768309310345,
36 "zeta": 4.0,
37 "K": 0.0161227107,
38 "omegaN": 0.190607889,
39 "theta0": 0.0872664626
40 },
41 "outputVars": {
42 "omegaDot": "state[0];",
43 "omega": "state[1];",
44 "theta": "state[2];"
45 },
46 "stateVarsInitialValue": [
47 "-1.82621384e-02",
48 "0.0",
49 "1.39626340e-01"
50 ],
51 "updateLaw": [
52 "if (pseudoStep == 0) (momentY - K * ( state[2] - theta0 ) - 2 * zeta * omegaN * I *state[1] ) / I; else state[0];",
53 "if (pseudoStep == 0) state[1] + state[0] * timeStepSize; else state[1];",
54 "if (pseudoStep == 0) state[2] + state[1] * timeStepSize; else state[2];"
55 ],
56 "inputBoundaryPatches": [
57 "plateBlock/noSlipWall"
58 ],
59 "outputTargetName": "plateBlock"
60 }
61 ]
62}
```

For this case the dynamics of the plate are:

which corresponds to the three `updateLaw`

arguments in the above JSON input file. The symbols are defined in the table below.

Symbol |
Description |
---|---|

\(\theta\) |
Rotation angle of the plate in radians. |

\(\omega\) |
Rotation velocity of the plate in radians. |

\(\dot{\omega}\) |
Rotation acceleration of the plate in radians. |

\(\tau_y\) |
Aerodynamic moment exerted on the plate. |

\(K\) |
Stiffness of the spring attached to the plate at the structural support. |

\(\zeta\) |
Structural damping ratio. |

\(\omega_N\) |
Structural natural angular frequency. |

\(I\) |
Structural moment of inertia. |

In the `inputVars`

, the `momentY`

is the y-component of the rotational moment imposed by the aerodynamic loading. The moment is calculated relative to the momentCenter on `"plateBlock/noSlipWall"`

.

The name of rotating volume zone that we want to control is `plateBlock`

and thus the `"outputTargetName"`

is set to `plateBlock`

. The first, second and third state variable correspond to \(\dot{\omega}\), \(\omega\), and \(\theta\), respectively.

## 7.10.3. Custom set of user defined forces and moments#

In this example we will use a simple airplane geometry with control surfaces (aileron and rudders) to showcase how a user could calculate user defined forces and moments to track the evolution of the torque on the control surfaces as the simulation converges.

To generate the geometry and associated mesh we will simply use these input csm , mesh surface definition , volume definition. See this example tutorial or this one for more information on how to do that.

As you can see in this image below, the airplane has 2 ailerons and 2 rudders. All 4 control surfaces move in a simple rotation around an axis. For each of these we will monitor the torque around the rotation axis.

Note

Due to the coarseness of the mesh settings selected, the solver may give you a warning when you go to submit the case with that configuration. You can ignore it and proceed to the case submission.

Now that we have a volume mesh ready we need to define the run parameters. In this tutorial we will focus on the definition of `userDefinedDynamics`

. Please download this sample input json file. The setup is for our simple airplane flying at 50m/s at 10 ° angle of attack.

The control surfaces have their rotation centers and axes as:

Name |
rotation Center |
rotation Axis |
---|---|---|

right Aileron |
(5.7542, 7, 0) |
(0, 1, 0) |

left Aileron |
(5.7542, -7, 0) |
(0, -1, 0) |

right Rudder |
(12.01, 0.861, 0.861) |
(0, 0.7071, 0.7071) |

left Rudder |
(12.01, -0.861, 0.861) |
(0, -0.7071, 0.7071) |

Note

In this tutorial we have arbitrarily set the `geometry/momentCenter`

at the same X and Z location as the Aileron rotation axes ( as in, on the Ailerons’ rotation axes). Since the aileron hinges axes is aligned with the Y axis, this allows us to check the hinge moment values we get for the ailerons against the Moment Y value for that same aileron automatically generated by the software. They are the same.

The interesting part is the definition of the control surface hinge moments via user defined variables and the automatic tracking of the outputs to get the evolution of those forces as the run converges to see if we have reached satisfactory convergence of the desired forces and moments.

Here, we will convert the monitored torques into SI forces (N) and moments (Nm).

The input json file has an optional `userDefinedDynamics`

(UDD) section which contains 4 similar items. Each control surface requires its own `userDefinedDynamics`

instance with the only differences being the names and geometric inputs for each control surface as per the table above.

If we look at the “userDefinedDynamics” section of the configuration json file we see that the UDD instance has 4 items each containing 6 sections. The 4 items are the 4 control surfaces. The 6 sections are described below.

### 7.10.3.1. constants#

Named constants that will be used in the update law. “newCenterX,Y,Z” and “newAxisX,Y,Z” are the coordinates of the new moment reference center and rotation axes we want to project onto. They define the aileron or rudder’s hingelines.

1"constants": { 2 "density_kgpm3": 1.225, 3 "c_inf_mps": 340.2, 4 "l_grid_unit": 1, 5 "newCenterX": 5.7542, 6 "newCenterY": 7, 7 "newCenterZ": 0, 8 "newAxisX": 0, 9 "newAxisY": 1, 10 "newAxisZ": 0 11}

### 7.10.3.2. updateLaw#

This block is a list of expressions used to define the outputs we want to save. Because we convert the raw solver outputs to dimensional metric value we need to calculate the \(\rho_\infty C_\infty^2 L_\text{gridUnit}^3\) :ref:` moment dimensionalization values<nondim_output>`.

The equation on lines 2 defines that dimensionalization value. Lines 3 to 5 define the new dimensional moments as per the new moment reference center and rotation axis we defined above. The last line defines the total hinge moment, as in the torque that the control surface’s actuating mechanism will have to overcome to rotate the control surface.

```
1"updateLaw": [
2 "density_kgpm3 * c_inf_mps * c_inf_mps * l_grid_unit * l_grid_unit * l_grid_unit",
3 "(rotMomentX - ((newCenterY - momentCenterY) * forceZ - (newCenterZ - momentCenterZ) * forceY)) * state [0];",
4 "(rotMomentY + ((newCenterX - momentCenterX) * forceZ - (newCenterZ - momentCenterZ) * forceX)) * state [0];",
5 "(rotMomentZ - ((newCenterX - momentCenterX) * forceY - (newCenterY - momentCenterY) * forceX)) * state [0];",
6 "state[1] * newAxisX + state[2] * newAxisY + state[3] * newAxisZ "
7]
```

### 7.10.3.3. inputBoundaryPatches#

Patches of the mesh that we want to apply the calculations to. For example:

1"inputBoundaryPatches": [ "fluid/aileronRight" ]

### 7.10.3.4. visualizing the results#

All the UDD outputs can be downloaded in the `results/udd_DYNAMICSNAME_v2.csv`

where `DYNAMICSNAME`

matches the values entered in the “dynamicsName” section.

They can also be automatically visualized in the `Monitors`

tab for that case on our website.
Remember that the hinge torque’s dimensional value is calculated in `state[4]`

. By clicking on each line’s legend icon you can disable the values you are not interested in, leaving only the desired values’ convergence history displayed.

A good way to check that the calculations are correct is to compare the overall forces on a given component with the resulting torque values. If you recall above it was mentioned that we have purposefully put the simulation `geometry/momentCenter`

on the aileron hinge line. Both are at X=5.7542 and since the aileron hinge vector [0,1,0] is aligned with the Y axis, the left and right aileron My magnitudes are identical to the left and right aileron hinge torque. The left aileron has it hinge vector set to [0,-1,0] hence the sign will be flipped between My and hinge torque.

To check that the calculations are correct let’s get the final value of the right aileron hinge torque. To do that please click on the DataView icon circled in red in the above figure. Scroll to the `state[4]`

column and down to the last row. Make sure to navigate to the table’s final section(by clicking on the 6 below the table as circled in red below) That highlighted value of -52.65Nm is the right aileron hinge torque in dimensional Newton-meters.

Now by going back to the `Forces`

tab, down to the `Forces and Moments by surface`

section, we can similarly find the final CMy= \(-5.73 \times 10^{-4}\) for the right aileron:

Now if we dimensionalize that value, we get