Source code for butterfly.snappyHexMeshDict

# coding=utf-8
"""snappyHexMeshDict class."""
from collections import OrderedDict
import re

from .foamfile import FoamFile, foam_file_from_file
from .utilities import get_snappyHexMesh_geometry_feild, \
    get_snappyHexMesh_refinement_surfaces, get_snappyHexMesh_surface_layers
from .refinementRegion import refinement_mode_from_dict


# TODO(mostapha): Move default values into a separate file.
# TODO(mostapha): Add specific methods to access most common values
[docs]class SnappyHexMeshDict(FoamFile): """Control dict class.""" # set default valus for this class __default_values = OrderedDict() __default_values['castellatedMesh'] = 'true' __default_values['snap'] = 'true' __default_values['addLayers'] = 'false' # geometry __default_values['geometry'] = {} # castellatedMeshControls __default_values['castellatedMeshControls'] = OrderedDict() __default_values['castellatedMeshControls']['maxLocalCells'] = '1000000' __default_values['castellatedMeshControls']['maxGlobalCells'] = '2000000' __default_values['castellatedMeshControls']['minRefinementCells'] = '10' __default_values['castellatedMeshControls']['maxLoadUnbalance'] = '0.10' __default_values['castellatedMeshControls']['nCellsBetweenLevels'] = '3' __default_values['castellatedMeshControls']['features'] = '()' __default_values['castellatedMeshControls']['refinementSurfaces'] = {} __default_values['castellatedMeshControls']['resolveFeatureAngle'] = '180' __default_values['castellatedMeshControls']['refinementRegions'] = {} __default_values['castellatedMeshControls']['locationInMesh'] = '(0 0 0)' __default_values['castellatedMeshControls']['allowFreeStandingZoneFaces'] = 'true' # snap controls __default_values['snapControls'] = OrderedDict() __default_values['snapControls']['nSmoothPatch'] = '5' __default_values['snapControls']['tolerance'] = '5' __default_values['snapControls']['nSolveIter'] = '100' __default_values['snapControls']['nRelaxIter'] = '8' __default_values['snapControls']['nFeatureSnapIter'] = '10' __default_values['snapControls']['extractFeaturesRefineLevel'] = None __default_values['snapControls']['explicitFeatureSnap'] = None __default_values['snapControls']['implicitFeatureSnap'] = 'true' __default_values['snapControls']['multiRegionFeatureSnap'] = 'true' # layer control __default_values['addLayersControls'] = OrderedDict() __default_values['addLayersControls']['relativeSizes'] = 'true' __default_values['addLayersControls']['layers'] = {} __default_values['addLayersControls']['expansionRatio'] = '1.0' __default_values['addLayersControls']['finalLayerThickness'] = '0.3' __default_values['addLayersControls']['minThickness'] = '0.2' __default_values['addLayersControls']['nGrow'] = '0' __default_values['addLayersControls']['featureAngle'] = '110' __default_values['addLayersControls']['nRelaxIter'] = '3' __default_values['addLayersControls']['nSmoothSurfaceNormals'] = '1' __default_values['addLayersControls']['nSmoothThickness'] = '10' __default_values['addLayersControls']['nSmoothNormals'] = '3' __default_values['addLayersControls']['maxFaceThicknessRatio'] = '0.5' __default_values['addLayersControls']['maxThicknessToMedialRatio'] = '0.3' __default_values['addLayersControls']['minMedianAxisAngle'] = '130' __default_values['addLayersControls']['nBufferCellsNoExtrude'] = '0' __default_values['addLayersControls']['nLayerIter'] = '50' __default_values['addLayersControls']['nRelaxedIter'] = '20' __default_values['meshQualityControls'] = OrderedDict() __default_values['meshQualityControls']['maxNonOrtho'] = '60' __default_values['meshQualityControls']['maxBoundarySkewness'] = '20' __default_values['meshQualityControls']['maxInternalSkewness'] = '4' __default_values['meshQualityControls']['maxConcave'] = '80' __default_values['meshQualityControls']['minFlatness'] = '0.5' __default_values['meshQualityControls']['minVol'] = '1e-13' __default_values['meshQualityControls']['minTetQuality'] = '1e-15' __default_values['meshQualityControls']['minArea'] = '-1' __default_values['meshQualityControls']['minTwist'] = '0.02' __default_values['meshQualityControls']['minDeterminant'] = '0.001' __default_values['meshQualityControls']['minFaceWeight'] = '0.02' __default_values['meshQualityControls']['minVolRatio'] = '0.01' __default_values['meshQualityControls']['minTriangleTwist'] = '-1' __default_values['meshQualityControls']['nSmoothScale'] = '4' __default_values['meshQualityControls']['errorReduction'] = '0.75' __default_values['meshQualityControls']['relaxed'] = {'maxNonOrtho': '75'} __default_values['debug'] = '0' __default_values['mergeTolerance'] = '1E-6' __globRefineLevel = (0, 0) def __init__(self, values=None): """Init class.""" FoamFile.__init__(self, name='snappyHexMeshDict', cls='dictionary', location='system', default_values=self.__default_values, values=values) self.__geometries = None self.__isFeatureEdgeRefinementImplicit = True self.convertToMeters = 1.0 # This is useful to scale the locationInMesh
[docs] @classmethod def from_file(cls, filepath): """Create a FoamFile from a file. Args: filepath: Full file path to dictionary. """ return cls(values=foam_file_from_file(filepath, cls.__name__))
[docs] @classmethod def from_bf_geometries(cls, project_name, geometries, meshing_parameters=None, convertToMeters=1, values=None): """Create snappyHexMeshDict from HBGeometries.""" _cls = cls(values) _cls.convertToMeters = convertToMeters _cls.project_name = project_name _cls.__geometries = cls._check_input_geometries(geometries) _cls.update_meshing_parameters(meshing_parameters) _cls.set_geometry() _cls.set_refinement_surfaces() _cls.set_nSurfaceLayers() return _cls
@property def project_name(self): """Project name.""" return self.__project_name # TODO(mostapha): updating the name should update refinementSurfaces and setGeometry # when happens from Case.from_file() with no butterfly geometry. @project_name.setter def project_name(self, name): assert re.match("^[a-zA-Z0-9_]*$", name), \ 'Invalid project name: "{}".\n' \ 'Do not use whitespace or special charecters.'.format(name) self.__project_name = name @property def geometries(self): """Butterfly geometries.""" return self.__geometries @property def is_featureEdgeRefinement_implicit(self): """Return True if implicit feature refinment is used.""" return self.__isFeatureEdgeRefinementImplicit @property def locationInMesh(self): """A tuple for the location of the volume the should be meshed. x, y, z values will be multiplied to self.convertToMeters. If the units are not Meters you can set the convertToMeters using self.convertToMeters """ return self.values['castellatedMeshControls']['locationInMesh'] @locationInMesh.setter def locationInMesh(self, point): if not point: point = (0, 0, 0) try: x, y, z = tuple(eval(point)) except Exception: x, y, z = tuple(point) # scale point based on convertToMeters point = x * self.convertToMeters, \ y * self.convertToMeters, \ z * self.convertToMeters self.values['castellatedMeshControls']['locationInMesh'] = \ str(tuple(point)).replace(',', "") @property def globRefineLevel(self): """A tuple of (min, max) values for global refinment.""" return self.__globRefineLevel @globRefineLevel.setter def globRefineLevel(self, r): self.__globRefineLevel = (0, 0) if not r else tuple(r) if self.__globRefineLevel: self.set_refinement_surfaces() @property def castellatedMesh(self): """Set if castellatedMesh should be ran.""" return self.values['castellatedMesh'] @castellatedMesh.setter def castellatedMesh(self, value=True): value = value if isinstance(value, bool) else \ bool(str(value).capitalize()) self.values['castellatedMesh'] = str(value).lower() @property def snap(self): """Set if snap should be ran.""" return self.values['snap'] @snap.setter def snap(self, value=True): value = value if isinstance(value, bool) else \ bool(str(value).capitalize()) self.values['snap'] = str(value).lower() @property def addLayers(self): """Set if addLayers should be ran.""" return self.values['addLayers'] @addLayers.setter def addLayers(self, value=True): value = value if isinstance(value, bool) else \ bool(str(value).capitalize()) self.values['addLayers'] = str(value).lower() @property def features(self): """Set features for castellatedMeshControls.""" return self.values['castellatedMeshControls']['features'] @features.setter def features(self, value=None): value = value or () self.values['castellatedMeshControls']['features'] = str(value) @property def extractFeaturesRefineLevel(self): """A refinment value for extract feature level.""" return self.values['snapControls']['extractFeaturesRefineLevel'] @extractFeaturesRefineLevel.setter def extractFeaturesRefineLevel(self, value=1): self.values['snapControls']['extractFeaturesRefineLevel'] = str(int(value)) @property def nCellsBetweenLevels(self): """Number of cells between levels for castellatedMeshControls (default: 3).""" return self.values['castellatedMeshControls']['nCellsBetweenLevels'] @nCellsBetweenLevels.setter def nCellsBetweenLevels(self, value=3): value = value or 3 self.values['castellatedMeshControls']['nCellsBetweenLevels'] = str(int(value)) @property def maxGlobalCells(self): """Number of max global cells for castellatedMeshControls (default: 2000000).""" return self.values['castellatedMeshControls']['maxGlobalCells'] @maxGlobalCells.setter def maxGlobalCells(self, value=2000000): value = value or 2000000 self.values['castellatedMeshControls']['maxGlobalCells'] = str(int(value)) @property def stl_file_names(self): """List of names for stl files if any. This method doesn't return stl files for refinementRegions. You can use self.refinementRegion_names to get the names for refinment regions. """ stl_f_names = self.values['geometry'].keys() return tuple(f[:-4] for f in stl_f_names if not f[:-4] in self.refinementRegion_names) @property def refinementRegions(self): """Refinement regions.""" return self.values['castellatedMeshControls']['refinementRegions'] @property def refinementRegion_names(self): """List of stl files if any.""" return self.values['castellatedMeshControls']['refinementRegions'].keys()
[docs] def update_meshing_parameters(self, meshing_parameters): """Update meshing parameters for blockMeshDict.""" if not meshing_parameters: return assert hasattr(meshing_parameters, 'isMeshingParameters'), \ 'Expected MeshingParameters not {}'.format(type(meshing_parameters)) if meshing_parameters.locationInMesh: self.locationInMesh = meshing_parameters.locationInMesh if meshing_parameters.globRefineLevel: self.globRefineLevel = meshing_parameters.globRefineLevel
[docs] def refinementRegion_mode(self, refinementRegion_name): """Refinement region mode for a refinement region.""" assert refinementRegion_name in self.refinementRegion_names, \ 'Failed to find {} in {}'.format(refinementRegion_name, self.refinementRegion_names) c_mesh_control = self.values['castellatedMeshControls'] mode = c_mesh_control['refinementRegions'][refinementRegion_name] return refinement_mode_from_dict(mode)
[docs] def set_geometry(self): """Set geometry from bf_geometries.""" _geoField = get_snappyHexMesh_geometry_feild(self.project_name, self.geometries, meshing_type='triSurfaceMesh') self.values['geometry'].update(_geoField)
[docs] def set_refinement_surfaces(self): """Set refinement values for geometries.""" _ref = get_snappyHexMesh_refinement_surfaces(self.project_name, self.geometries, self.globRefineLevel) self.values['castellatedMeshControls']['refinementSurfaces'] = _ref
[docs] def set_nSurfaceLayers(self): """Set number of surface layers for geometries.""" layers = get_snappyHexMesh_surface_layers(self.geometries) self.values['addLayersControls']['layers'] = layers
[docs] def set_featureEdgeRefinement_to_implicit(self): """Set meshing snap to implicitFeatureSnap.""" self.values['snapControls']['implicitFeatureSnap'] = 'true' self.values['snapControls']['multiRegionFeatureSnap'] = 'true' self.values['snapControls']['explicitFeatureSnap'] = None self.values['snapControls']['extractFeaturesRefineLevel'] = None self.values['castellatedMeshControls']['features'] = '()' self.__isFeatureEdgeRefinementImplicit = True
[docs] def set_featureEdgeRefinement_to_explicit(self, file_name, refinement_level=2): """Set meshing snap to explicitFeatureSnap. Args: file_name: eMesh file name. refinement_level: extractFeaturesRefineLevel (default: 2) """ file_name = file_name.replace('.eMesh', '') if hasattr(refinement_level, 'levels'): # in case it's a distance refinment refinement_level = refinement_level.levels else: refinement_level = refinement_level or 2 self.values['castellatedMeshControls']['features'] = \ '({file "%s.eMesh"; level %s;} )' % (file_name, str(refinement_level)) self.values['snapControls']['implicitFeatureSnap'] = None self.values['snapControls']['multiRegionFeatureSnap'] = None self.values['snapControls']['explicitFeatureSnap'] = 'true' self.values['snapControls']['extractFeaturesRefineLevel'] = 'true' self.__isFeatureEdgeRefinementImplicit = False
[docs] def add_stl_geometry(self, file_name): """Add stl geometry to snappyHexMeshDict. Args: file_name: Stl file name. This file should be located under /constant/triSurface. """ stl = {'{}.stl'.format(file_name): {'type': 'triSurfaceMesh', 'name': file_name}} self.values['geometry'].update(stl)
[docs] def add_refinementRegion(self, refinementRegion=None): """Add refinement region to snappyHexMeshDict.""" if refinementRegion is None: return assert hasattr(refinementRegion, 'isRefinementRegion'), \ '{} is not a refinement region.'.format(refinementRegion) # add geometry to stl self.add_stl_geometry(refinementRegion.name) rg = {refinementRegion.name: refinementRegion.refinement_mode.to_openfoam_dict()} self.values['castellatedMeshControls']['refinementRegions'].update(rg)
@staticmethod def _check_input_geometries(geos): for geo in geos: assert hasattr(geo, 'isBFMesh'), \ 'Expected butterfly.Mesh not {}'.format(geo) return geos