Automotive Report

Contents

Automotive Report#

This script demonstrates the automated generation of a comprehensive aerodynamic report for the DrivAer automotive model using the Flow360 Python API and its reporting plugin. It sets up and runs a series of steady-state simulations at varying yaw angles (beta = 0/5/10 degrees). Post-simulation, it utilizes the flow360.plugins.report module to compile results, including statistical data, force coefficients, and various flow visualizations (e.g., surface pressure coefficient, wall shear stress, flow field slices, isosurfaces) into a structured PDF document.

  1import flow360 as fl
  2from flow360 import u
  3from flow360.examples import DrivAer
  4from flow360.log import log
  5from flow360.plugins.report.report import ReportTemplate
  6from flow360.plugins.report.report_items import (
  7    BottomCamera,
  8    Chart2D,
  9    Chart3D,
 10    FrontCamera,
 11    FrontLeftBottomCamera,
 12    FrontLeftTopCamera,
 13    Inputs,
 14    LeftCamera,
 15    RearCamera,
 16    RearLeftTopCamera,
 17    RearRightBottomCamera,
 18    Settings,
 19    Summary,
 20    Table,
 21    TopCamera,
 22)
 23from flow360.plugins.report.utils import Average, DataItem, Delta, Expression, Variable
 24from flow360.version import __solver_version__
 25
 26DrivAer.get_files()
 27
 28project = fl.Project.from_volume_mesh(
 29    DrivAer.mesh_filename,
 30    name="Automotive DrivAer",
 31)
 32
 33vm = project.volume_mesh
 34
 35log.info("Volume mesh contains the following boundaries:")
 36for boundary in vm.boundary_names:
 37    log.info("Boundary: " + boundary)
 38
 39freestream_surfaces = ["blk-1/WT_side1", "blk-1/WT_side2", "blk-1/WT_inlet", "blk-1/WT_outlet"]
 40slip_wall_surfaces = ["blk-1/WT_ceiling", "blk-1/WT_ground_front", "blk-1/WT_ground"]
 41wall_surfaces = list(set(vm.boundary_names) - set(freestream_surfaces) - set(slip_wall_surfaces))
 42
 43cases = []
 44
 45for beta in [0, 5, 10]:
 46    with fl.SI_unit_system:
 47        params = fl.SimulationParams(
 48            meshing=None,
 49            reference_geometry=fl.ReferenceGeometry(area=2.17, moment_length=2.7862),
 50            operating_condition=fl.AerospaceCondition(velocity_magnitude=40, beta=beta * u.deg),
 51            models=[
 52                fl.Wall(surfaces=[vm[i] for i in wall_surfaces], use_wall_function=True),
 53                fl.Freestream(
 54                    surfaces=[vm[i] for i in freestream_surfaces],
 55                ),
 56                fl.SlipWall(
 57                    surfaces=[vm[i] for i in slip_wall_surfaces],
 58                ),
 59            ],
 60            outputs=[
 61                fl.SurfaceOutput(
 62                    surfaces=vm["*"],
 63                    output_fields=[
 64                        "Cp",
 65                        "Cf",
 66                        "yPlus",
 67                        "CfVec",
 68                        "primitiveVars",
 69                        "wall_shear_stress_magnitude",
 70                    ],
 71                ),
 72                fl.SliceOutput(
 73                    entities=[
 74                        *[
 75                            fl.Slice(
 76                                name=f"slice_y_{name}",
 77                                normal=(0, 1, 0),
 78                                origin=(0, y, 0),
 79                            )
 80                            for name, y in zip(
 81                                ["0", "0_2", "0_4", "0_6", "0_8"], [0, 0.2, 0.4, 0.6, 0.8]
 82                            )
 83                        ],
 84                        *[
 85                            fl.Slice(
 86                                name=f"slice_z_{name}",
 87                                normal=(0, 0, 1),
 88                                origin=(0, 0, z),
 89                            )
 90                            for name, z in zip(
 91                                ["neg0_2", "0", "0_2", "0_4", "0_6", "0_8"],
 92                                [-0.2, 0, 0.2, 0.4, 0.6, 0.8],
 93                            )
 94                        ],
 95                    ],
 96                    output_fields=["velocity", "velocity_x", "velocity_y", "velocity_z"],
 97                ),
 98                fl.IsosurfaceOutput(
 99                    output_fields=["Cp", "Mach"],
100                    isosurfaces=[
101                        fl.Isosurface(
102                            name="isosurface-cpt",
103                            iso_value=-1,
104                            field="Cpt",
105                        ),
106                    ],
107                ),
108                fl.ProbeOutput(
109                    entities=[fl.Point(name="point1", location=(10, 0, 1))],
110                    output_fields=["velocity"],
111                ),
112            ],
113        )
114
115    case_new = project.run_case(params=params, name=f"DrivAer 5.7M - beta={beta}")
116
117    cases.append(case_new)
118
119# wait until all cases finish running
120for case in cases:
121    case.wait()
122
123exclude = ["blk-1/WT_ground_close", "blk-1/WT_ground_patch"]
124size = "5.7M"
125
126exclude += freestream_surfaces + slip_wall_surfaces
127
128SOLVER_VERSION = "report-25.4.0"
129
130top_camera = TopCamera(pan_target=(1.5, 0, 0), dimension=5, dimension_dir="width")
131top_camera_slice = TopCamera(pan_target=(2.5, 0, 0), dimension=8, dimension_dir="width")
132side_camera = LeftCamera(pan_target=(1.5, 0, 0), dimension=5, dimension_dir="width")
133side_camera_slice = LeftCamera(pan_target=(2.5, 0, 1.5), dimension=8, dimension_dir="width")
134rear_camera = RearCamera(dimension=2.5, dimension_dir="width")
135front_camera = FrontCamera(dimension=2.5, dimension_dir="width")
136bottom_camera = BottomCamera(pan_target=(1.5, 0, 0), dimension=5, dimension_dir="width")
137front_left_bottom_camera = FrontLeftBottomCamera(
138    pan_target=(1.5, 0, 0), dimension=5, dimension_dir="width"
139)
140rear_right_bottom_camera = RearRightBottomCamera(
141    pan_target=(1.5, 0, 0), dimension=6, dimension_dir="width"
142)
143front_left_top_camera = FrontLeftTopCamera(
144    pan_target=(1.5, 0, 0), dimension=6, dimension_dir="width"
145)
146rear_left_top_camera = RearLeftTopCamera(pan_target=(1.5, 0, 0), dimension=6, dimension_dir="width")
147
148cameras_geo = [
149    top_camera,
150    side_camera,
151    rear_camera,
152    bottom_camera,
153    front_left_bottom_camera,
154    rear_right_bottom_camera,
155]
156
157
158limits_cp = [(-1, 1), (-1, 1), (-1, 1), (-0.3, 0), (-0.3, 0), (-1, 1), (-1, 1), (-1, 1)]
159cameras_cp = [
160    front_camera,
161    front_left_top_camera,
162    side_camera,
163    rear_left_top_camera,
164    rear_camera,
165    bottom_camera,
166    front_left_bottom_camera,
167    rear_right_bottom_camera,
168]
169
170
171avg = Average(fraction=0.1)
172CD = DataItem(data="surface_forces/totalCD", exclude=exclude, title="CD", operations=avg)
173
174CL = DataItem(data="surface_forces/totalCL", exclude=exclude, title="CL", operations=avg)
175
176CDA = DataItem(
177    data="surface_forces",
178    exclude=exclude,
179    title="CD*area",
180    variables=[Variable(name="area", data="params.reference_geometry.area")],
181    operations=[Expression(expr="totalCD * area"), avg],
182)
183
184CLf = DataItem(
185    data="surface_forces",
186    exclude=exclude,
187    title="CLf",
188    operations=[Expression(expr="1/2*totalCL + totalCMy"), avg],
189)
190
191CLr = DataItem(
192    data="surface_forces",
193    exclude=exclude,
194    title="CLr",
195    operations=[Expression(expr="1/2*totalCL - totalCMy"), avg],
196)
197
198CFy = DataItem(data="surface_forces/totalCFy", exclude=exclude, title="CS", operations=avg)
199
200statistical_data = [
201    "params/reference_geometry/area",
202    CD,
203    CDA,
204    Delta(data=CD),
205    CL,
206    CLf,
207    CLr,
208    CFy,
209    "volume_mesh/stats/n_nodes",
210    "params/time_stepping/max_steps",
211]
212statistical_table = Table(
213    data=statistical_data,
214    section_title="Statistical data",
215    formatter=[
216        (
217            None
218            if d
219            in [
220                "params/reference_geometry/area",
221                "volume_mesh/stats/n_nodes",
222                "params/time_stepping/max_steps",
223            ]
224            else ".4f"
225        )
226        for d in statistical_data
227    ],
228)
229
230
231geometry_screenshots = [
232    Chart3D(
233        section_title="Geometry",
234        items_in_row=2,
235        force_new_page=True,
236        show="boundaries",
237        camera=camera,
238        exclude=exclude,
239        fig_name=f"geo_{i}",
240    )
241    for i, camera in enumerate(cameras_geo)
242]
243
244cpt_screenshots = [
245    Chart3D(
246        section_title="Isosurface, Cpt=-1",
247        items_in_row=2,
248        force_new_page=True,
249        show="isosurface",
250        iso_field="Cpt",
251        exclude=exclude,
252        camera=camera,
253    )
254    for camera in cameras_cp
255]
256
257cfvec_screenshots = [
258    Chart3D(
259        section_title="CfVec",
260        items_in_row=2,
261        force_new_page=True,
262        show="boundaries",
263        field="CfVec",
264        mode="lic",
265        limits=(1e-4, 10),
266        is_log_scale=True,
267        exclude=exclude,
268        camera=camera,
269    )
270    for camera in cameras_cp
271]
272
273y_slices_lic_screenshots = [
274    Chart3D(
275        section_title=f"Slice velocity LIC y={y}",
276        items_in_row=2,
277        force_new_page=True,
278        show="slices",
279        include=[f"slice_y_{name}"],
280        field="velocityVec",
281        mode="lic",
282        limits=(0 * u.m / u.s, 50 * u.m / u.s),
283        camera=side_camera_slice,
284        fig_name=f"slice_y_vec_{name}",
285    )
286    for name, y in zip(["0", "0_2", "0_4", "0_6", "0_8"], [0, 0.2, 0.4, 0.6, 0.8])
287]
288
289y_slices_screenshots = [
290    Chart3D(
291        section_title=f"Slice velocity y={y}",
292        items_in_row=2,
293        force_new_page=True,
294        show="slices",
295        include=[f"slice_y_{name}"],
296        field="velocity",
297        limits=(0 * u.m / u.s, 50 * u.m / u.s),
298        camera=side_camera_slice,
299        fig_name=f"slice_y_{name}",
300    )
301    for name, y in zip(["0", "0_2", "0_4", "0_6", "0_8"], [0, 0.2, 0.4, 0.6, 0.8])
302]
303
304
305y_slices_lic_screenshots = [
306    Chart3D(
307        section_title=f"Slice velocity LIC y={y}",
308        items_in_row=2,
309        force_new_page=True,
310        show="slices",
311        include=[f"slice_y_{name}"],
312        field="velocityVec",
313        mode="lic",
314        limits=(0 * u.m / u.s, 50 * u.m / u.s),
315        camera=side_camera_slice,
316        fig_name=f"slice_y_vec_{name}",
317    )
318    for name, y in zip(["0", "0_2", "0_4", "0_6", "0_8"], [0, 0.2, 0.4, 0.6, 0.8])
319]
320
321z_slices_screenshots = [
322    Chart3D(
323        section_title=f"Slice velocity z={z}",
324        items_in_row=2,
325        force_new_page=True,
326        show="slices",
327        include=[f"slice_z_{name}"],
328        field="velocity",
329        limits=(0 * u.m / u.s, 50 * u.m / u.s),
330        camera=top_camera_slice,
331        fig_name=f"slice_z_{name}",
332    )
333    for name, z in zip(["neg0_2", "0", "0_2", "0_4", "0_6", "0_8"], [-0.2, 0, 0.2, 0.4, 0.6, 0.8])
334]
335
336y_plus_screenshots = [
337    Chart3D(
338        section_title="y+",
339        items_in_row=2,
340        show="boundaries",
341        field="yPlus",
342        exclude=exclude,
343        limits=(0, 5),
344        camera=camera,
345        fig_name=f"yplus_{i}",
346    )
347    for i, camera in enumerate([top_camera, bottom_camera])
348]
349cp_screenshots = [
350    Chart3D(
351        section_title="Cp",
352        items_in_row=2,
353        show="boundaries",
354        field="Cp",
355        exclude=exclude,
356        limits=limits,
357        camera=camera,
358        fig_name=f"cp_{i}",
359    )
360    for i, (limits, camera) in enumerate(zip(limits_cp, cameras_cp))
361]
362wall_shear_screenshots = [
363    Chart3D(
364        section_title="Wall shear stress magnitude",
365        items_in_row=2,
366        show="boundaries",
367        field="wallShearMag",
368        exclude=exclude,
369        limits=(0 * u.Pa, 5 * u.Pa),
370        camera=camera,
371        fig_name=f"wallShearMag_{i}",
372    )
373    for i, camera in enumerate(cameras_cp)
374]
375
376report = ReportTemplate(
377    title="Aerodynamic analysis of DrivAer",
378    items=[
379        Summary(),
380        Inputs(),
381        statistical_table,
382        Chart2D(
383            x="x_slicing_force_distribution/X",
384            y="x_slicing_force_distribution/totalCumulative_CD_Curve",
385            fig_name="totalCumulative_CD_Curve",
386            background="geometry",
387            exclude=exclude,
388        ),
389        Chart2D(
390            x="surface_forces/pseudo_step",
391            y="surface_forces/totalCD",
392            section_title="Drag Coefficient",
393            fig_name="cd_fig",
394            exclude=exclude,
395            focus_x=(1 / 3, 1),
396        ),
397        *geometry_screenshots,
398        *cpt_screenshots,
399        *y_slices_screenshots,
400        *y_slices_lic_screenshots,
401        *z_slices_screenshots,
402        *y_plus_screenshots,
403        *cp_screenshots,
404        *wall_shear_screenshots,
405    ],
406    settings=Settings(dpi=150),
407)
408
409report = report.create_in_cloud(
410    f"{size}-{len(cases)}cases-slices-using-groups-Cpt, Cpx, wallShear, dpi=default",
411    cases,
412    solver_version=SOLVER_VERSION,
413)
414
415report.wait()
416report.download("report.pdf")

Notes#

  • Demonstrates the use of the report module, including ReportTemplate and various report items (e.g., Chart3D, Table, DataItem), to automatically generate a detailed PDF report from simulation data.

  • Illustrates a workflow for conducting a parametric study by programmatically submitting multiple simulation cases with varying operating conditions (yaw angle beta) using the Project interface.

  • Shows the configuration of diverse output types (SurfaceOutput, SliceOutput, IsosurfaceOutput) to capture data for detailed flow analysis and visualization within the report.