Modeling External Compressible Flow#

The purpose of this tutorial is to compute the turbulent flow past a transonic wing at a nonzero angle of attack using the k-w SST turbulence model.

This example uses the guided workflow for watertight geometry meshing because it is appropriate for geometries that can have no imperfections, such as gaps and leakages.

Workflow tasks

The Modeling External Compressible Flow Using the Meshing Workflow guides you through these tasks:

  • Creation of capsule mesh using Watertight Geometry workflow.

  • Model compressible flow (using the ideal gas law for density).

  • Set boundary conditions for external aerodynamics.

  • Use the k-w SST turbulence model.

  • Calculate a solution using the pressure-based coupled solver with global time step selected for the pseudo time method.

  • Check the near-wall mesh resolution by plotting the distribution of .

Problem description

The problem considers the flow around a wing at an angle of attack a=3.06° and a free stream Mach number of 0.8395 (M=0.8395). The flow is transonic, and has a shock near the mid-chord (x/c≃0.20) on the upper (suction) side. The wing has a mean aerodynamic chord length of 0.64607 m, a span of 1.1963 m, an aspect ratio of 3.8, and a taper ratio of 0.562.

../../_images/external_compressible_flow_011.png

Example Setup#

Before you can use the meshing workflow, you must set up the example and initialize this workflow.

Perform required imports#

Perform required imports, which includes downloading and importing the geometry files.

import ansys.fluent.core as pyfluent
from ansys.fluent.core import examples

wing_spaceclaim_file, wing_intermediary_file = [
    examples.download_file(CAD_file, "pyfluent/external_compressible")
    for CAD_file in ["wing.scdoc", "wing.pmdb"]
]

Launch Fluent#

Launch Fluent as a service in meshing mode with double precision running on four processors and print Fluent version.

meshing = pyfluent.launch_fluent(
    precision="double",
    processor_count=4,
    mode="meshing",
)
print(meshing.get_fluent_version())
Fluent version 2025 R1

Initialize workflow#

Initialize the watertight geometry meshing workflow.

meshing.workflow.InitializeWorkflow(WorkflowType="Watertight Geometry")

Watertight geometry meshing workflow#

The fault-tolerant meshing workflow guides you through the several tasks that follow.

Import CAD and set length units#

Import the CAD geometry and set the length units to inches.

geo_import = meshing.workflow.TaskObject["Import Geometry"]
geo_import.Arguments.set_state(
    {
        "FileName": wing_intermediary_file,
    }
)

meshing.upload(wing_intermediary_file)
geo_import.Execute()
/home/ansys/actions-runner/_work/pyfluent/pyfluent/src/ansys/fluent/core/session.py:360: PyFluentUserWarning: You have directly called the upload() method of the session.         Please be advised that for the current version of Fluent, many API methods         automatically handle file uploads and downloads internally. You may not         need to explicitly call upload() in most cases.         However, there are exceptions, particularly in PMFileManagement, where complex         file interactions require explicit use of upload()  method         for relevant files.
  warnings.warn(self._file_transfer_api_warning("upload()"), PyFluentUserWarning)

Warning: PMDB file will not be saved as the same is being imported.

Importing one geom object per program-controlled and one zone per body ...
writing cad import logs to "/mnt/pyfluent/cadImport1732718345.608516.log"

Reading "/mnt/pyfluent/FM_2c4a6ca1e415_152/out1732718345.604977152.tgf"...

nodes: 1603
edges: 267
faces: 2649
cells: 0
generating pointers...done.
extracting boundary entities...
 1603 boundary nodes.
 2649 boundary faces.
 6 boundary face zones.
done.
analyzing boundary connectivity...done.


---------------- Import of wing, consisting of 2 parts/objects, complete.


---------------- Import complete in  0.07 minutes.

Add local sizing#

Add local sizing controls to the faceted geometry.

local_sizing = meshing.workflow.TaskObject["Add Local Sizing"]
local_sizing.Arguments.set_state(
    {
        "AddChild": "yes",
        "BOIControlName": "wing-facesize",
        "BOIFaceLabelList": ["wing_bottom", "wing_top"],
        "BOISize": 10,
    }
)

local_sizing.AddChildAndUpdate()

local_sizing.Arguments.set_state(
    {
        "AddChild": "yes",
        "BOIControlName": "wing-ege-facesize",
        "BOIFaceLabelList": ["wing_edge"],
        "BOISize": 2,
    }
)

local_sizing.AddChildAndUpdate()

local_sizing.Arguments.set_state(
    {
        "AddChild": "yes",
        "BOIControlName": "boi_1",
        "BOIExecution": "Body Of Influence",
        "BOIFaceLabelList": ["wing-boi"],
        "BOISize": 5,
    }
)

local_sizing.AddChildAndUpdate()
---------------- A local size of 10 was added to wing-facesize

---------------- The Global Min size was adjusted to 2

---------------- A local size of 2 was added to wing-ege-facesize

---------------- A Body of Influence with a target size of 5 was added to: wing-boi

Generate surface mesh#

Generate the surface mash.

surface_mesh_gen = meshing.workflow.TaskObject["Generate the Surface Mesh"]
surface_mesh_gen.Arguments.set_state(
    {"CFDSurfaceMeshControls": {"MaxSize": 1000, "MinSize": 2}}
)

surface_mesh_gen.Execute()
Writing "/mnt/pyfluent/FM_2c4a6ca1e415_152/TaskObject3.msh.h5" ...
writing 1 node zones
writing 10 edge zones
writing 6 face zones
writing node curvature data
done.    processing size functions/scoped sizing to create Size Field...

Writing "/mnt/pyfluent/FM_2c4a6ca1e415_152/wing.sf"...

Done.


Importing one mesh object per program-controlled and one zone per body ...
writing cad import logs to "/mnt/pyfluent/cadImport1732718353.956592.log"

Reading "/mnt/pyfluent/FM_2c4a6ca1e415_152/out1732718353.95456152.tgf"...

nodes: 77317
edges: 4045
faces: 146550
cells: 0
appending mesh...
done.
generating pointers...done.
extracting boundary entities...
 78920 boundary nodes.
 149199 boundary faces.
 12 boundary face zones.
done.

Reading "/mnt/pyfluent/FM_2c4a6ca1e415_152//wing.sf"...

 Read 379882 vertices
Warning: Deleting empty objects (wing-boi)

removed 54012 faces.
0 faces marked.

    computing regions...done
Deleting import_curvature_0
Deleting import_proximity_0
    processing size functions/scoped sizing to create Size Field...

Writing "/mnt/pyfluent/FM_2c4a6ca1e415_152/wing.sf"...

Done.


remeshing...


------------------------- --------------------- -------------------- ---------------- ----------
                     name skewed-cells (> 0.80)    averaged-skewness maximum-skewness face count
------------------------- --------------------- -------------------- ---------------- ----------

                    fluid                     0           0.02760156       0.65385375      79804
------------------------- --------------------- -------------------- ---------------- ----------
                     name skewed-cells (> 0.80)    averaged-skewness maximum-skewness face count
------------------------- --------------------- -------------------- ---------------- ----------

               wing-fluid                     0           0.02760156       0.65385375      79804

---------------- After Surface mesh, the model consists of 1 fluid/solid regions and 0 voids.

---------------- Surface Meshing of wing complete in  0.67 minutes, with a maximum skewness of  0.65.

Describe geometry#

Describe geometry and define the fluid region.

describe_geo = meshing.workflow.TaskObject["Describe Geometry"]
describe_geo.UpdateChildTasks(SetupTypeChanged=False)

describe_geo.Arguments.set_state(
    {"SetupType": "The geometry consists of only fluid regions with no voids"}
)

describe_geo.UpdateChildTasks(SetupTypeChanged=True)

describe_geo.Execute()
---------------- Symmetry zone type was automatically assigned to zones containing the string '*symmetry*'.

---------------- Pressure-far-field zone type was automatically assigned to zones containing the string '*far*field*'.

---------------- Describe Geometry task complete in  0.01 minutes.

Update boundaries#

Update the boundaries.

meshing.workflow.TaskObject["Update Boundaries"].Execute()
---------------- Boundary Conditions Updated

Update regions#

Update the regions.

meshing.workflow.TaskObject["Update Regions"].Execute()
---------------- Regions Updated

Add boundary layers#

Add boundary layers, which consist of setting properties for the boundary layer mesh.

add_boundary_layer = meshing.workflow.TaskObject["Add Boundary Layers"]
add_boundary_layer.Arguments.set_state({"NumberOfLayers": 12})

add_boundary_layer.AddChildAndUpdate()
Created Scoped Prism: smooth-transition_1

---------------- Inflation control added to wing-fluid

Generate volume mesh#

Generate the volume mesh, which consists of setting properties for the volume mesh.

volume_mesh_gen = meshing.workflow.TaskObject["Generate the Volume Mesh"]
volume_mesh_gen.Arguments.set_state(
    {
        "VolumeFill": "poly-hexcore",
        "VolumeFillControls": {"HexMaxCellLength": 512},
        "VolumeMeshPreferences": {
            "CheckSelfProximity": "yes",
            "ShowVolumeMeshPreferences": True,
        },
    }
)

volume_mesh_gen.Execute()
Writing "/mnt/pyfluent/FM_2c4a6ca1e415_152/TaskObject11.msh.h5" ...
writing 3 node zones
writing 23 edge zones
writing 11 face zones
writing node curvature data
done.    processing size functions/scoped sizing to create Size Field...

Writing "/mnt/pyfluent/FM_2c4a6ca1e415_152/wing.sf"...

Done.


    updating regions...
    done.
checking object "wing-fluid"...
    skipping validating regions of mesh object "wing-fluid"...done.
auto meshing object wing-fluid...

processing scoped prisms...
    starting orientation...
done.
    setting prism growth...done.
done.
Identifying Topology...

The octree hexcore cells will be refined using the computed Size Field.
Generating Prisms...

Generating initial mesh...

Refining mesh...

Create polyhedra ...

delete virtual and dead zones.

Merging zones...

Cleaning up dead entities...


Merging Domains...
done.

                     name       id cells (quality < 0.05)  minimum quality cell count
------------------------- -------- ---------------------- ---------------- ----------
                    fluid      670                      0      0.060909445    1009880

                     name       id cells (quality < 0.05)  minimum quality cell count
------------------------- -------- ---------------------- ---------------- ----------
          Overall Summary     none                      0      0.060909445    1009880

Total Number of Cell Zones : 1

[Quality Measure : Orthogonal Quality]

---------------- Volume mesh creation completed in :  0.72 minutes

---------------- 1009880 cells were created in :  0.74 minutes

---------------- The mesh has a minimum Orthogonal Quality of:  0.06

---------------- The volume meshing of wing-fluid is complete.

Check mesh in meshing mode#

Check the mesh in meshing mode.

meshing.tui.mesh.check_mesh()
Domain extents.
  x-coordinate: min = -2.503520e+04, max = 2.614408e+04.
  y-coordinate: min = -9.623149e-08, max = 2.503895e+04.
  z-coordinate: min = -2.503942e+04, max = 2.503943e+04.
Volume statistics.
  minimum volume: 6.592227e-04.
  maximum volume: 6.084455e+08.
    total volume: 4.214431e+13.
Face area statistics.
   minimum face area: 3.731184e-06.
   maximum face area: 1.203898e+06.
   average face area: 6.134290e+04.
Checking number of nodes per edge.
Checking number of nodes per face.
Checking number of nodes per cell.
Checking number of faces/neighbors per cell.
Checking cell faces/neighbors.
Checking isolated cells.
Checking face handedness.
Checking periodic face pairs.
Checking face children.
Checking face zone boundary conditions.
Checking for invalid node coordinates.
Checking poly cells.
Checking zones.
Checking neighborhood.
Checking modified centroid.
Checking non-positive or too small area.
Checking face zones thread type.

Save mesh file#

Save the mesh file (wing.msh.h5).

meshing.meshing.File.WriteMesh(FileName="wing.msh.h5")
Done.Writing "wing.msh.h5" ...
writing 3 node zones
writing 13 edge zones
writing 6 face zones
writing 1 cell zones
writing boundary layer flags
writing node curvature data
done.Copying the required intermediate mesh files into wing_workflow_files
Done.

Solve and postprocess#

Once you have completed the watertight geometry meshing workflow, you can solve and postprcess the results.

Switch to solution mode#

Switch to solution mode. Now that a high-quality mesh has been generated using Fluent in meshing mode, you can switch to solver mode to complete the setup of the simulation.

solver = meshing.switch_to_solver()
Preparing...

Check mesh in solver mode#

Check the mesh in solver mode. The mesh check lists the minimum and maximum x, y, and z values from the mesh in the default SI units of meters. It also reports a number of other mesh features that are checked. Any errors in the mesh are reported.

solver.mesh.check()
unused zone boundary-node-21 removed

Transferring mesh
        creating threads... done
        transferring nodes... done
        transferring cells... done
        transferring faces... done
        post mesh transfer operations... done
done

Building...
     mesh
        auto partitioning mesh by Metis (fast),
        distributing mesh
                parts....,
                faces....,
                nodes....,
                cells....,
        bandwidth reduction using Reverse Cuthill-McKee: 249313/10326 = 24.1442
     materials,
     interface,
     domains,
     zones,
        interior--fluid
        fluid_symmetry
        pressure_farfield
        wing_edge
        wing_bottom
        wing_top
        fluid
     surfaces,
     parallel,
Done.
Mesh is now scaled to meters.
Warning: Pressure far-field boundary condition can only be used with ideal gas law.
  Please change either the boundary condition type or enable ideal gas law.
  Solver cannot proceed until this is fixed.
        Warning: Pressure far-field boundary condition can only be used with ideal gas law.
  Please change either the boundary condition type or enable ideal gas law.
  Solver cannot proceed until this is fixed.

 Domain Extents:
   x-coordinate: min (m) = -2.502407e+01, max (m) = 2.614408e+01
   y-coordinate: min (m) = -9.623149e-11, max (m) = 2.503569e+01
   z-coordinate: min (m) = -2.503942e+01, max (m) = 2.503943e+01
 Volume statistics:
   minimum volume (m3): 6.592227e-13
   maximum volume (m3): 6.084455e-01
     total volume (m3): 4.214431e+04
 Face area statistics:
   minimum face area (m2): 3.731184e-12
   maximum face area (m2): 1.203898e+00
 Checking mesh.....................................
Done.

Note: Settings to improve the robustness of pathline and
      particle tracking have been automatically enabled.

Define model#

Set the k-w sst turbulence model.

# model : k-omega
# k-omega model : sst

viscous = solver.setup.models.viscous

viscous.model = "k-omega"
viscous.k_omega_model = "sst"

Define materials#

Modify the default material air to account for compressibility and variations of the thermophysical properties with temperature.

# density : ideal-gas
# viscosity : sutherland
# viscosity method : three-coefficient-method
# reference viscosity : 1.716e-05 [kg/(m s)]
# reference temperature : 273.11 [K]
# effective temperature : 110.56 [K]

air = solver.setup.materials.fluid["air"]

air.density.option = "ideal-gas"

air.viscosity.option = "sutherland"

air.viscosity.sutherland.option = "three-coefficient-method"

air.viscosity.sutherland.reference_viscosity = 1.716e-05

air.viscosity.sutherland.reference_temperature = 273.11

air.viscosity.sutherland.effective_temperature = 110.56
Note: Enabling energy equation as required by material density method.

Boundary Conditions#

Set the boundary conditions for pressure_farfield.

# gauge pressure : 0 [Pa]
# mach number : 0.8395
# temperature : 255.56 [K]
# x-component of flow direction : 0.998574
# z-component of flow direction : 0.053382
# turbulent intensity : 5 [%]
# turbulent viscosity ratio : 10

pressure_farfield = solver.setup.boundary_conditions.pressure_far_field[
    "pressure_farfield"
]

pressure_farfield.momentum.gauge_pressure = 0

pressure_farfield.momentum.mach_number = 0.8395

pressure_farfield.thermal.temperature = 255.56

pressure_farfield.momentum.flow_direction[0] = 0.998574

pressure_farfield.momentum.flow_direction[2] = 0.053382

pressure_farfield.turbulence.turbulent_intensity = 0.05

pressure_farfield.turbulence.turbulent_viscosity_ratio = 10

Operating Conditions#

Set the operating conditions.

# operating pressure : 80600 [Pa]

solver.setup.general.operating_conditions.operating_pressure = 80600

Initialize flow field#

Initialize the flow field using hybrid initialization.

solver.solution.initialization.hybrid_initialize()
Initialize using the hybrid initialization method.

Checking case topology...
-This case has a single farfield bc
-Case will be initialized with constant pressure

        iter            scalar-0

        1               1.000000e+00
        2               3.386578e-04
        3               6.850164e-05
        4               1.218787e-05
        5               3.071446e-06
        6               5.898514e-07
        7               1.483115e-07
        8               2.794801e-08
        9               6.803494e-09
        10              1.664232e-09

Hybrid initialization is done.

Save case file#

Save the case file external_compressible1.cas.h5.

solver.file.write(file_name="external_compressible.cas.h5", file_type="case")
Fast-loading "/ansys_inc/v251/fluent/fluent25.1.0/addons/afd/lib/hdfio.bin"
Done.

Writing to 2c4a6ca1e415:"/mnt/pyfluent/external_compressible.cas.h5" in NODE0 mode and compression level 1 ...
Grouping cells for Laplace smoothing ...
     1009880 cells,     1 zone  ...
     3995325 faces,     6 zones ...
     2083869 nodes,     1 zone  ...
  Done.

  Writing boundary layer flags ...
  Done.
Done.

Solve for 25 iterations#

Solve for 25 iterations (100 iterations is recommended, however for this example 25 is sufficient).

solver.solution.run_calculation.iterate(iter_count=25)
/home/ansys/actions-runner/_work/pyfluent/pyfluent/src/ansys/fluent/core/session_solver.py:309: DeprecatedSettingWarning: 'solution' is deprecated. Use 'settings.solution' instead.
  warnings.warn(

  iter  continuity  x-velocity  y-velocity  z-velocity      energy           k       omega     time/iter
     1  1.0000e+00  4.0335e-07  3.8304e-07  8.1691e-07  9.2470e-07  9.9600e-01  4.9891e-01  0:04:32   24
     2  1.0000e+00  3.6220e-07  3.4687e-07  6.1131e-07  8.7077e-07  9.9341e-01  4.9823e-01  0:04:14   23
     3  1.0000e+00  3.4447e-07  3.6960e-07  5.3973e-07  7.8462e-07  9.8817e-01  4.9711e-01  0:03:59   22
     4  1.0000e+00  3.2099e-07  3.9396e-07  5.1410e-07  7.0951e-07  9.7278e-01  4.9524e-01  0:03:41   21
     5  1.0000e+00  3.0103e-07  4.0980e-07  5.0372e-07  6.4156e-07  9.3756e-01  4.9201e-01  0:03:24   20
     6  1.0300e+00  2.8211e-07  4.1698e-07  4.9810e-07  5.8548e-07  8.4358e-01  4.8629e-01  0:03:09   19
     7  1.0394e+00  2.6739e-07  4.1449e-07  4.9097e-07  5.3676e-07  5.7303e-01  4.7582e-01  0:02:55   18
     8  1.0287e+00  2.5715e-07  4.0228e-07  4.7789e-07  4.9543e-07  2.9234e-01  4.5621e-01  0:02:43   17
     9  1.0054e+00  2.5130e-07  3.8204e-07  4.5921e-07  4.6678e-07  1.3994e-01  4.1971e-01  0:02:31   16
    10  9.7975e-01  2.4922e-07  3.5754e-07  4.3792e-07  4.5132e-07  6.6768e-02  3.5659e-01  0:02:21   15
    11  9.4738e-01  2.4727e-07  3.3153e-07  4.1589e-07  4.4118e-07  3.3488e-02  2.6515e-01  0:02:11   14

  iter  continuity  x-velocity  y-velocity  z-velocity      energy           k       omega     time/iter
    12  9.1232e-01  2.4502e-07  3.0477e-07  3.9250e-07  4.3095e-07  1.8692e-02  1.6522e-01  0:02:02   13
    13  8.7797e-01  2.4520e-07  2.7883e-07  3.6764e-07  4.1918e-07  1.2388e-02  8.7038e-02  0:01:53   12
    14  8.4088e-01  2.4418e-07  2.5205e-07  3.4001e-07  3.9899e-07  9.4535e-03  4.1059e-02  0:01:41   11
    15  8.0849e-01  2.4216e-07  2.2390e-07  3.0915e-07  3.8400e-07  7.5559e-03  1.8468e-02  0:01:30   10
    16  7.7903e-01  2.4321e-07  1.9900e-07  2.8224e-07  3.7615e-07  6.1619e-03  8.2983e-03  0:01:19    9
    17  7.5550e-01  2.4337e-07  1.7697e-07  2.5946e-07  3.6938e-07  5.1356e-03  3.8478e-03  0:01:10    8
    18  7.3927e-01  2.4495e-07  1.5946e-07  2.4136e-07  3.7141e-07  4.3687e-03  1.8890e-03  0:01:02    7
    19  7.3206e-01  2.4647e-07  1.4752e-07  2.2830e-07  3.7416e-07  3.7692e-03  1.0003e-03  0:00:53    6
    20  7.2046e-01  2.4232e-07  1.3824e-07  2.1719e-07  3.7258e-07  3.2978e-03  5.7585e-04  0:00:44    5
    21  7.0008e-01  2.3746e-07  1.3005e-07  2.0521e-07  3.5842e-07  2.9065e-03  3.5926e-04  0:00:36    4
    22  6.7301e-01  2.2634e-07  1.2162e-07  1.9204e-07  3.4148e-07  2.5834e-03  2.4031e-04  0:00:27    3

  iter  continuity  x-velocity  y-velocity  z-velocity      energy           k       omega     time/iter
    23  6.3985e-01  2.1337e-07  1.1347e-07  1.7679e-07  3.2566e-07  2.2946e-03  1.7007e-04  0:00:18    2
    24  5.9642e-01  2.0386e-07  1.0741e-07  1.6071e-07  3.1560e-07  2.0317e-03  1.2572e-04  0:00:09    1
    25  5.5051e-01  1.9653e-07  1.0128e-07  1.4510e-07  3.0266e-07  1.7956e-03  9.6212e-05  0:00:00    0

Write final case file and data#

Write the final case file and the data.

solver.file.write(file_name="external_compressible1.cas.h5", file_type="case")
/home/ansys/actions-runner/_work/pyfluent/pyfluent/src/ansys/fluent/core/session_solver.py:309: DeprecatedSettingWarning: 'file' is deprecated. Use 'settings.file' instead.
  warnings.warn(

Writing to 2c4a6ca1e415:"/mnt/pyfluent/external_compressible1.cas.h5" in NODE0 mode and compression level 1 ...
Grouping cells for Laplace smoothing ...
     1009880 cells,     1 zone  ...
     3995325 faces,     6 zones ...
     2083869 nodes,     1 zone  ...
  Done.

  Writing boundary layer flags ...
  Done.
Done.

Close Fluent#

Close Fluent.

solver.exit()

Total running time of the script: (7 minutes 11.863 seconds)

Gallery generated by Sphinx-Gallery