.. _automatedMeshing: 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\: .. literalinclude:: Files/geometry.example.csm 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 :ref:`internal flow `, only one solid body representing the fluid/air is allowed. .. _fig_geometry: .. figure:: Figures/sphereAndBox.svg :width: 80% :align: center Box and sphere in ESP. As shown in :numref:`fig_geometry`, faces with different :code:`groupName` will be "grouped" as different boundary conditions when exporting the CGNS file. The face with :code:`faceName` will get extra refinement when generating the surface mesh. .. _ESPtoSurfaceMeshSection: 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 :ref:`JSON surface mesher`. Here is an example surfaceMesh.json: .. literalinclude:: Files/surfaceMesh.example.json :language: JSON As shown in the above JSON file, the baseline :code:`maxEdgeLength` is 0.05. An extra refinement is applied to the :code:`top` face of the box with :code:`maxEdgeLength` = 0.01. The surface mesh can be created by submitting the geometry file and surfaceMesh.json via the PythonAPI: .. code-block:: python 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 :code:`surfaceMeshId` .. _fig_surfMesh: .. figure:: Figures/surfaceMesh.png :width: 80% :align: center Auto-generated surface mesh. An extra refinement is applied to the top face of the box. .. _SurfaceToVolumeMeshSection: Volume Meshing ============== The volume mesher takes a :code:`surfaceMeshId` and a volumeMesh.json to generate a volume mesh. The volumeMesh.json determines the volume mesh resolution. It contains the :code:`firstLayerThickness` and :code:`growthRate` of 3D anisotropic layers, the size/location/resolution of :code:`refinement` zones and etc. Full description of volumeMesh.json can be found at :ref:`JSON volume mesher`. Below is an example volumeMesh.json: .. literalinclude:: Files/volumeMesh.example.json :language: JSON 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 :code:`noSlipWall` and prism layers will be grown off these faces. For :ref:`internal flow`, the user can specify which faces are not :code:`noSlipWall` 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. - :code:`auto`: The mesher will Sphere or semi-sphere will be generated based on the bounding box of the geometry - Full 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 - :code:`quasi-3d`: Thin disk will be generated for quasi 3D cases. Both sides of the farfield disk will be treated as "symmetric plane". - :code:`user-defined`: The farfield shape is provided by the user in ESP The volume mesh can be created by submitting the :code:`surfaceMeshId` and volumeMesh.json using the PythonAPI: .. code-block:: python volumeMeshId = flow360client.NewMeshFromSurface(surfaceMeshId, "volumeMesh.json", meshName="my_volume_mesh") Inputs - :code:`surfaceMeshId` - volumeMesh.json (or a Python dictionary) Outputs - volume mesh on cluster (in CGNS format) - Flow360Mesh.json on cluster - return the :code:`volumeMeshId` .. _fig_volMesh3D: .. figure:: Figures/volumeMesh3DView.svg :width: 80% :align: center Left\: WebUI showing the refinement zone. Right\: auto-generated volume mesh. .. _fig_volMeshSlices: .. figure:: Figures/volumeMeshSlices.svg :width: 80% :align: center Auto-generated volume mesh. Left\: sliced at y=1. Right: sliced at y=-1. .. _JSON surface mesher: JSON surface mesher =================== .. list-table:: :widths: 20 10 70 :header-rows: 1 * - Option - Default - Description * - :code:`maxEdgeLength` - REQUIRED - [float] Global maximum edge length for surface cells. This value will be overwritten by the local :code:`maxEdgeLength` in the :code:`faces` subsection * - :code:`curvatureResolutionAngle` - 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 * - :code:`growthRate` - REQUIRED - [float] Growth rate of the anisotropic layers grown from the edges * - :code:`edges` - {} - [dict] :ref:`Extra treatment for edges with attribute edgeName ` * - :code:`faces` - {} - [dict] :ref:`Extra refinement for faces with attribute faceName ` .. _JSON surface mesher edges: :code:`edges` -------------- Subsection :code:`edges` is a dictionary, the keys in this dictionary should correspond to the :code:`edgeName` attribute in the CSM file. If users want to apply special treatment to a particular edge, they need to add the :code:`edgeName` attribute for that edge in ESP. They can then specify the desired type of treatment in this subsection. .. list-table:: :widths: 10 10 80 :header-rows: 1 * - Option - Default - Description * - :code:`type` - REQUIRED - | [string] :code:`aniso` or :code:`projectAnisoSpacing` | (1) :code:`aniso`: grow anisotropic layers orthogonal to the edge, see :numref:`fig_surfMesh_edges` | (2) :code:`projectAnisoSpacing`: project the anisotropic spacing from neighboring faces to the edge, see :numref:`fig_surfMesh_edges_projectAnisoSpacing` * - :code:`method` - REQUIRED for :code:`aniso` - | [string] :code:`angle`, :code:`height` or :code:`aspectRatio` | (1) :code:`angle`: curvature resolution in degrees | (2) :code:`height`: first layer height of the anisotropic layers | (3) :code:`aspectRatio`: maximum aspect ratio of the anisotropic cells * - :code:`value` - REQUIRED for :code:`aniso` - [float] Input value for the corresponding :code:`method` Example\: .. code-block:: json "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" } } .. _fig_surfMesh_edges: .. figure:: Figures/surfaceMesh_firstLayerThickness.png :width: 50% :align: center Surface mesh on a wing. Anisotropic layers are growing from leading and trailing edges. .. _fig_surfMesh_edges_projectAnisoSpacing: .. figure:: Figures/projectAnisoSpacing.png :width: 70% :align: center Surface mesh on a hub. Anisotropic layers are growing from :code:`hubCircle` (green). The anisotropic spacing on the neighboring patches is "projected" to :code:`hubSplitEdge` (red) and updates the nodes distribution along :code:`hubSplitEdge`. .. _JSON surface mesher faces: :code:`faces` -------------- Subsection :code:`faces` is a dictionary where the keys should match the :code:`faceName` attribute in the CSM file. By default, the global :code:`maxEdgeLength` will be applied to all faces. If users want extra refinement on a specific face, they need to add the :code:`faceName` attribute to that face in ESP and specify the target local :code:`maxEdgeLength` in this subsection. .. list-table:: :widths: 10 10 80 :header-rows: 1 * - Option - Default - Description * - maxEdgeLength - REQUIRED - [float] Local maximum edge length. This value overwrites the global :code:`maxEdgeLength`. Example\: .. code-block:: json "faces": { "rightWing": { "maxEdgeLength": 0.05 }, "fuselage": { "maxEdgeLength": 0.05 } } .. _JSON volume mesher: JSON volume mesher ================== .. list-table:: :widths: 10 10 80 :header-rows: 1 * - Option - Default - Description * - :code:`refinementFactor` - 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 :math:`2^{1/3}\approx 1.26` , so the resulting mesh will have approximately 2 times more nodes * - :code:`farfield->type` - :code:`auto` - | [string] :code:`auto`, :code:`quasi-3d` or :code:`user-defined` | (1) :code:`auto` automatically detect if the geometry is a full/+Y half/-Y half airplane and create the farfield accordingly | (2) :code:`quasi-3d` creates two slip walls at the min/max Y locations | (3) :code:`user-defined` farfield geometry provided by the user in ESP * - :code:`volume` - REQUIRED - [dict] :ref:`Global parameters for the volume mesher ` * - :code:`faces` - {} - [dict] :ref:`Special treatments for faces with attribute faceName ` * - :code:`refinement` - [] - [list(dict)] :ref:`Description of refinement zones ` * - :code:`rotorDisks` - [] - [list(dict)] :ref:`Description of rotor disks ` * - :code:`slidingInterfaces` - [] - [list(dict)] :ref:`Description of sliding interfaces ` .. _JSON volume mesher volume: :code:`volume` --------------- .. list-table:: :widths: 10 10 80 :header-rows: 1 * - Option - Default - Description * - :code:`firstLayerThickness` - REQUIRED - [float] First layer thickness for volumetric anisotropic layers * - :code:`growthRate` - REQUIRED - [float] Growth rate for volume prism layers * - :code:`gapTreatmentStrength` - 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\: .. _fig_gaptreatment: .. figure:: Figures/gap.svg :width: 100% :align: center Different :code:`gapTreatmentStrength` strength. The larger :code:`gapTreatmentStrength` is, the more space is dedicated to isotropic mesh than anisotropic mesh in narrow gaps. .. _JSON volume mesher faces: :code:`faces` -------------- Subsection :code:`faces` is a dictionary where the keys should align with the :code:`faceName` attribute in the CSM file. By default, all faces are treated as :code:`aniso` and the global :code:`firstLayerThickness` is applied. If users wish to overwrite the global :code:`firstLayerThickness` for a specific face, they need to add the :code:`faceName` attribute to that face in ESP and specify the local :code:`firstLayerThickness` in this subsection. Additionally, users can also disable the anisotropic layers by changing the face :code:`type`. .. list-table:: :widths: 10 10 80 :header-rows: 1 * - Option - Default - Description * - :code:`type` - REQUIRED - | [string] :code:`aniso`, :code:`projectAnisoSpacing` or :code:`none` | (1) :code:`aniso`: grow anisotropic layers normal to the face | (2) :code:`projectAnisoSpacing`: turn off anisotropic layers growing for this face. Project the anisotropic spacing from the neighboring volumes to this face. See figure :numref:`sphere_in_wind_tunnel_mesh` | (3) :code:`none`, turn off anisotropic layers growing for this face. The surface mesh will remain unaltered when populating the volume mesh. * - :code:`firstLayerThickness` - REQUIRED for :code:`aniso` - [float] local first layer thickness, which will overwrite the global value For more details of the usage of :code:`faces` subsection in volumeMesh.json, see :ref:`Auto_Meshing_Internal_Flow` .. _JSON volume mesher refinement: :code:`refinement` (list) -------------------------- 1. The mesh inside the refinement zone is uniformly refined 2. The refinement zone could enclose/intersect with other objects .. list-table:: :widths: 10 20 70 :header-rows: 1 * - Option - Default - Description * - :code:`type` - :code:`box` - [string] :code:`box` or :code:`cylinder`. Shape of the refinement zone * - :code:`center` - REQUIRED - [3-array(float)] Coordinate of geometrical center * - :code:`spacing` - REQUIRED - [float] Cell spacing applied in the refinement zone * - :code:`size` - REQUIRED - [3-array(float)] Size of the box in x, y and z directions * - :code:`axisOfRotation` - REQUIRED for :code:`box` - [3-array(float)] Axis of rotation for the box * - :code:`angleOfRotation` - REQUIRED for :code:`box` - [float] Angle (in degrees) of rotation for the box * - :code:`radius` - REQUIRED for :code:`cylinder` - [float] Radius of the cylinder * - :code:`length` - REQUIRED for :code:`cylinder` - [float] Length of the cylinder * - :code:`axis` - REQUIRED for :code:`cylinder` - [3-array(float)] Axis of the cylinder Example\: .. code-block:: json "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] }] .. _JSON volume mesher rotorDisks: :code:`rotorDisks` (list) -------------------------- 1. The mesh inside the rotor disk is semi-structured 2. The rotor disk cannot enclose/intersect with other objects 3. Users could create a donut-shape rotor disk and place their hub/centerbody in the middle 4. Rotor disks are used for resolving the strong flow gradient along the axial direction for the actuator or BET disks 5. The spacings along the axial, radial and circumferential directions can be adjusted separately .. list-table:: :widths: 10 20 70 :header-rows: 1 * - Option - Default - Description * - :code:`name` - index (0, 1, 2 ...) - [string] Name for the rotor disk. The boundary name will become: rotorDisk-:code:`name` * - :code:`innerRadius` - REQUIRED - [float] Inner radius of rotor disk, if greater than 0 then a \"donut\" is created * - :code:`outerRadius` - REQUIRED - [float] Outer radius of rotor disk * - :code:`thickness` - REQUIRED - [float] Thickness of rotor disk * - :code:`axisThrust` - REQUIRED - [3-array(float)] Axis of thrust of rotor disk. The disk is axisymmetric respect to :code:`axisThrust` * - :code:`center` - REQUIRED - [3-array(float)] Position of center of the rotor disk * - :code:`spacingAxial` - REQUIRED - [float] Spacing along the axial direction * - :code:`spacingRadial` - REQUIRED - [float] Spacing along the radial direction * - :code:`spacingCircumferential` - REQUIRED - [float] Spacing along the circumferential direction Example\: .. code-block:: json "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 } ], .. _JSON volume mesher slidingInterfaces: :code:`slidingInterfaces` (list) --------------------------------- 1. The mesh on the sliding interface is guaranteed to be concentric 2. The sliding interface is designed to enclose other objects, but it can't intersect with other objects 3. Users could create a donut-shape sliding interface and put their stationary centerbody in the middle .. list-table:: :widths: 10 20 70 :header-rows: 1 * - Option - Default - Description * - :code:`name` - index (0, 1, 2...) - [string] Name for the sliding interface. The boundary name will become: slidingInterface-:code:`name` * - :code:`innerRadius` - REQUIRED - [float] Inner radius of sliding interface, if greater than 0 then \"donut\" shape is created * - :code:`outerRadius` - REQUIRED - [float] Outer radius of sliding interface * - :code:`thickness` - REQUIRED - [float] Thickness of sliding interface * - :code:`axisOfRotation` - REQUIRED - [3-array(float)] Axis of rotation of the sliding interface. The cylindrical shape is positioned such its axis is aligned with axisOfRotation. * - :code:`center` - REQUIRED - [float] Position of center of the sliding interface * - :code:`spacingAxial` - REQUIRED - [float] Spacing along the axial direction * - :code:`spacingRadial` - REQUIRED - [float] Spacing along the radial direction * - :code:`spacingCircumferential` - REQUIRED - [float] Spacing along the circumferential direction * - :code:`enclosedObjects` - REQUIRED - [list(string)] Objects enclosed by this sliding interface. Can be faces, other sliding interfaces or rotor disks Example\: .. code-block:: json "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"] } ], .. _fig_slidingInterfaces: .. figure:: Figures/slidingInterfaces.png :width: 90% :align: center Rotor disks and sliding interfaces generated according to the above configuration.