Aeroacoustics#
This script demonstrates the setup for obtaining aeroacoustic results for a quadcopter configuration using the Flow360 Python API. It outlines the process of defining rotating zones, configuring an unsteady simulation vital for capturing time-varying flow features, and setting up aeroacoustic outputs based on the Ffowcs-Williams and Hawkings (FWH) analogy. The example focuses on specifying acoustic observer locations and retrieving the resulting acoustic data.
1import pandas as pd
2
3import flow360 as fl
4from flow360.examples import Quadcopter
5
6Quadcopter.get_files()
7
8project = fl.Project.from_volume_mesh(
9 Quadcopter.mesh_filename, name="Aeroacoustic results from Python"
10)
11
12vm = project.volume_mesh
13
14with fl.SI_unit_system:
15 rotation_zone_1 = vm["zone_r1"]
16 rotation_zone_1.center = [-0.125, 0.125, 0.0055]
17 rotation_zone_1.axis = [0, 0, 1]
18
19 rotation_zone_2 = vm["zone_r2"]
20 rotation_zone_2.center = [-0.125, -0.125, 0.0055]
21 rotation_zone_2.axis = [0, 0, -1]
22
23 rotation_zone_3 = vm["zone_r3"]
24 rotation_zone_3.center = [0.125, -0.125, 0.0055]
25 rotation_zone_3.axis = [0, 0, 1]
26
27 rotation_zone_4 = vm["zone_r4"]
28 rotation_zone_4.center = [0.125, 0.125, 0.0055]
29 rotation_zone_4.axis = [0, 0, -1]
30
31 omega = 6000 * fl.u.rpm
32
33 # Time step size will be calculated based on predetermined degrees per time step (3 deg for this run)
34 deg_per_time_step_0 = 3.0 * fl.u.deg
35 time_step_0 = deg_per_time_step_0 / omega.to("deg/s")
36
37 # Amount of time steps will be adjusted to satisfy the required amount of revolutions (5 rev for this run)
38 revolution_time_0 = 360 * fl.u.deg / omega.to("deg/s")
39 steps_0 = int(5 * revolution_time_0 / time_step_0)
40
41 params = fl.SimulationParams(
42 reference_geometry=fl.ReferenceGeometry(
43 area=0.0447726728530549,
44 moment_center=[0, 0, 0],
45 moment_length=[0.11938, 0.11938, 0.11938],
46 ),
47 operating_condition=fl.AerospaceCondition.from_mach(
48 mach=0,
49 thermal_state=fl.ThermalState(temperature=293.15),
50 reference_mach=0.21868415800906676,
51 ),
52 time_stepping=fl.Unsteady(
53 step_size=time_step_0,
54 steps=steps_0,
55 ),
56 models=[
57 fl.Fluid(
58 navier_stokes_solver=fl.NavierStokesSolver(
59 absolute_tolerance=1e-10,
60 relative_tolerance=0.1,
61 order_of_accuracy=1,
62 linear_solver=fl.LinearSolver(max_iterations=30),
63 ),
64 turbulence_model_solver=fl.SpalartAllmaras(
65 absolute_tolerance=1e-8,
66 relative_tolerance=0.1,
67 order_of_accuracy=1,
68 linear_solver=fl.LinearSolver(max_iterations=20),
69 ),
70 ),
71 fl.Wall(
72 surfaces=[
73 vm["zone_s/airframe"],
74 vm["zone_r1/blade1"],
75 vm["zone_r2/blade2"],
76 vm["zone_r3/blade3"],
77 vm["zone_r4/blade4"],
78 ],
79 ),
80 fl.Freestream(surfaces=vm["zone_s/farfield"]),
81 fl.Rotation(
82 name="Rotation",
83 volumes=[rotation_zone_1, rotation_zone_2, rotation_zone_3, rotation_zone_4],
84 spec=fl.AngularVelocity(value=omega),
85 ),
86 ],
87 )
88
89case = project.run_case(params, "First order run")
90
91
92case.params.models[0].navier_stokes_solver.order_of_accuracy = 2
93case.params.models[0].navier_stokes_solver.linear_solver = fl.LinearSolver(max_iterations=25)
94
95case.params.models[0].turbulence_model_solver.order_of_accuracy = 2
96case.params.models[0].turbulence_model_solver.linear_solver = fl.LinearSolver(max_iterations=25)
97
98deg_per_time_step_1 = 0.404496 * fl.u.deg
99time_step_1 = deg_per_time_step_1 / omega.to("deg/s")
100
101revolution_time_1 = 360 * fl.u.deg / omega.to("deg/s")
102steps_1 = int(5 * revolution_time_1 / time_step_1)
103
104case.params.time_stepping.step_size = time_step_1
105case.params.time_stepping.steps = steps_1
106
107case_fork_1 = project.run_case(case.params, "Second order run", fork_from=case)
108
109case_fork_1.params.outputs = [
110 fl.AeroAcousticOutput(
111 observers=[
112 fl.Observer(position=[0, -1.905, 0] * fl.u.m, group_name="1"),
113 fl.Observer(position=[0, -1.7599905, -0.72901194] * fl.u.m, group_name="1"),
114 fl.Observer(position=[0, -1.3470384, -1.3470384] * fl.u.m, group_name="1"),
115 fl.Observer(position=[0.9525, -0.9525, -1.3470384] * fl.u.m, group_name="1"),
116 fl.Observer(position=[1.3470384, 0, -1.3470384] * fl.u.m, group_name="1"),
117 fl.Observer(position=[0, 0, 1.905] * fl.u.m, group_name="1"),
118 fl.Observer(position=[0, -0.37164706, 1.8683959] * fl.u.m, group_name="1"),
119 fl.Observer(position=[0, -1.868396, 0.37164707] * fl.u.m, group_name="1"),
120 fl.Observer(position=[1.295, 0, -0.767] * fl.u.m, group_name="2"),
121 ],
122 write_per_surface_output=True,
123 )
124]
125
126case_fork_2 = project.run_case(case_fork_1.params, "Final run", fork_from=case_fork_1)
127
128case_fork_2.wait()
129
130results = case_fork_2.results
131
132total_acoustics = results.aeroacoustics
133print(total_acoustics)
134
135# There are also surface specific aeroacoustic output files
136blade_1_acoustics = results.download_file_by_name(
137 "results/surface_zone_r1_blade1_acoustics_v3.csv", to_folder="aeroacoustic_results"
138)
139blade_1_acoustics = pd.read_csv(blade_1_acoustics)
140print(blade_1_acoustics)
141blade_2_acoustics = results.download_file_by_name(
142 "results/surface_zone_r2_blade2_acoustics_v3.csv", to_folder="aeroacoustic_results"
143)
144blade_2_acoustics = pd.read_csv(blade_2_acoustics)
145print(blade_2_acoustics)
146blade_3_acoustics = results.download_file_by_name(
147 "results/surface_zone_r3_blade3_acoustics_v3.csv", to_folder="aeroacoustic_results"
148)
149blade_3_acoustics = pd.read_csv(blade_3_acoustics)
150print(blade_3_acoustics)
151blade_4_acoustics = results.download_file_by_name(
152 "results/surface_zone_r4_blade4_acoustics_v3.csv", to_folder="aeroacoustic_results"
153)
154blade_4_acoustics = pd.read_csv(blade_4_acoustics)
155print(blade_4_acoustics)
Notes#
The script showcases the configuration of aeroacoustic data computation using
fl.AeroAcousticOutput
and the definition of microphone locations viafl.Observer
.It illustrates how to retrieve both the total acoustic results aggregated over all sources and the acoustic contributions from individual surfaces after the simulation concludes.
A simulation forking strategy (using the
fork_from
argument inproject.run_case
) is employed to initialize the flow field before adding the specific acoustic output requests for the final simulation phase.