Mod: mesh oop style

New: submeshes
Fix: salome current version
Mod: default values for mesh parameters - less values in main config
Fix: smesh_* style for boundary groups
Move: notes to real notes, reserve docs for docs
This commit is contained in:
L-Nafaryus 2021-07-09 23:07:53 +05:00
parent a7adc59b70
commit 7f75e9c790
No known key found for this signature in database
GPG Key ID: C76D8DCD2727DBB7
9 changed files with 274 additions and 291 deletions

View File

@ -112,7 +112,7 @@ def createQueue():
for structure in config.base.__dict__.keys(): for structure in config.base.__dict__.keys():
theta = getattr(config, structure).parameters.theta theta = getattr(config, structure).geometry.theta
parameters_theta[structure] = [ n * theta[2] for n in range(int(theta[0] / theta[2]), int(theta[1] / theta[2]) + 1) ] parameters_theta[structure] = [ n * theta[2] for n in range(int(theta[0] / theta[2]), int(theta[1] / theta[2]) + 1) ]
thickness = getattr(config, structure).mesh.thickness thickness = getattr(config, structure).mesh.thickness
@ -156,10 +156,8 @@ def createQueue():
"meshTime": 0, "meshTime": 0,
"flowTime": 0 "flowTime": 0
}, },
"parameters": {
"theta": theta
},
"geometry": { "geometry": {
"theta": theta,
"direction": direction, "direction": direction,
"fillet": getattr(config, structure).geometry.fillet "fillet": getattr(config, structure).geometry.fillet
}, },

View File

@ -11,17 +11,15 @@ name = "anisotropy"
format = "%(levelname)s: %(message)s" format = "%(levelname)s: %(message)s"
[base] [base]
simple = false simple = true
bodyCentered = true bodyCentered = true
faceCentered = false faceCentered = true
### ###
# Simple # Simple
## ##
[simple.parameters]
theta = [0.01, 0.28, 0.01]
[simple.geometry] [simple.geometry]
theta = [0.01, 0.28, 0.01]
directions = [ directions = [
[1, 0, 0], [1, 0, 0],
[0, 0, 1], [0, 0, 1],
@ -30,37 +28,20 @@ directions = [
fillet = true fillet = true
[simple.mesh] [simple.mesh]
fineness = 3 viscousLayers = true
minSize = 0.05
maxSize = 0.1
growthRate = 0.5
nbSegPerEdge = 0.5
nbSegPerRadius = 0.5
chordalErrorEnabled = false
chordalError = -1
secondOrder = false
optimize = true
quadAllowed = false
useSurfaceCurvature = true
fuseEdges = true
checkChartBoundary = false
localSizeOnShape.strips = 0.01
viscousLayers = false
thickness = [0.01, 0.005] thickness = [0.01, 0.005]
numberOfLayers = 1
stretchFactor = 1.2 [simple.mesh.submesh.strips]
isFacesToIgnore = true growthRate = 0.2
nbSegPerEdge = 2
nbSegPerRadius = 3
### ###
# Body-centered # Body-centered
## ##
[bodyCentered.parameters]
# TODO: 0.18 # TODO: 0.18
theta = [0.01, 0.17, 0.01]
[bodyCentered.geometry] [bodyCentered.geometry]
theta = [0.01, 0.17, 0.01]
directions = [ directions = [
[1, 0, 0], [1, 0, 0],
[0, 0, 1], [0, 0, 1],
@ -69,36 +50,19 @@ directions = [
fillet = true fillet = true
[bodyCentered.mesh] [bodyCentered.mesh]
fineness = 3
minSize = 0.05
maxSize = 0.5
growthRate = 0.5
nbSegPerEdge = 0.5
nbSegPerRadius = 0.5
chordalErrorEnabled = false
chordalError = -1
secondOrder = false
optimize = true
quadAllowed = false
useSurfaceCurvature = true
fuseEdges = true
checkChartBoundary = false
localSizeOnShape.strips = 0.01
viscousLayers = false viscousLayers = false
thickness = [0.005, 0.0005] thickness = [0.005, 0.0005]
numberOfLayers = 1
stretchFactor = 1.2 [bodyCentered.mesh.submesh.strips]
isFacesToIgnore = true growthRate = 0.2
nbSegPerEdge = 2
nbSegPerRadius = 3
### ###
# Face-centered # Face-centered
## ##
[faceCentered.parameters]
theta = [0.01, 0.13, 0.01]
[faceCentered.geometry] [faceCentered.geometry]
theta = [0.01, 0.13, 0.01]
directions = [ directions = [
[1, 0, 0], [1, 0, 0],
[0, 0, 1], [0, 0, 1],
@ -107,28 +71,13 @@ directions = [
fillet = true fillet = true
[faceCentered.mesh] [faceCentered.mesh]
fineness = 3
minSize = 0.01
maxSize = 0.5
growthRate = 0.5
nbSegPerEdge = 0.5
nbSegPerRadius = 0.5
chordalErrorEnabled = false
chordalError = -1
secondOrder = false
optimize = true
quadAllowed = false
useSurfaceCurvature = true
fuseEdges = true
checkChartBoundary = false
localSizeOnShape.strips = 0.01
viscousLayers = false viscousLayers = false
thickness = [0.001, 0.0005] thickness = [0.001, 0.0005]
numberOfLayers = 1
stretchFactor = 1.2 [faceCentered.mesh.submesh.strips]
isFacesToIgnore = true growthRate = 0.2
nbSegPerEdge = 2
nbSegPerRadius = 3
### ###
# Flow # Flow

View File

@ -41,7 +41,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (inlet_); patches (smesh_inlet);
} }
{ {
@ -55,7 +55,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (outlet_); patches (smesh_outlet);
} }
{ {
@ -68,7 +68,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (wall_); patches (smesh_wall);
} }
{ {
@ -81,7 +81,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (symetry0_); patches (smesh_symetry0);
} }
{ {
@ -94,7 +94,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (symetry1_); patches (smesh_symetry1);
} }
{ {
@ -107,7 +107,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (symetry2_); patches (smesh_symetry2);
} }
{ {
@ -120,7 +120,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (symetry3_); patches (smesh_symetry3);
} }
{ {
@ -133,7 +133,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (symetry4_); patches (smesh_symetry4);
} }
{ {
@ -146,7 +146,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (symetry5_); patches (smesh_symetry5);
} }
{ {
@ -159,7 +159,7 @@ patches
} }
constructFrom patches; constructFrom patches;
patches (strips_); patches (smesh_strips);
} }

View File

@ -22,34 +22,64 @@ import toml
import logging import logging
from anisotropy.utils import struct from anisotropy.utils import struct
CONFIG = os.path.join(CASE, "task.toml")
config = struct(toml.load(CONFIG))
LOG = os.path.join(ROOT, "logs")
logging.basicConfig(
level = logging.INFO,
format = config.logger.format,
handlers = [
logging.StreamHandler(),
logging.FileHandler(f"{ LOG }/{ config.logger.name }.log")
]
)
logger = logging.getLogger(config.logger.name)
from salomepl.simple import simple from salomepl.simple import simple
from salomepl.faceCentered import faceCentered from salomepl.faceCentered import faceCentered
from salomepl.bodyCentered import bodyCentered from salomepl.bodyCentered import bodyCentered
from salomepl.geometry import getGeom from salomepl.geometry import getGeom
from salomepl.mesh import smeshBuilder, meshCreate, meshCompute, meshStats, meshExport from salomepl.mesh import Mesh, Fineness, ExtrusionMethod
def defaultParameters(**configParameters):
maxSize = 0.5
minSize = 0.05
fineness = Fineness.Custom.value
growthRate = 0.7
nbSegPerEdge = 0.3
nbSegPerRadius = 1
chordalErrorEnabled = True
chordalError = 0.25
secondOrder = False
optimize = True
quadAllowed = False
useSurfaceCurvature = True
fuseEdges = True
checkChartBoundary = False
viscousLayers = False
thickness = 0.005
numberOfLayers = 1
stretchFactor = 1
isFacesToIgnore = True
facesToIgnore = ["inlet", "outlet"]
faces = []
extrusionMethod = ExtrusionMethod.SURF_OFFSET_SMOOTH
p = locals()
del p["configParameters"]
if configParameters:
for k, v in p.items():
if configParameters.get(k) is not None:
p[k] = configParameters[k]
# Overwrite special values
if k == "fineness":
p["fineness"] = Fineness.__dict__[p["fineness"]].value
if k == "extrusionMethod":
p["extrusionMethod"] = ExtrusionMethod.__dict__[p["extrusionMethod"]]
return p
def genmesh(): def genmesh(config):
logger.info(f"""genmesh: logger.info(f"""genmesh:
structure type:\t{ config.structure } structure type:\t{ config.structure }
coefficient:\t{ config.parameters.theta } coefficient:\t{ config.geometry.theta }
fillet:\t{ config.geometry.fillet } fillet:\t{ config.geometry.fillet }
flow direction:\t{ config.geometry.direction }""") flow direction:\t{ config.geometry.direction }""")
@ -60,7 +90,7 @@ def genmesh():
## ##
geompy = getGeom() geompy = getGeom()
structure = globals().get(config.structure) structure = globals().get(config.structure)
shape, groups = structure(config.parameters.theta, config.geometry.fillet, config.geometry.direction) shape, groups = structure(config.geometry.theta, config.geometry.fillet, config.geometry.direction)
[length, surfaceArea, volume] = geompy.BasicProperties(shape, theTolerance = 1e-06) [length, surfaceArea, volume] = geompy.BasicProperties(shape, theTolerance = 1e-06)
logger.info(f"""shape: logger.info(f"""shape:
@ -71,31 +101,87 @@ def genmesh():
### ###
# Mesh # Mesh
## ##
facesToIgnore = [] config = dict(config)
mconfig = defaultParameters(**config["mesh"])
lengths = [ geompy.BasicProperties(edge)[0] for edge in geompy.SubShapeAll(shape, geompy.ShapeType["EDGE"]) ]
meanSize = sum(lengths) / len(lengths)
mconfig["maxSize"] = meanSize
mconfig["minSize"] = meanSize * 1e-1
mconfig["chordalError"] = mconfig["maxSize"] / 2
faces = []
for group in groups: for group in groups:
if group.GetName() in ["inlet", "outlet"]: if group.GetName() in mconfig["facesToIgnore"]:
facesToIgnore.append(group) faces.append(group)
meshParameters = config.mesh mconfig["faces"] = faces
meshParameters.facesToIgnore = facesToIgnore
meshParameters.extrusionMethod = smeshBuilder.SURF_OFFSET_SMOOTH mesh = Mesh(shape)
mesh.Tetrahedron(**mconfig)
if mconfig["viscousLayers"]:
mesh.ViscousLayers(**mconfig)
mesh = meshCreate(shape, meshParameters, groups) config["mesh"].update(mconfig)
returncode = meshCompute(mesh, groups) smconfigs = config["mesh"]["submesh"]
if returncode == 0: for name in smconfigs.keys():
config.status.mesh = True for group in groups:
if group.GetName() == name:
subshape = group
smconfig = defaultParameters(**smconfigs[name])
smconfig["maxSize"] = meanSize * 1e-1
smconfig["minSize"] = meanSize * 1e-3
smconfig["chordalError"] = smconfig["minSize"] * 1e+1
mesh.Triangle(subshape, **smconfig)
config["mesh"]["submesh"][name].update(smconfig)
with open(CONFIG, "w") as io:
toml.dump(dict(config), io)
meshStats(mesh) returncode, errors = mesh.compute()
meshExport(mesh, os.path.join(CASE, "mesh.unv"))
if not returncode:
config["status"]["mesh"] = True
else:
logger.error(errors)
with open(CONFIG, "w") as io:
toml.dump(config, io)
mesh.removePyramids()
mesh.assignGroups()
mesh.exportUNV(os.path.join(CASE, "mesh.unv"))
stats = ""
for k, v in mesh.stats().items():
stats += f"{ k }:\t\t{ v }\n"
logger.info(f"mesh stats:\n{ stats[ :-1] }")
salome.salome_close() salome.salome_close()
if __name__ == "__main__": if __name__ == "__main__":
genmesh() CONFIG = os.path.join(CASE, "task.toml")
config = struct(toml.load(CONFIG))
LOG = os.path.join(ROOT, "logs")
logging.basicConfig(
level = logging.INFO,
format = config.logger.format,
handlers = [
logging.StreamHandler(),
logging.FileHandler(f"{ LOG }/{ config.logger.name }.log")
]
)
logger = logging.getLogger(config.logger.name)
genmesh(config)

View File

@ -2,201 +2,151 @@ import SMESH
from salome.smesh import smeshBuilder from salome.smesh import smeshBuilder
smesh = smeshBuilder.New() smesh = smeshBuilder.New()
import logging import enum
logger = logging.getLogger("anisotropy")
class Fineness(enum.Enum):
VeryCoarse = 0
Coarse = 1
Moderate = 2
Fine = 3
VeryFine = 4
Custom = 5
class ExtrusionMethod(object):
SURF_OFFSET_SMOOTH = smeshBuilder.SURF_OFFSET_SMOOTH
FACE_OFFSET = smeshBuilder.FACE_OFFSET
NODE_OFFSET = smeshBuilder.NODE_OFFSET
def getSmesh(): def getSmesh():
return smesh return smesh
def updateParams(old, new: dict):
old.SetMaxSize(new.get("maxSize") if new.get("maxSize") else old.GetMaxSize())
old.SetMinSize(new.get("minSize") if new.get("minSize") else old.GetMinSize())
def meshCreate(shape, parameters, groups): #fineness, parameters, viscousLayers = None): old.SetFineness(new.get("fineness") if new.get("fineness") else old.GetFineness())
""" old.SetGrowthRate(new.get("growthRate") if new.get("growthRate") else old.GetGrowthRate())
Creates a mesh from a geometry. old.SetNbSegPerEdge(new.get("nbSegPerEdge") if new.get("nbSegPerEdge") else old.GetNbSegPerEdge())
old.SetNbSegPerRadius(new.get("nbSegPerRadius") if new.get("nbSegPerRadius") else old.GetNbSegPerRadius())
Parameters: old.SetChordalErrorEnabled(new.get("chordalErrorEnabled") if new.get("chordalErrorEnabled") else old.GetChordalErrorEnabled())
fineness (int): Fineness of mesh. old.SetChordalError(new.get("chordalError") if new.get("chordalError") else old.GetChordalError())
0 - Very coarse, old.SetSecondOrder(new.get("secondOrder") if new.get("secondOrder") else old.GetSecondOrder())
1 - Coarse, old.SetOptimize(new.get("optimize") if new.get("optimize") else old.GetOptimize())
2 - Moderate, old.SetQuadAllowed(new.get("quadAllowed") if new.get("quadAllowed") else old.GetQuadAllowed())
3 - Fine, old.SetUseSurfaceCurvature(new.get("useSurfaceCurvature") if new.get("useSurfaceCurvature") else old.GetUseSurfaceCurvature())
4 - Very fine. old.SetFuseEdges(new.get("fuseEdges") if new.get("fuseEdges") else old.GetFuseEdges())
old.SetCheckChartBoundary(new.get("checkChartBoundary") if new.get("checkChartBoundary") else old.GetCheckChartBoundary())
Returns:
Configured instance of class <SMESH.SMESH_Mesh>, containig the parameters and boundary groups.
""" class Mesh(object):
### def __init__(self, shape, name = ""):
# Netgen self.name = name if name else shape.GetName()
## self.mesh = smesh.Mesh(shape, self.name)
Fineness = { self.geom = shape
0: "Very coarse", self.algo = None
1: "Coarse", self.params = None
2: "Moderate", self.viscousLayers = None
3: "Fine",
4: "Very fine",
5: "Custom"
}[parameters.fineness]
# Mesh self.submeshes = []
mesh = smesh.Mesh(shape)
netgen = mesh.Tetrahedron(algo=smeshBuilder.NETGEN_1D2D3D)
# Parameters
param = netgen.Parameters()
param.SetMinSize(parameters.minSize)
param.SetMaxSize(parameters.maxSize)
param.SetFineness(parameters.fineness)
if parameters.fineness == 5: def Tetrahedron(self, **kwargs):
param.SetGrowthRate(parameters.growthRate) self.algo = self.mesh.Tetrahedron(algo = smeshBuilder.NETGEN_1D2D3D)
param.SetNbSegPerEdge(parameters.nbSegPerEdge) self.params = self.algo.Parameters()
param.SetNbSegPerRadius(parameters.nbSegPerRadius)
param.SetChordalErrorEnabled(parameters.chordalErrorEnabled)
param.SetChordalError(parameters.chordalError)
param.SetSecondOrder(parameters.secondOrder) self.params = updateParams(self.params, kwargs)
param.SetOptimize(parameters.optimize)
param.SetQuadAllowed(parameters.quadAllowed)
param.SetUseSurfaceCurvature(parameters.useSurfaceCurvature)
param.SetFuseEdges(parameters.fuseEdges)
param.SetCheckChartBoundary(parameters.checkChartBoundary)
logger.info("""meshCreate:
fineness:\t{}
min size:\t{}
max size:\t{}
growth rate:\t{}
nb segs per edge:\t{}
nb segs per radius:\t{}
limit size by surface curvature:\t{}
quad-dominated:\t{}
second order:\t{}
optimize:\t{}""".format(
Fineness, param.GetMinSize(), param.GetMaxSize(),
param.GetGrowthRate(), param.GetNbSegPerEdge(), param.GetNbSegPerRadius(),
True if param.GetUseSurfaceCurvature() else False,
True if param.GetQuadAllowed() else False,
True if param.GetSecondOrder() else False,
True if param.GetOptimize() else False))
def ViscousLayers(self,
### thickness = 1,
# Local sizes numberOfLayers = 1,
## stretchFactor = 0,
for group in groups: faces = [],
localSize = parameters.localSizeOnShape.__dict__.get(group) isFacesToIgnore = True,
extrMethod = ExtrusionMethod.SURF_OFFSET_SMOOTH,
if localSize: **kwargs
param.SetLocalSizeOnShape(group, localSize) ):
### self.viscousLayers = self.algo.ViscousLayers(
# Viscous layers thickness,
## numberOfLayers,
if parameters.viscousLayers: stretchFactor,
vlayer = netgen.ViscousLayers( faces,
parameters.thickness, isFacesToIgnore,
parameters.numberOfLayers, extrMethod
parameters.stretchFactor,
parameters.facesToIgnore,
parameters.isFacesToIgnore,
parameters.extrusionMethod
) )
logger.info("""meshCreate: def Triangle(self, subshape, **kwargs):
viscous layers: submesh = Submesh(self.mesh, subshape)
thickness:\t{} submesh.algo = self.mesh.Triangle(algo = smeshBuilder.NETGEN_1D2D, geom = subshape)
number:\t{} submesh.mesh = submesh.algo.subm
stretch factor:\t{}""".format( submesh.params = submesh.algo.Parameters()
parameters.thickness,
parameters.numberOfLayers,
parameters.stretchFactor))
else: submesh.params = updateParams(submesh.params, kwargs)
logger.info("""meshCreate:
viscous layers: false""")
return mesh self.submeshes.append(submesh)
def meshCompute(mobj, groups):
"""Compute the mesh."""
status = mobj.Compute()
if status: def assignGroups(self, withPrefix = True):
logger.info("meshCompute: computed") prefix = "smesh_" if withPrefix else ""
### for group in self.mesh.geompyD.GetGroups(self.geom):
# Post computing if group.GetName():
## self.mesh.GroupOnGeom(group, f"{ prefix }{ group.GetName() }", SMESH.FACE)
if mobj.NbPyramids() > 0:
logger.info(f"meshCompute: detected {mobj.NbPyramids()} pyramids: splitting volumes into tetrahedrons") def compute(self):
isDone = self.mesh.Compute()
returncode = int(not isDone)
errors = self.mesh.GetComputeErrors()
return returncode, errors
def stats(self):
return {
"elements": self.mesh.NbElements(),
"edges": self.mesh.NbEdges(),
"faces": self.mesh.NbFaces(),
"volumes": self.mesh.NbVolumes(),
"tetrahedrons": self.mesh.NbTetras(),
"prisms": self.mesh.NbPrisms(),
"pyramids": self.mesh.NbPyramids()
}
def exportUNV(self, path):
returncode = 0
error = ""
try:
self.mesh.ExportUNV(path)
except Exception as e:
error = e.details.text
returncode = 1
return returncode, error
def removePyramids(self):
if self.mesh.NbPyramids() > 0:
pyramidCriterion = smesh.GetCriterion( pyramidCriterion = smesh.GetCriterion(
SMESH.VOLUME, SMESH.VOLUME,
SMESH.FT_ElemGeomType, SMESH.FT_ElemGeomType,
SMESH.FT_Undefined, SMESH.FT_Undefined,
SMESH.Geom_PYRAMID SMESH.Geom_PYRAMID
) )
pyramidGroup = mobj.MakeGroupByCriterion("pyramids", pyramidCriterion) pyramidGroup = self.mesh.MakeGroupByCriterion("pyramids", pyramidCriterion)
pyramidVolumes = mobj.GetIDSource(pyramidGroup.GetIDs(), SMESH.VOLUME) pyramidVolumes = self.mesh.GetIDSource(pyramidGroup.GetIDs(), SMESH.VOLUME)
mobj.SplitVolumesIntoTetra(pyramidVolumes, smesh.Hex_5Tet) self.mesh.SplitVolumesIntoTetra(pyramidVolumes, smesh.Hex_5Tet)
mobj.RemoveGroup(pyramidGroup) self.mesh.RemoveGroup(pyramidGroup)
mobj.RenumberElements() self.mesh.RenumberElements()
###
# Groups
##
for group in groups:
mobj.GroupOnGeom(group, f"{ group.GetName() }_", SMESH.FACE)
else:
logger.warning("meshCompute: not computed")
return not status
def meshStats(mobj):
"""
Print mesh information.
"""
stats = {
"Elements": mobj.NbElements(),
"Edges": mobj.NbEdges(),
"Faces": mobj.NbFaces(),
"Volumes": mobj.NbVolumes(),
"Tetrahedrons": mobj.NbTetras(),
"Prisms": mobj.NbPrisms(),
"Pyramids": mobj.NbPyramids()
}
info = "meshStats:\n"
for key in stats: class Submesh(object):
info += f"\t{key}:\t{stats[key]}\n" def __init__(self, father, subshape, name = ""):
self.name = name if name else subshape.GetName()
logger.info(info) self.mesh = None
self.geom = subshape
self.algo = None
def meshExport(mobj, path): self.params = None
"""
Export the mesh in a file in UNV format.
Parameters:
path (string): full path to the expected directory.
"""
try:
mobj.ExportUNV(path)
logger.info("""meshExport:
format:\t{}""".format("unv"))
except:
logger.error("""meshExport: Cannot export.""")

View File

@ -20,11 +20,11 @@ def startServer(port):
return p return p
def salomeVersion() -> str: def salomeVersion() -> str:
return "Salome 9.6.0" return "Salome 9.7.0 MPI"
def runExecute(port: int, scriptpath: str, *args) -> int: def runExecute(port: int, scriptpath: str, *args) -> int:
cmd = ["salome", "start", "--shutdown-servers=1", "--port", str(port), "-t", cmd = ["salome-9.7.0-mpi", "start", "--shutdown-servers=1", "--port", str(port), "-t",
scriptpath, "args:{}".format(", ".join([str(arg) for arg in args]))] scriptpath, "args:{}".format(", ".join([str(arg) for arg in args]))]
logger.info("salome: {}".format(cmd[1 : 6])) logger.info("salome: {}".format(cmd[1 : 6]))