Forces and Moments using UDD

Contents

Forces and Moments using UDD#

This example demonstrates the utilization of the User Defined Dynamics (UDD) feature within the Flow360 Python API to compute aerodynamic forces and moments, specifically focusing on hinge torques for control surfaces like ailerons and rudders. The script outlines the setup process, including geometry handling, mesh generation with targeted refinements, defining simulation parameters using SimulationParams, configuring the Spalart-Allmaras turbulence model, specifying outputs, and importantly, configuring multiple UserDefinedDynamic objects to calculate hinge torques based on runtime force and moment data from specific boundary patches.

  1import flow360 as fl
  2from flow360.examples import TutorialUDDForcesMoments
  3
  4TutorialUDDForcesMoments.get_files()
  5
  6project = fl.Project.from_geometry(
  7    TutorialUDDForcesMoments.geometry,
  8    name="Tutorial UDD forces and moments from Python",
  9)
 10geometry = project.geometry
 11
 12geometry.show_available_groupings()
 13geometry.group_edges_by_tag("edgeName")
 14geometry.group_faces_by_tag("groupName")
 15
 16with fl.SI_unit_system:
 17    box1 = fl.Box(name="box1", size=[2, 6, 3], center=[6.5, 9, 0], axis_of_rotation=[0, 1, 0])
 18    box2 = fl.Box(name="box2", size=[2, 6, 3], center=[6.5, -9, 0], axis_of_rotation=[0, 1, 0])
 19    box3 = fl.Box(name="box3", size=[4, 8, 3], center=[12, 0, 2], axis_of_rotation=[0, 1, 0])
 20    farfield = fl.AutomatedFarfield()
 21    params = fl.SimulationParams(
 22        meshing=fl.MeshingParams(
 23            defaults=fl.MeshingDefaults(
 24                surface_max_edge_length=0.5,
 25                curvature_resolution_angle=10 * fl.u.deg,
 26                boundary_layer_first_layer_thickness=2e-6,
 27                boundary_layer_growth_rate=1.2,
 28            ),
 29            refinement_factor=1,
 30            refinements=[
 31                fl.SurfaceEdgeRefinement(
 32                    method=fl.AngleBasedRefinement(value=1 * fl.u.deg),
 33                    edges=[geometry["leadingEdge"]],
 34                ),
 35                fl.SurfaceEdgeRefinement(
 36                    method=fl.HeightBasedRefinement(value=5e-3), edges=[geometry["trailingEdge"]]
 37                ),
 38                fl.SurfaceRefinement(max_edge_length=0.5, faces=[geometry["wing*"]]),
 39                fl.UniformRefinement(
 40                    name="box_refinement1", entities=[box1, box2, box3], spacing=0.2
 41                ),
 42            ],
 43            volume_zones=[farfield],
 44        ),
 45        reference_geometry=fl.ReferenceGeometry(
 46            area=60, moment_center=[5.7542, 0, 0], moment_length=[1, 1, 1]
 47        ),
 48        operating_condition=fl.AerospaceCondition(
 49            velocity_magnitude=50,
 50            alpha=10 * fl.u.deg,
 51            atmosphere=fl.ThermalState(temperature=288.15),
 52        ),
 53        models=[
 54            fl.Fluid(
 55                navier_stokes_solver=fl.NavierStokesSolver(
 56                    absolute_tolerance=1e-9, linear_solver=fl.LinearSolver(max_iterations=35)
 57                ),
 58                turbulence_model_solver=fl.SpalartAllmaras(
 59                    linear_solver=fl.LinearSolver(max_iterations=25)
 60                ),
 61            ),
 62            fl.Wall(
 63                surfaces=[geometry["*Left"], geometry["*Right"], geometry["fuselage"]],
 64            ),
 65            fl.Freestream(surfaces=[farfield.farfield]),
 66        ],
 67        time_stepping=fl.Steady(max_steps=5000),
 68        outputs=[
 69            fl.VolumeOutput(
 70                output_fields=["primitiveVars", "vorticity", "qcriterion", "Cp", "Mach"]
 71            ),
 72            fl.SurfaceOutput(
 73                surfaces=[geometry["*"]],
 74                output_fields=["primitiveVars", "Cf", "wallDistance", "Cp", "CfVec", "yPlus"],
 75            ),
 76        ],
 77        user_defined_dynamics=[
 78            fl.UserDefinedDynamic(
 79                name="rightAileronHingeTorque",
 80                input_vars=["forceX", "forceY", "forceZ", "momentX", "momentY", "momentZ"],
 81                constants={
 82                    "density_kgpm3": 1.225,
 83                    "c_inf_mps": 340.29400580821283,
 84                    "l_grid_unit": 1,
 85                    "newCenterX": 5.7542,
 86                    "newCenterY": 7,
 87                    "newCenterZ": 0,
 88                    "newAxisX": 0,
 89                    "newAxisY": 1,
 90                    "newAxisZ": 0,
 91                },
 92                state_vars_initial_value=["0.0", "0.0", "0.0", "0.0", "0.0"],
 93                update_law=[
 94                    "density_kgpm3 * c_inf_mps * c_inf_mps * l_grid_unit * l_grid_unit * l_grid_unit;",
 95                    "(momentX - ((newCenterY - momentCenterY) * forceZ - (newCenterZ - momentCenterZ) * forceY)) * state[0];",
 96                    "(momentY + ((newCenterX - momentCenterX) * forceZ - (newCenterZ - momentCenterZ) * forceX)) * state[0];",
 97                    "(momentZ - ((newCenterX - momentCenterX) * forceY - (newCenterY - momentCenterY) * forceX)) * state[0];",
 98                    "state[1] * newAxisX + state[2] * newAxisY + state[3] * newAxisZ;",
 99                ],
100                input_boundary_patches=[geometry["aileronRight"]],
101            ),
102            fl.UserDefinedDynamic(
103                name="leftAileronHingeTorque",
104                input_vars=["forceX", "forceY", "forceZ", "momentX", "momentY", "momentZ"],
105                constants={
106                    "density_kgpm3": 1.225,
107                    "c_inf_mps": 340.29400580821283,
108                    "l_grid_unit": 1,
109                    "newCenterX": 5.7542,
110                    "newCenterY": -7,
111                    "newCenterZ": 0,
112                    "newAxisX": 0,
113                    "newAxisY": -1,
114                    "newAxisZ": 0,
115                },
116                state_vars_initial_value=["0.0", "0.0", "0.0", "0.0", "0.0"],
117                update_law=[
118                    "density_kgpm3 * c_inf_mps * c_inf_mps * l_grid_unit * l_grid_unit * l_grid_unit",
119                    "(momentX - ((newCenterY - momentCenterY) * forceZ - (newCenterZ - momentCenterZ) * forceY)) * state[0];",
120                    "(momentY + ((newCenterX - momentCenterX) * forceZ - (newCenterZ - momentCenterZ) * forceX)) * state[0];",
121                    "(momentZ - ((newCenterX - momentCenterX) * forceY - (newCenterY - momentCenterY) * forceX)) * state[0];",
122                    "state[1] * newAxisX + state[2] * newAxisY + state[3] * newAxisZ ",
123                ],
124                input_boundary_patches=[geometry["aileronLeft"]],
125            ),
126            fl.UserDefinedDynamic(
127                name="rightRudderHingeTorque",
128                input_vars=["forceX", "forceY", "forceZ", "momentX", "momentY", "momentZ"],
129                constants={
130                    "density_kgpm3": 1.225,
131                    "c_inf_mps": 340.29400580821283,
132                    "l_grid_unit": 1,
133                    "newCenterX": 12.01,
134                    "newCenterY": 0.861,
135                    "newCenterZ": 0.861,
136                    "newAxisX": 0,
137                    "newAxisY": 0.7071,
138                    "newAxisZ": 0.7071,
139                },
140                state_vars_initial_value=["0.0", "0.0", "0.0", "0.0", "0.0"],
141                update_law=[
142                    "density_kgpm3 * c_inf_mps * c_inf_mps * l_grid_unit * l_grid_unit * l_grid_unit",
143                    "(momentX - ((newCenterY - momentCenterY) * forceZ - (newCenterZ - momentCenterZ) * forceY)) * state[0];",
144                    "(momentY + ((newCenterX - momentCenterX) * forceZ - (newCenterZ - momentCenterZ) * forceX)) * state[0];",
145                    "(momentZ - ((newCenterX - momentCenterX) * forceY - (newCenterY - momentCenterY) * forceX)) * state[0];",
146                    "state[1] * newAxisX + state[2] * newAxisY + state[3] * newAxisZ ",
147                ],
148                input_boundary_patches=[geometry["rudderRight"]],
149            ),
150            fl.UserDefinedDynamic(
151                name="leftRudderHingeTorque",
152                input_vars=["forceX", "forceY", "forceZ", "momentX", "momentY", "momentZ"],
153                constants={
154                    "density_kgpm3": 1.225,
155                    "c_inf_mps": 340.29400580821283,
156                    "l_grid_unit": 1,
157                    "newCenterX": 12.01,
158                    "newCenterY": -0.861,
159                    "newCenterZ": 0.861,
160                    "newAxisX": 0,
161                    "newAxisY": -0.7071,
162                    "newAxisZ": 0.7071,
163                },
164                state_vars_initial_value=["0.0", "0.0", "0.0", "0.0", "0.0"],
165                update_law=[
166                    "density_kgpm3 * c_inf_mps * c_inf_mps * l_grid_unit * l_grid_unit * l_grid_unit",
167                    "(momentX - ((newCenterY - momentCenterY) * forceZ - (newCenterZ - momentCenterZ) * forceY)) * state[0];",
168                    "(momentY + ((newCenterX - momentCenterX) * forceZ - (newCenterZ - momentCenterZ) * forceX)) * state[0];",
169                    "(momentZ - ((newCenterX - momentCenterX) * forceY - (newCenterY - momentCenterY) * forceX)) * state[0];",
170                    "state[1] * newAxisX + state[2] * newAxisY + state[3] * newAxisZ ",
171                ],
172                input_boundary_patches=[geometry["rudderLeft"]],
173            ),
174        ],
175    )
176
177project.run_case(params, name="Case of tutorial UDD forces and moments from Python")

Notes#

  • Multiple fl.UserDefinedDynamic instances are configured to calculate hinge torques for different control surfaces (ailerons, rudders). Each UDD instance takes forces and moments from specified input_boundary_patches as input_vars.

  • The hinge torque calculation logic resides within the update_law expressions, which perform moment transformations from the reference moment center to the specified hinge axis using provided constants (like hinge location and axis) and the input forces/moments.

  • Mesh refinements, including fl.UniformRefinement using fl.Box volumes and fl.SurfaceEdgeRefinement, are employed to enhance grid resolution in areas critical to accurate force prediction, such as around the wing and control surfaces.