Source code for vasp.automatedPreprocessing.vmtkmeshgeneratorfsi

#!/usr/bin/env python

# Program:   VMTK
# Module:    $RCSfile: vmtkmeshgeneratorfsi.py,v $
# Language:  Python
# Date:      $$
# Version:   $$

# Copyright (c) Luca Antiga, David Steinman. All rights reserved.
# See LICENCE file for details.

# This software is distributed WITHOUT ANY WARRANTY; without even
# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the above copyright notices for more information.

# This class is a modified versions of vmtkmeshgenerator.
# For Fluid-Structure Interaction mesh generation.
# Modifications by:
#   Alban Souche, Simula Research Laboratory, Fornebu (October 2018)
#   Johannes Ring, Simula Research Laboratory, Oslo (2022-2023)

from __future__ import absolute_import  # NEEDS TO STAY AS TOP LEVEL MODULE FOR Py2-3 COMPATIBILITY
import vtk
from vmtk import vtkvmtk, vmtkscripts, pypes
import sys
import numpy as np


[docs] class vmtkMeshGeneratorFsi(pypes.pypeScript): def __init__(self): pypes.pypeScript.__init__(self) self.Surface = None self.TargetEdgeLength = 1.0 self.TargetEdgeLengthFactor = 1.0 self.TargetEdgeLengthArrayName = '' self.TargetEdgeLengthArrayNameSolid = '' self.MaxEdgeLength = 1E16 self.MinEdgeLength = 0.0 self.TriangleSplitFactor = 5.0 self.CellEntityIdsArrayName = 'CellEntityIds' self.ElementSizeMode = 'edgelength' self.ElementSizeModeFluid = 'edgelength' self.ElementSizeModeSolid = 'edgelength' self.VolumeElementScaleFactor = 0.8 self.CappingMethod = 'simple' self.SkipCapping = 0 self.RemeshCapsOnly = 0 self.SkipRemeshing = 0 self.EndcapsEdgeLengthFactor = 1.0 self.BoundaryLayer = 0 self.NumberOfSubLayersFluid = 2 self.NumberOfSubLayersSolid = 2 self.SubLayerRatioFluid = 0.75 self.SubLayerRatioSolid = 0.5 self.BoundaryLayerThicknessFactor = 0.85 self.SolidThickness = 1 self.NumberOfSubsteps = 2000 self.Relaxation = 0.01 self.LocalCorrectionFactor = 0.45 self.Tetrahedralize = 0 self.BoundaryLayerOnCaps = 1 self.SizingFunctionArrayName = 'VolumeSizingFunction' self.SolidSideWallId = 11 self.InterfaceFsiId = 22 self.SolidOuterWallId = 33 self.FluidVolumeId = 0 # (keep to 0) self.SolidVolumeId = 1 self.ExtractBranch = False self.BranchGroupIds = [] self.BranchIdsOffset = 1000 self.Centerlines = None self.Mesh = None self.RemeshedSurface = None self.SetScriptName('vmtkmeshgenerator') self.SetScriptDoc('generate a mesh suitable for CFD from a surface') self.SetInputMembers([ ['Surface', 'i', 'vtkPolyData', 1, '', 'the input surface', 'vmtksurfacereader'], ['TargetEdgeLength', 'edgelength', 'float', 1, '(0.0,)'], ['TargetEdgeLengthArrayName', 'edgelengtharray', 'str', 1], ['TargetEdgeLengthArrayNameSolid', 'edgelengtharraysolid', 'str', 1], ['TargetEdgeLengthFactor', 'edgelengthfactor', 'float', 1, '(0.0,)'], ['TriangleSplitFactor', 'trianglesplitfactor', 'float', 1, '(0.0,)'], ['EndcapsEdgeLengthFactor', 'endcapsedgelengthfactor', 'float', 1, '(0.0,)'], ['MaxEdgeLength', 'maxedgelength', 'float', 1, '(0.0,)'], ['MinEdgeLength', 'minedgelength', 'float', 1, '(0.0,)'], ['CellEntityIdsArrayName', 'entityidsarray', 'str', 1], ['ElementSizeMode', 'elementsizemode', 'str', 1, '["edgelength","edgelengtharray"]'], ['ElementSizeModeFluid', 'elementsizemodefluid', 'str', 1, '["edgelength","edgelengtharray"]'], ['ElementSizeModeSolid', 'elementsizemodesolid', 'str', 1, '["edgelength","edgelengtharray"]'], ['CappingMethod', 'cappingmethod', 'str', 1, '["simple","annular","concaveannular"]'], ['SkipCapping', 'skipcapping', 'bool', 1, ''], ['SkipRemeshing', 'skipremeshing', 'bool', 1, ''], ['VolumeElementScaleFactor', 'volumeelementfactor', 'float', 1, '(0.0,)'], ['BoundaryLayer', 'boundarylayer', 'bool', 1, ''], ['NumberOfSubLayersFluid', 'sublayersfluid', 'int', 1, '(0,)'], ['NumberOfSubLayersSolid', 'sublayerssolid', 'int', 1, '(0,)'], ['NumberOfSubsteps', 'substeps', 'int', 1, '(0,)'], ['NumberOfSubstepsSolid', 'substepssolid', 'int', 1, '(0,)'], ['NumberOfSubstepsFluid', 'substepsfluid', 'int', 1, '(0,)'], ['Relaxation', 'relaxation', 'float', 1, '(0.0,)'], ['RelaxationSolid', 'relaxationsolid', 'float', 1, '(0.0,)'], ['RelaxationFluid', 'relaxationfluid', 'float', 1, '(0.0,)'], ['LocalCorrectionFactor', 'localcorrection', 'float', 1, '(0.0,)'], ['LocalCorrectionFactorFluid', 'localcorrectionfluid', 'float', 1, '(0.0,)'], ['LocalCorrectionFactorSolid', 'localcorrectionsolid', 'float', 1, '(0.0,)'], ['SubLayerRatioFluid', 'sublayerratiofluid', 'float', 1, '(0.0,)'], ['SubLayerRatioSolid', 'sublayerratiosolid', 'float', 1, '(0.0,)'], ['BoundaryLayerThicknessFactor', 'thicknessfactor', 'float', 1, '(0.0,)'], ['SolidThickness', 'solidthickness', 'float', 1, '(0.0,)'], ['RemeshCapsOnly', 'remeshcapsonly', 'bool', 1, ''], ['BoundaryLayerOnCaps', 'boundarylayeroncaps', 'bool', 1, ''], ['Tetrahedralize', 'tetrahedralize', 'bool', 1, ''], ['ExtractBranch', 'extractbranch', 'bool', 0, ''], ['BranchGroupIds', 'branchgroupids', 'int', -1, ''], ['BranchIdsOffset', 'tbranchidsoffset', 'int', 1000, '(0,)'], ['Centerlines', 'centerlines', 'vtkPolyData', 1, '', '', 'vmtksurfacereader'], ]) self.SetOutputMembers([ ['Mesh', 'o', 'vtkUnstructuredGrid', 1, '', 'the output mesh', 'vmtkmeshwriter'], ['CellEntityIdsArrayName', 'entityidsarray', 'str', 1], ['RemeshedSurface', 'remeshedsurface', 'vtkPolyData', 1, '', 'the output surface', 'vmtksurfacewriter'], ])
[docs] def Execute(self): if self.Surface is None: self.PrintError('Error: No input surface.') wallEntityOffset = 1 if self.SkipCapping or not self.BoundaryLayerOnCaps: self.PrintLog("Not capping surface") surface = self.Surface cellEntityIdsArray = vtk.vtkIntArray() cellEntityIdsArray.SetName(self.CellEntityIdsArrayName) cellEntityIdsArray.SetNumberOfTuples(surface.GetNumberOfCells()) cellEntityIdsArray.FillComponent(0, 0.0) surface.GetCellData().AddArray(cellEntityIdsArray) else: self.PrintLog("Capping surface") capper = vmtkscripts.vmtkSurfaceCapper() capper.Surface = self.Surface capper.Interactive = 0 capper.Method = self.CappingMethod capper.TriangleOutput = 0 capper.CellEntityIdOffset = wallEntityOffset capper.Execute() surface = capper.Surface if self.SkipRemeshing: remeshedSurface = surface else: self.PrintLog("Remeshing surface") remeshing = vmtkscripts.vmtkSurfaceRemeshing() remeshing.Surface = surface remeshing.CellEntityIdsArrayName = self.CellEntityIdsArrayName remeshing.TargetEdgeLength = self.TargetEdgeLength remeshing.MaxEdgeLength = self.MaxEdgeLength remeshing.MinEdgeLength = self.MinEdgeLength remeshing.TargetEdgeLengthFactor = self.TargetEdgeLengthFactor remeshing.TargetEdgeLengthArrayName = self.TargetEdgeLengthArrayName remeshing.TriangleSplitFactor = self.TriangleSplitFactor remeshing.ElementSizeMode = self.ElementSizeMode if self.RemeshCapsOnly: remeshing.ExcludeEntityIds = [wallEntityOffset] remeshing.Execute() remeshedSurface = remeshing.Surface if self.BoundaryLayer: projection = vmtkscripts.vmtkSurfaceProjection() projection.Surface = remeshedSurface projection.ReferenceSurface = surface projection.Execute() normals = vmtkscripts.vmtkSurfaceNormals() normals.Surface = projection.Surface normals.NormalsArrayName = 'Normals' normals.Execute() surfaceToMesh = vmtkscripts.vmtkSurfaceToMesh() surfaceToMesh.Surface = normals.Surface surfaceToMesh.Execute() self.PrintLog("Generating boundary layer fluid") placeholderCellEntityId = 9999 boundaryLayer = vmtkscripts.vmtkBoundaryLayer() boundaryLayer.Mesh = surfaceToMesh.Mesh boundaryLayer.WarpVectorsArrayName = 'Normals' boundaryLayer.NegateWarpVectors = True boundaryLayer.ThicknessArrayName = self.TargetEdgeLengthArrayName if self.ElementSizeMode == 'edgelength': boundaryLayer.ConstantThickness = True else: boundaryLayer.ConstantThickness = False boundaryLayer.IncludeSurfaceCells = 0 boundaryLayer.NumberOfSubLayers = self.NumberOfSubLayersFluid boundaryLayer.NumberOfSubsteps = self.NumberOfSubsteps boundaryLayer.Relaxation = self.Relaxation boundaryLayer.LocalCorrectionFactor = self.LocalCorrectionFactor boundaryLayer.SubLayerRatio = self.SubLayerRatioFluid boundaryLayer.Thickness = self.BoundaryLayerThicknessFactor * self.TargetEdgeLength boundaryLayer.ThicknessRatio = self.BoundaryLayerThicknessFactor * self.TargetEdgeLengthFactor boundaryLayer.MaximumThickness = self.BoundaryLayerThicknessFactor * self.MaxEdgeLength if not self.BoundaryLayerOnCaps: boundaryLayer.SidewallCellEntityId = placeholderCellEntityId boundaryLayer.InnerSurfaceCellEntityId = wallEntityOffset boundaryLayer.VolumeCellEntityId = self.FluidVolumeId boundaryLayer.Execute() self.PrintLog("Generating boundary layer solid") boundaryLayer2 = vmtkscripts.vmtkBoundaryLayer() boundaryLayer2.Mesh = surfaceToMesh.Mesh boundaryLayer2.WarpVectorsArrayName = 'Normals' boundaryLayer2.NegateWarpVectors = True boundaryLayer2.ThicknessArrayName = self.TargetEdgeLengthArrayNameSolid if self.ElementSizeModeSolid == 'edgelength': boundaryLayer2.ConstantThickness = True else: boundaryLayer2.ConstantThickness = False boundaryLayer2.IncludeSurfaceCells = 1 boundaryLayer2.NumberOfSubLayers = self.NumberOfSubLayersSolid boundaryLayer2.NumberOfSubsteps = self.NumberOfSubsteps boundaryLayer2.Relaxation = self.Relaxation boundaryLayer2.LocalCorrectionFactor = self.LocalCorrectionFactor boundaryLayer2.SubLayerRatio = self.SubLayerRatioSolid boundaryLayer2.Thickness = self.SolidThickness boundaryLayer2.ThicknessRatio = 1 if not self.BoundaryLayerOnCaps: boundaryLayer2.SidewallCellEntityId = self.SolidSideWallId boundaryLayer2.InnerSurfaceCellEntityId = self.InterfaceFsiId boundaryLayer2.OuterSurfaceCellEntityId = self.SolidOuterWallId boundaryLayer2.VolumeCellEntityId = self.SolidVolumeId boundaryLayer2.Execute() meshToSurface = vmtkscripts.vmtkMeshToSurface() meshToSurface.Mesh = boundaryLayer.InnerSurfaceMesh meshToSurface.Execute() innerSurface = meshToSurface.Surface if self.ExtractBranch and self.Centerlines is not None: self.PrintLog("Branch extraction enabled. Marking solid mesh IDs of the selected branch with an " f"offset of {self.BranchIdsOffset}.") # Extract branch groups from the centerlines extractGroups = vmtkscripts.vmtkBranchExtractor() extractGroups.Centerlines = self.Centerlines extractGroups.Execute() # Clip the mesh using the extracted branch groups clipBranch = vmtkscripts.vmtkMeshBranchClipper() clipBranch.Mesh = boundaryLayer2.Mesh clipBranch.Centerlines = extractGroups.Centerlines # Determine whether to use specified group IDs or interactive selection if self.BranchGroupIds: clipBranch.Interactive = 0 clipBranch.GroupIds = self.BranchGroupIds else: clipBranch.Interactive = 1 clipBranch.Execute() # Create cell locators for the solid mesh and the clipped mesh solidCellLocator = vtk.vtkCellLocator() branchCellLocator = vtk.vtkCellLocator() # Build locators for efficient cell searches solidCellLocator.SetDataSet(boundaryLayer2.Mesh) solidCellLocator.BuildLocator() branchCellLocator.SetDataSet(clipBranch.Mesh) branchCellLocator.BuildLocator() # Get the solid cell ID's from the original mesh solidCellIds = boundaryLayer2.Mesh.GetCellData().GetScalars(self.CellEntityIdsArrayName) # Find solid cells in the original mesh that are within the bounds of the clipped mesh vtkIdList = vtk.vtkIdList() solidCellLocator.FindCellsWithinBounds(clipBranch.Mesh.GetBounds(), vtkIdList) ids = np.array([vtkIdList.GetId(i) for i in range(vtkIdList.GetNumberOfIds())]) # Initialize variables for finding the closest point in the clipped branch to the current solid cell cell = [0.0, 0.0, 0.0] cellId = vtk.mutable(0) subId = vtk.mutable(0) dist = vtk.mutable(0.0) # Iterate through found solid cells within the bounds of the clipped branch for solidCellId in ids: # Extract the coordinates of the first point of the current solid cell in the original mesh point = boundaryLayer2.Mesh.GetCell(solidCellId).GetPoints().GetPoint(0) # Find the closest point in the clipped branch to the current cell in the original mesh branchCellLocator.FindClosestPoint(point, cell, cellId, subId, dist) # If the distance is zero, indicating the current solid cell is part of the clipped branch if dist == 0: # Mark the solid mesh ID of the current cell with an offset solidCellIds.SetValue(solidCellId, solidCellIds.GetValue(solidCellId) + self.BranchIdsOffset) # Update the cell data in the original mesh boundaryLayer2.Mesh.GetCellData().Update() if not self.BoundaryLayerOnCaps: self.PrintLog("Capping inner surface") capper = vmtkscripts.vmtkSurfaceCapper() capper.Surface = innerSurface capper.Interactive = 0 capper.Method = self.CappingMethod capper.TriangleOutput = 1 capper.CellEntityIdOffset = wallEntityOffset capper.Execute() self.PrintLog("Remeshing endcaps") remeshing = vmtkscripts.vmtkSurfaceRemeshing() remeshing.Surface = capper.Surface remeshing.CellEntityIdsArrayName = self.CellEntityIdsArrayName remeshing.TargetEdgeLength = self.TargetEdgeLength * self.EndcapsEdgeLengthFactor remeshing.MaxEdgeLength = self.MaxEdgeLength remeshing.MinEdgeLength = self.MinEdgeLength remeshing.TargetEdgeLengthFactor = self.TargetEdgeLengthFactor * self.EndcapsEdgeLengthFactor remeshing.TargetEdgeLengthArrayName = self.TargetEdgeLengthArrayName remeshing.TriangleSplitFactor = self.TriangleSplitFactor remeshing.ElementSizeMode = self.ElementSizeMode remeshing.ExcludeEntityIds = [wallEntityOffset] remeshing.Execute() innerSurface = remeshing.Surface self.PrintLog("Computing sizing function") sizingFunction = vtkvmtk.vtkvmtkPolyDataSizingFunction() sizingFunction.SetInputData(innerSurface) sizingFunction.SetSizingFunctionArrayName(self.SizingFunctionArrayName) sizingFunction.SetScaleFactor(self.VolumeElementScaleFactor) sizingFunction.Update() surfaceToMesh2 = vmtkscripts.vmtkSurfaceToMesh() surfaceToMesh2.Surface = sizingFunction.GetOutput() surfaceToMesh2.Execute() self.PrintLog("Generating volume mesh") tetgen = vmtkscripts.vmtkTetGen() tetgen.Mesh = surfaceToMesh2.Mesh tetgen.GenerateCaps = 0 tetgen.UseSizingFunction = 1 tetgen.SizingFunctionArrayName = self.SizingFunctionArrayName tetgen.CellEntityIdsArrayName = self.CellEntityIdsArrayName tetgen.Order = 1 tetgen.Quality = 1 tetgen.PLC = 1 tetgen.NoBoundarySplit = 1 tetgen.RemoveSliver = 1 tetgen.OutputSurfaceElements = 1 tetgen.OutputVolumeElements = 1 tetgen.RegionAttrib = 0 tetgen.Execute() if tetgen.Mesh.GetNumberOfCells() == 0 and surfaceToMesh.Mesh.GetNumberOfCells() > 0: self.PrintError('Running TetGen failed. Try to re-mesh.') self.PrintLog("Assembling fluid mesh") appendFilter = vtkvmtk.vtkvmtkAppendFilter() appendFilter.AddInputData(boundaryLayer.Mesh) appendFilter.AddInputData(tetgen.Mesh) appendFilter.Update() self.Mesh = appendFilter.GetOutput() if not self.BoundaryLayerOnCaps: cellEntityIdsArray = self.Mesh.GetCellData().GetArray(self.CellEntityIdsArrayName) def VisitNeighbors(i, cellEntityId): cellPointIds = vtk.vtkIdList() self.Mesh.GetCellPoints(i, cellPointIds) neighborPointIds = vtk.vtkIdList() neighborPointIds.SetNumberOfIds(1) pointNeighborCellIds = vtk.vtkIdList() neighborCellIds = vtk.vtkIdList() for j in range(cellPointIds.GetNumberOfIds()): neighborPointIds.SetId(0, cellPointIds.GetId(j)) self.Mesh.GetCellNeighbors(i, neighborPointIds, pointNeighborCellIds) for k in range(pointNeighborCellIds.GetNumberOfIds()): neighborCellIds.InsertNextId(pointNeighborCellIds.GetId(k)) for j in range(neighborCellIds.GetNumberOfIds()): cellId = neighborCellIds.GetId(j) neighborCellEntityId = cellEntityIdsArray.GetTuple1(cellId) neighborCellType = self.Mesh.GetCellType(cellId) if neighborCellType not in [vtk.VTK_TRIANGLE, vtk.VTK_QUADRATIC_TRIANGLE, vtk.VTK_QUAD]: continue if neighborCellEntityId != placeholderCellEntityId: continue cellEntityIdsArray.SetTuple1(cellId, cellEntityId) VisitNeighbors(cellId, cellEntityId) for i in range(self.Mesh.GetNumberOfCells()): cellEntityId = cellEntityIdsArray.GetTuple1(i) cellType = self.Mesh.GetCellType(i) if cellType not in [vtk.VTK_TRIANGLE, vtk.VTK_QUADRATIC_TRIANGLE, vtk.VTK_QUAD]: continue if cellEntityId in [0, 1, placeholderCellEntityId]: continue VisitNeighbors(i, cellEntityId) self.PrintLog("Assembling final FSI mesh") appendFilter2 = vtkvmtk.vtkvmtkAppendFilter() appendFilter2.AddInputData(appendFilter.GetOutput()) appendFilter2.AddInputData(boundaryLayer2.Mesh) # appendFilter2.AddInputData(mergeSolid.GetOutput()) appendFilter2.Update() self.Mesh = appendFilter2.GetOutput() else: self.PrintLog("Computing sizing function") sizingFunction = vtkvmtk.vtkvmtkPolyDataSizingFunction() sizingFunction.SetInputData(remeshedSurface) sizingFunction.SetSizingFunctionArrayName(self.SizingFunctionArrayName) sizingFunction.SetScaleFactor(self.VolumeElementScaleFactor) sizingFunction.Update() self.PrintLog("Converting surface to mesh") surfaceToMesh = vmtkscripts.vmtkSurfaceToMesh() surfaceToMesh.Surface = sizingFunction.GetOutput() surfaceToMesh.Execute() self.PrintLog("Generating volume mesh") tetgen = vmtkscripts.vmtkTetGen() tetgen.Mesh = surfaceToMesh.Mesh tetgen.GenerateCaps = 0 tetgen.UseSizingFunction = 1 tetgen.SizingFunctionArrayName = self.SizingFunctionArrayName tetgen.CellEntityIdsArrayName = self.CellEntityIdsArrayName tetgen.Order = 1 tetgen.Quality = 1 tetgen.PLC = 1 tetgen.NoBoundarySplit = 1 tetgen.RemoveSliver = 1 tetgen.OutputSurfaceElements = 1 tetgen.OutputVolumeElements = 1 tetgen.Execute() self.Mesh = tetgen.Mesh if self.Mesh.GetNumberOfCells() == 0 and surfaceToMesh.Mesh.GetNumberOfCells() > 0: self.Mesh = surfaceToMesh.Mesh raise Exception('An error occurred during tetrahedralization. Will only output surface mesh.') if self.Tetrahedralize: tetrahedralize = vtkvmtk.vtkvmtkUnstructuredGridTetraFilter() tetrahedralize.SetInputData(self.Mesh) tetrahedralize.Update() self.Mesh = tetrahedralize.GetOutput() self.RemeshedSurface = remeshedSurface
if __name__ == '__main__': main = pypes.pypeMain() main.Arguments = sys.argv main.Execute()