Automated Meshing#
Overview#
Flow360 offers automated meshing, from a CAD geometry to a surface mesh and finally to a volume mesh. The supported CAD formats are CSM and EGADS. The output volume meshes are in CGNS format.
Geometry#
The Engineering Sketch Pad (ESP) is a solid-modeling, feature-based, web-enabled system for building parametric geometry. Readers can download the pre-built ESP so there is no need to compile the source code. The CSM file contains all operations for constructing the geometry. The tutorials for preparing the CSM files can be found on ESP’s official website. Here is an example CSM file:
sphere 0 -1 0 0.5
attribute groupName $sphere
box -0.5 0.5 -0.5 1 1 1
attribute groupName $box
select face 6
attribute faceName $top
This example contains two solid bodies: a sphere and a box. The sphere is centered at (0,-1,0) and the radius is 0.5. The corner of the box is located at (-0.5,0.5,0.5) and the dimensions of the box along the x,y,z directions are [1,1,1].
Note
For external flow, multiple solid bodies are allowed. For internal flow, only one solid body representing the fluid/air is allowed.
As shown in Fig. 8, faces with different groupName
will be “grouped” as different boundary conditions when exporting the CGNS file.
The face with faceName
will get extra refinement when generating the surface mesh.
Surface Meshing#
The surface mesher takes the geometry file and a surfaceMesh.json as inputs to generate a surface mesh. The surfaceMesh.json controls the surface mesh resolution. Full description of surfaceMesh.json can be found at JSON surface mesher. Here is an example surfaceMesh.json:
{
"maxEdgeLength": 0.05,
"curvatureResolutionAngle": 15,
"growthRate": 1.2,
"faces": {
"top": {
"maxEdgeLength": 0.01
}
}
}
As shown in the above JSON file, the baseline maxEdgeLength
is 0.05.
An extra refinement is applied to the top
face of the box with maxEdgeLength
= 0.01.
The surface mesh can be created by submitting the geometry file and surfaceMesh.json via the PythonAPI:
import flow360client
surfaceMeshId = flow360client.NewSurfaceMeshFromGeometry("path/to/geometry.csm", "surfaceMesh.json", surfaceMeshName="my_surface_mesh")
- Inputs
geometry.csm file
surfaceMesh.json (or a Python dictionary)
- Outputs
surface mesh on cluster
return the
surfaceMeshId
Volume Meshing#
The volume mesher takes a surfaceMeshId
and a volumeMesh.json to generate a volume mesh. The volumeMesh.json determines the volume mesh resolution.
It contains the firstLayerThickness
and growthRate
of 3D anisotropic layers, the size/location/resolution of refinement
zones and etc.
Full description of volumeMesh.json can be found at JSON volume mesher. Below is an example volumeMesh.json:
{
"volume": {
"firstLayerThickness": 1e-3,
"growthRate": 1.2
},
"refinement": [
{
"size": [6, 4, 2],
"center": [1, 0, -1],
"axisOfRotation": [0, 1, 0],
"angleOfRotation": 45,
"spacing": 0.1
}
]
}
In the above JSON file, there is a 6x4x2 volume mesh refinement zone centered at (1,0,-1). This refinement box will be rotated by 45 degrees around the y-axis. The mesh size within this refinement zone is limited to 0.1.
Note
By default, all faces from ESP are treated as
noSlipWall
and prism layers will be grown off these faces. For internal flow, the user can specify which faces are notnoSlipWall
and turn off the prism layers.By default, The farfield shape will be automatically determined by the volume mesher. The user can prescribe the farfield shape if needed.
auto
: The mesher will Sphere or semi-sphere will be generated based on the bounding box of the geometryFull sphere if min{Y} < 0 and max{Y} > 0
+Y semi sphere if min{Y} = 0 and max{Y} > 0
-Y semi sphere if min{Y} <> 0 and max{Y} = 0
quasi-3d
: Thin disk will be generated for quasi 3D cases. Both sides of the farfield disk will be treated as “symmetric plane”.user-defined
: The farfield shape is provided by the user in ESP
The volume mesh can be created by submitting the surfaceMeshId
and volumeMesh.json using the PythonAPI:
volumeMeshId = flow360client.NewMeshFromSurface(surfaceMeshId, "volumeMesh.json", meshName="my_volume_mesh")
- Inputs
surfaceMeshId
volumeMesh.json (or a Python dictionary)
- Outputs
volume mesh on cluster (in CGNS format)
Flow360Mesh.json on cluster
return the
volumeMeshId
JSON surface mesher#
Option |
Default |
Description |
---|---|---|
|
REQUIRED |
[float] Global maximum edge length for surface cells. This value will be overwritten by the local |
|
REQUIRED |
[float] Global maximum angular deviation in degrees. This value will restrict:
(1) The angle between a cell’s normal and its underlying surface normal
(2) The angle between a line segment’s normal and its underlying curve normal
|
|
REQUIRED |
[float] Growth rate of the anisotropic layers grown from the edges |
|
{} |
|
|
{} |
edges
#
Subsection edges
is a dictionary, the keys in this dictionary should correspond to the edgeName
attribute in the CSM file.
If users want to apply special treatment to a particular edge, they need to add the edgeName
attribute for that edge in ESP. They can then specify the desired type of treatment in this subsection.
Option |
Default |
Description |
---|---|---|
|
REQUIRED |
|
|
REQUIRED for |
[string]
angle , height or aspectRatio (1)
angle : curvature resolution in degrees(2)
height : first layer height of the anisotropic layers(3)
aspectRatio : maximum aspect ratio of the anisotropic cells |
|
REQUIRED for |
[float] Input value for the corresponding |
Example:
"edges": {
"leadingEdge": {
"type": "aniso",
"method": "angle",
"value": 1
},
"trailingEdge": {
"type": "aniso",
"method": "height",
"value": 1e-3
},
"hubCircle": {
"type": "aniso",
"method": "height",
"value": 0.1
},
"hubSplitEdge": {
"type": "projectAnisoSpacing"
}
}
faces
#
Subsection faces
is a dictionary where the keys should match the faceName
attribute in the CSM file.
By default, the global maxEdgeLength
will be applied to all faces.
If users want extra refinement on a specific face, they need to add the faceName
attribute to that face in ESP and specify the target local maxEdgeLength
in this subsection.
Option |
Default |
Description |
---|---|---|
maxEdgeLength |
REQUIRED |
[float] Local maximum edge length. This value overwrites the global |
Example:
"faces": {
"rightWing": {
"maxEdgeLength": 0.05
},
"fuselage": {
"maxEdgeLength": 0.05
}
}
JSON volume mesher#
Option |
Default |
Description |
---|---|---|
|
1 |
[float] If refinementFactor=r is provided all spacings in refinement regions and first layer thickness will be adjusted to generate r-times finer mesh. For example, if refinementFactor=2, all spacings will be divided by \(2^{1/3}\approx 1.26\) , so the resulting mesh will have approximately 2 times more nodes |
|
|
[string]
auto , quasi-3d or user-defined (1)
auto automatically detect if the geometry is a full/+Y half/-Y half airplane and create the farfield accordingly(2)
quasi-3d creates two slip walls at the min/max Y locations(3)
user-defined farfield geometry provided by the user in ESP |
|
REQUIRED |
|
|
{} |
|
|
[] |
[list(dict)] Description of refinement zones |
|
[] |
[list(dict)] Description of rotor disks |
|
[] |
[list(dict)] Description of sliding interfaces |
volume
#
Option |
Default |
Description |
---|---|---|
|
REQUIRED |
[float] First layer thickness for volumetric anisotropic layers |
|
REQUIRED |
[float] Growth rate for volume prism layers |
|
0 |
[float] Narrow gap treatment strength used when two surfaces are in close proximity. Use a value between 0 and 1, where 0 is no treatment and 1 is the most conservative treatment. This parameter has a global impact where the anisotropic transition into the isotropic mesh. However, the impact on regions without close proximity is negligible. See the examples below. |
Gap treatment examples:
faces
#
Subsection faces
is a dictionary where the keys should align with the faceName
attribute in the CSM file.
By default, all faces are treated as aniso
and the global firstLayerThickness
is applied.
If users wish to overwrite the global firstLayerThickness
for a specific face, they need to add the faceName
attribute to that face in ESP and specify the local firstLayerThickness
in this subsection. Additionally, users can also disable the anisotropic layers by changing the face type
.
Option |
Default |
Description |
---|---|---|
|
REQUIRED |
[string]
aniso , projectAnisoSpacing or none (1)
aniso : grow anisotropic layers normal to the face(2)
projectAnisoSpacing : turn off anisotropic layers growing for this face. Project the anisotropic spacing from the neighboring volumes to this face. See figure Fig. 231(3)
none , turn off anisotropic layers growing for this face. The surface mesh will remain unaltered when populating the volume mesh. |
|
REQUIRED for |
[float] local first layer thickness, which will overwrite the global value |
For more details of the usage of faces
subsection in volumeMesh.json, see Automated Meshing for Internal Flow
refinement
(list)#
The mesh inside the refinement zone is uniformly refined
The refinement zone could enclose/intersect with other objects
Option |
Default |
Description |
---|---|---|
|
|
[string] |
|
REQUIRED |
[3-array(float)] Coordinate of geometrical center |
|
REQUIRED |
[float] Cell spacing applied in the refinement zone |
|
REQUIRED |
[3-array(float)] Size of the box in x, y and z directions |
|
REQUIRED for |
[3-array(float)] Axis of rotation for the box |
|
REQUIRED for |
[float] Angle (in degrees) of rotation for the box |
|
REQUIRED for |
[float] Radius of the cylinder |
|
REQUIRED for |
[float] Length of the cylinder |
|
REQUIRED for |
[3-array(float)] Axis of the cylinder |
Example:
"refinement": [
{
"size": [4, 3, 2],
"center": [2, 0, 0],
"spacing": 0.05,
"axisOfRotation": [ 0, 0, 1 ],
"angleOfRotation": 45
},
{
"type": "cylinder",
"radius": 4,
"length": 5,
"center": [5, 0, 0],
"spacing": 0.05,
"axis": [1, 0, 0]
}]
rotorDisks
(list)#
The mesh inside the rotor disk is semi-structured
The rotor disk cannot enclose/intersect with other objects
Users could create a donut-shape rotor disk and place their hub/centerbody in the middle
Rotor disks are used for resolving the strong flow gradient along the axial direction for the actuator or BET disks
The spacings along the axial, radial and circumferential directions can be adjusted separately
Option |
Default |
Description |
---|---|---|
|
index (0, 1, 2 …) |
[string] Name for the rotor disk. The boundary name will become: rotorDisk- |
|
REQUIRED |
[float] Inner radius of rotor disk, if greater than 0 then a "donut" is created |
|
REQUIRED |
[float] Outer radius of rotor disk |
|
REQUIRED |
[float] Thickness of rotor disk |
|
REQUIRED |
[3-array(float)] Axis of thrust of rotor disk. The disk is axisymmetric respect to |
|
REQUIRED |
[3-array(float)] Position of center of the rotor disk |
|
REQUIRED |
[float] Spacing along the axial direction |
|
REQUIRED |
[float] Spacing along the radial direction |
|
REQUIRED |
[float] Spacing along the circumferential direction |
Example:
"rotorDisks": [
{
"name": "enclosed",
"innerRadius": 0,
"outerRadius": 0.75,
"thickness": 0.1,
"axisThrust": [1, 0, 0],
"center": [0, 5.0, 0],
"spacingAxial": 0.1,
"spacingRadial": 0.1,
"spacingCircumferential": 0.1
},
{
"innerRadius": 0,
"outerRadius": 0.75,
"thickness": 0.1,
"axisThrust": [0, 0, 1],
"center": [0, -5, 0],
"spacingAxial": 0.1,
"spacingRadial": 0.1,
"spacingCircumferential": 0.1
}
],
slidingInterfaces
(list)#
The mesh on the sliding interface is guaranteed to be concentric
The sliding interface is designed to enclose other objects, but it can’t intersect with other objects
Users could create a donut-shape sliding interface and put their stationary centerbody in the middle
Option |
Default |
Description |
---|---|---|
|
index (0, 1, 2…) |
[string] Name for the sliding interface. The boundary name will become: slidingInterface- |
|
REQUIRED |
[float] Inner radius of sliding interface, if greater than 0 then "donut" shape is created |
|
REQUIRED |
[float] Outer radius of sliding interface |
|
REQUIRED |
[float] Thickness of sliding interface |
|
REQUIRED |
[3-array(float)] Axis of rotation of the sliding interface. The cylindrical shape is positioned such its axis is aligned with axisOfRotation. |
|
REQUIRED |
[float] Position of center of the sliding interface |
|
REQUIRED |
[float] Spacing along the axial direction |
|
REQUIRED |
[float] Spacing along the radial direction |
|
REQUIRED |
[float] Spacing along the circumferential direction |
|
REQUIRED |
[list(string)] Objects enclosed by this sliding interface. Can be faces, other sliding interfaces or rotor disks |
Example:
"slidingInterfaces": [
{
"name": "inner",
"innerRadius": 0,
"outerRadius": 0.75,
"thickness": 0.5,
"axisOfRotation": [0, 0, 1],
"center": [0, 0, 0],
"spacingAxial": 0.2,
"spacingRadial": 0.2,
"spacingCircumferential": 0.2,
"enclosedObjects": ["hub", "blade1", "blade2", "blade3"]
}, {
"name": "mid",
"innerRadius": 0,
"outerRadius": 2.0,
"thickness": 2.0,
"axisOfRotation": [0, 1, 0],
"center": [0, 0, 0],
"spacingAxial": 0.2,
"spacingRadial": 0.2,
"spacingCircumferential": 0.2,
"enclosedObjects": ["slidingInterface-inner"]
}, {
"innerRadius": 0,
"outerRadius": 2.0,
"thickness": 2.0,
"axisOfRotation": [0, 1, 0],
"center": [0, 5, 0],
"spacingAxial": 0.2,
"spacingRadial": 0.2,
"spacingCircumferential": 0.2,
"enclosedObjects": ["rotorDisk-enclosed"]
}, {
"innerRadius": 1.5,
"outerRadius": 2.0,
"thickness": 2.0,
"axisOfRotation": [0, 1, 0],
"center": [0, -5, 0],
"spacingAxial": 0.2,
"spacingRadial": 0.2,
"spacingCircumferential": 0.2,
"enclosedObjects": []
}, {
"name": "outer",
"innerRadius": 0,
"outerRadius": 8,
"thickness": 6,
"axisOfRotation": [1, 0, 0],
"center": [0, 0, 0],
"spacingAxial": 0.4,
"spacingRadial": 0.4,
"spacingCircumferential": 0.4,
"enclosedObjects": ["slidingInterface-mid", "rotorDisk-1", "slidingInterface-2", "slidingInterface-3"]
}
],