Mod: improved mesh and shape classes
Mod: exception handling
This commit is contained in:
parent
d724b45e85
commit
7dce03e97a
@ -396,11 +396,5 @@ def show(params, path, printlist, export, fields, output):
|
|||||||
# CLI entry
|
# CLI entry
|
||||||
##
|
##
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
#try:
|
anisotropy()
|
||||||
anisotropy()
|
|
||||||
|
|
||||||
#except KeyboardInterrupt:
|
|
||||||
# click.echo("Interrupted!")
|
|
||||||
|
|
||||||
#finally:
|
|
||||||
# sys.exit(0)
|
|
||||||
|
@ -4,14 +4,15 @@
|
|||||||
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import os
|
import os
|
||||||
from os import path
|
from os import path, PathLike
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from anisotropy.core.config import DefaultConfig
|
from anisotropy.core.config import DefaultConfig
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from anisotropy.core.postProcess import PostProcess
|
from anisotropy.core.postProcess import PostProcess
|
||||||
from anisotropy.core.utils import Timer
|
from anisotropy.core.utils import Timer, ErrorHandler
|
||||||
from anisotropy.core.parallel import ParallelRunner
|
from anisotropy.core.parallel import ParallelRunner
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -22,6 +23,7 @@ from anisotropy.shaping import Simple, BodyCentered, FaceCentered, Shape
|
|||||||
from anisotropy.meshing import Mesh
|
from anisotropy.meshing import Mesh
|
||||||
from anisotropy.solving import OnePhaseFlow
|
from anisotropy.solving import OnePhaseFlow
|
||||||
|
|
||||||
|
|
||||||
class UltimateRunner(object):
|
class UltimateRunner(object):
|
||||||
def __init__(self, config = None, exec_id: int = None, typo: str = "master"):
|
def __init__(self, config = None, exec_id: int = None, typo: str = "master"):
|
||||||
# Configuration file
|
# Configuration file
|
||||||
@ -46,18 +48,9 @@ class UltimateRunner(object):
|
|||||||
self.exec_id = T.Execution.create(date = datetime.now())
|
self.exec_id = T.Execution.create(date = datetime.now())
|
||||||
|
|
||||||
# Parameters
|
# Parameters
|
||||||
self.shape = None
|
|
||||||
self.mesh = None
|
|
||||||
self.flow = None
|
|
||||||
|
|
||||||
self.queue = []
|
self.queue = []
|
||||||
|
|
||||||
|
|
||||||
def dispose(self):
|
|
||||||
self.shape = None
|
|
||||||
self.mesh = None
|
|
||||||
self.flow = None
|
|
||||||
|
|
||||||
def createRow(self):
|
def createRow(self):
|
||||||
# create a row in each table for the current case
|
# create a row in each table for the current case
|
||||||
with self.database:
|
with self.database:
|
||||||
@ -81,6 +74,7 @@ class UltimateRunner(object):
|
|||||||
flow = T.FlowOnephase(mesh_id = mesh.mesh_id, **self.config.params)
|
flow = T.FlowOnephase(mesh_id = mesh.mesh_id, **self.config.params)
|
||||||
self.database.csave(mesh)
|
self.database.csave(mesh)
|
||||||
|
|
||||||
|
|
||||||
def fill(self):
|
def fill(self):
|
||||||
self.config.expand()
|
self.config.expand()
|
||||||
logger.info(f"Preparing queue: { len(self.config.cases) }")
|
logger.info(f"Preparing queue: { len(self.config.cases) }")
|
||||||
@ -96,7 +90,7 @@ class UltimateRunner(object):
|
|||||||
}
|
}
|
||||||
self.queue.append(kwargs)
|
self.queue.append(kwargs)
|
||||||
|
|
||||||
|
|
||||||
def start(self, queue: list = None, nprocs: int = None):
|
def start(self, queue: list = None, nprocs: int = None):
|
||||||
nprocs = nprocs or self.config["nprocs"]
|
nprocs = nprocs or self.config["nprocs"]
|
||||||
|
|
||||||
@ -109,17 +103,22 @@ class UltimateRunner(object):
|
|||||||
|
|
||||||
parallel.wait()
|
parallel.wait()
|
||||||
|
|
||||||
def casepath(self):
|
|
||||||
|
@property
|
||||||
|
def casepath(self) -> PathLike:
|
||||||
params = self.config.params
|
params = self.config.params
|
||||||
|
path = Path(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_BUILD_DIR"])
|
||||||
|
path /= "execution-{}".format(self.exec_id)
|
||||||
|
path /= "{}-[{},{},{}]-{}".format(
|
||||||
|
params["label"],
|
||||||
|
*[ str(d) for d in params["direction"] ],
|
||||||
|
params["alpha"]
|
||||||
|
)
|
||||||
|
|
||||||
execution = "execution-{}".format(self.exec_id)
|
return path.resolve()
|
||||||
case = "{}-[{},{},{}]-{}".format(params["label"], *[ str(d) for d in params["direction"] ], params["alpha"])
|
|
||||||
dirpath = path.join(os.environ["ANISOTROPY_CWD"], self.config["build"], execution, case)
|
|
||||||
|
|
||||||
return path.abspath(dirpath)
|
|
||||||
|
|
||||||
def computeShape(self):
|
def computeShape(self):
|
||||||
out, err, returncode = "", "", 0
|
|
||||||
params = self.config.params
|
params = self.config.params
|
||||||
shapeParams = self.database.getShape(
|
shapeParams = self.database.getShape(
|
||||||
params["label"],
|
params["label"],
|
||||||
@ -131,54 +130,48 @@ class UltimateRunner(object):
|
|||||||
logger.info("Computing shape for {} with direction = {} and alpha = {}".format(
|
logger.info("Computing shape for {} with direction = {} and alpha = {}".format(
|
||||||
params["label"], params["direction"], params["alpha"]
|
params["label"], params["direction"], params["alpha"]
|
||||||
))
|
))
|
||||||
filename = "shape.step"
|
shapeFile = self.casepath / "shape.step"
|
||||||
shapepath = path.join(self.casepath(), filename)
|
|
||||||
timer = Timer()
|
timer = Timer()
|
||||||
|
|
||||||
if path.exists(shapepath) and shapeParams.shapeStatus == "done" and not self.config["overwrite"]:
|
if shapeFile.exists() and shapeParams.shapeStatus == "done" and not self.config["overwrite"]:
|
||||||
logger.info("Shape exists. Skipping ...")
|
logger.info("Shape exists. Skipping ...")
|
||||||
return
|
return
|
||||||
|
|
||||||
shape = {
|
shapeSelected = {
|
||||||
"simple": Simple,
|
"simple": Simple,
|
||||||
"bodyCentered": BodyCentered,
|
"bodyCentered": BodyCentered,
|
||||||
"faceCentered": FaceCentered
|
"faceCentered": FaceCentered
|
||||||
}[shapeParams.label]
|
}[shapeParams.label]
|
||||||
|
|
||||||
self.shape = shape(
|
shape = shapeSelected(
|
||||||
direction = shapeParams.direction,
|
direction = shapeParams.direction,
|
||||||
alpha = shapeParams.alpha,
|
alpha = shapeParams.alpha,
|
||||||
r0 = shapeParams.r0,
|
r0 = shapeParams.r0,
|
||||||
filletsEnabled = shapeParams.filletsEnabled
|
filletsEnabled = shapeParams.filletsEnabled
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
with ErrorHandler() as (eh, handler):
|
||||||
self.shape.build()
|
handler(shape.build)()
|
||||||
|
|
||||||
except Exception as e:
|
if not eh.returncode:
|
||||||
err = e
|
self.casepath.mkdir(exist_ok = True)
|
||||||
returncode = 1
|
|
||||||
|
|
||||||
if not returncode:
|
with ErrorHandler() as (eh, handler):
|
||||||
os.makedirs(self.casepath(), exist_ok = True)
|
handler(shape.write)(shapeFile)
|
||||||
out, err, returncode = self.shape.export(path.join(self.casepath(), filename))
|
|
||||||
|
|
||||||
if not returncode:
|
if not eh.returncode:
|
||||||
shapeParams.shapeStatus = "done"
|
shapeParams.shapeStatus = "done"
|
||||||
|
shapeParams.volume = shape.shape.volume
|
||||||
shapeParams.volume = self.shape.shape.volume
|
shapeParams.volumeCell = shape.cell.volume
|
||||||
shapeParams.volumeCell = self.shape.cell.volume
|
|
||||||
shapeParams.porosity = shapeParams.volume / shapeParams.volumeCell
|
shapeParams.porosity = shapeParams.volume / shapeParams.volumeCell
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logger.error(err)
|
|
||||||
shapeParams.shapeStatus = "failed"
|
shapeParams.shapeStatus = "failed"
|
||||||
|
logger.error(eh.error)
|
||||||
|
|
||||||
#with self.database:
|
|
||||||
shapeParams.shapeExecutionTime = timer.elapsed()
|
shapeParams.shapeExecutionTime = timer.elapsed()
|
||||||
#shapeParams.save()
|
|
||||||
self.database.csave(shapeParams)
|
self.database.csave(shapeParams)
|
||||||
self.dispose()
|
|
||||||
|
|
||||||
def computeMesh(self):
|
def computeMesh(self):
|
||||||
out, err, returncode = "", "", 0
|
out, err, returncode = "", "", 0
|
||||||
@ -193,40 +186,45 @@ class UltimateRunner(object):
|
|||||||
logger.info("Computing mesh for {} with direction = {} and alpha = {}".format(
|
logger.info("Computing mesh for {} with direction = {} and alpha = {}".format(
|
||||||
params["label"], params["direction"], params["alpha"]
|
params["label"], params["direction"], params["alpha"]
|
||||||
))
|
))
|
||||||
filename = "mesh.mesh"
|
meshFile = self.casepath / "mesh.mesh"
|
||||||
meshpath = path.join(self.casepath(), filename)
|
|
||||||
timer = Timer()
|
timer = Timer()
|
||||||
|
|
||||||
if path.exists(meshpath) and meshParams.meshStatus == "done" and not self.config["overwrite"]:
|
if meshFile.exists() and meshParams.meshStatus == "done" and not self.config["overwrite"]:
|
||||||
logger.info("Mesh exists. Skipping ...")
|
logger.info("Mesh exists. Skipping ...")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not self.shape:
|
# Shape
|
||||||
shapefile = "shape.step"
|
shape = None
|
||||||
filepath = path.join(self.casepath(), shapefile)
|
shapeFile = self.casepath / "shape.step"
|
||||||
|
|
||||||
if not path.exists(filepath) and not path.isfile(filepath):
|
if not shapeFile.exists() and not shapeFile.is_file():
|
||||||
err = f"File not found: { filepath }"
|
err = f"File not found: { shapeFile }"
|
||||||
returncode = 2
|
returncode = 2
|
||||||
|
|
||||||
if not returncode:
|
|
||||||
self.shape = Shape()
|
|
||||||
self.shape.load(filepath)
|
|
||||||
|
|
||||||
if not returncode:
|
if not returncode:
|
||||||
self.mesh = Mesh(self.shape.shape)
|
shape = Shape().read(shapeFile)
|
||||||
|
|
||||||
|
# Mesh
|
||||||
|
if not returncode:
|
||||||
|
mesh = Mesh(shape.shape)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.mesh.build()
|
mesh.generate()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
err = e
|
err = e
|
||||||
returncode = 1
|
returncode = 1
|
||||||
|
|
||||||
if not returncode:
|
if not returncode:
|
||||||
os.makedirs(self.casepath(), exist_ok = True)
|
self.casepath.mkdir(exist_ok = True)
|
||||||
out, err, returncode = self.mesh.export(path.join(self.casepath(), filename))
|
|
||||||
out, err, returncode = self.mesh.export(path.join(self.casepath(), "mesh.msh"))
|
try:
|
||||||
|
mesh.write(meshFile)
|
||||||
|
mesh.write(self.casepath / "mesh.msh")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
err = e
|
||||||
|
returncode = 1
|
||||||
|
|
||||||
if not returncode:
|
if not returncode:
|
||||||
meshParams.meshStatus = "done"
|
meshParams.meshStatus = "done"
|
||||||
@ -241,7 +239,7 @@ class UltimateRunner(object):
|
|||||||
with self.database:
|
with self.database:
|
||||||
meshParams.meshExecutionTime = timer.elapsed()
|
meshParams.meshExecutionTime = timer.elapsed()
|
||||||
meshParams.save()
|
meshParams.save()
|
||||||
self.dispose()
|
|
||||||
|
|
||||||
def computeFlow(self):
|
def computeFlow(self):
|
||||||
params = self.config.params
|
params = self.config.params
|
||||||
@ -261,37 +259,33 @@ class UltimateRunner(object):
|
|||||||
flowParams.viscosityKinematic = flowParams.viscosity / flowParams.density
|
flowParams.viscosityKinematic = flowParams.viscosity / flowParams.density
|
||||||
|
|
||||||
with self.database:
|
with self.database:
|
||||||
flowParams.save()
|
flowParams.save()
|
||||||
|
|
||||||
|
|
||||||
with self.database:
|
with self.database:
|
||||||
self.flow = OnePhaseFlow(
|
flow = OnePhaseFlow(
|
||||||
direction = params["direction"],
|
direction = params["direction"],
|
||||||
**self.database.getFlowOnephase(*query, to_dict = True),
|
**self.database.getFlowOnephase(*query, to_dict = True),
|
||||||
path = self.casepath()
|
path = self.casepath
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.shape:
|
# Shape
|
||||||
filename = "shape.step"
|
shapeFile = self.casepath / "shape.step"
|
||||||
filepath = path.join(self.casepath(), filename)
|
|
||||||
|
|
||||||
if not path.exists(filepath) and not path.isfile(filepath):
|
if not shapeFile.exists() and not shapeFile.is_file():
|
||||||
err = f"File not found: { filepath }"
|
err = f"File not found: { shapeFile }"
|
||||||
returncode = 2
|
returncode = 2
|
||||||
|
|
||||||
if not returncode:
|
if not returncode:
|
||||||
self.shape = Shape()
|
shape = Shape().read(shapeFile)
|
||||||
self.shape.load(filepath)
|
|
||||||
|
|
||||||
faces = [ (n, face.name) for n, face in enumerate(self.shape.shape.faces) ]
|
# Patches from occ to openfoam
|
||||||
createPatchDict = OnePhaseFlow.facesToPatches(faces)
|
patches = shape.patches(group = True, shiftIndex = True, prefix = "patch")
|
||||||
|
flow.createPatches(patches)
|
||||||
self.flow.append(createPatchDict)
|
flow.write()
|
||||||
self.flow.write()
|
|
||||||
|
|
||||||
# Build a flow
|
# Build a flow
|
||||||
try:
|
try:
|
||||||
out, err, returncode = self.flow.build()
|
out, err, returncode = flow.build()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
out, err, returncode = "", e, 1
|
out, err, returncode = "", e, 1
|
||||||
@ -308,6 +302,7 @@ class UltimateRunner(object):
|
|||||||
flowParams.flowExecutionTime = timer.elapsed()
|
flowParams.flowExecutionTime = timer.elapsed()
|
||||||
flowParams.save()
|
flowParams.save()
|
||||||
|
|
||||||
|
|
||||||
def computePostProcess(self):
|
def computePostProcess(self):
|
||||||
params = self.config.params
|
params = self.config.params
|
||||||
flowParams = self.database.getFlowOnephase(
|
flowParams = self.database.getFlowOnephase(
|
||||||
@ -321,7 +316,7 @@ class UltimateRunner(object):
|
|||||||
params["label"], params["direction"], params["alpha"]
|
params["label"], params["direction"], params["alpha"]
|
||||||
))
|
))
|
||||||
|
|
||||||
postProcess = PostProcess(self.casepath())
|
postProcess = PostProcess(self.casepath)
|
||||||
|
|
||||||
if flowParams.flowStatus == "done":
|
if flowParams.flowStatus == "done":
|
||||||
flowParams.flowRate = postProcess.flowRate("outlet")
|
flowParams.flowRate = postProcess.flowRate("outlet")
|
||||||
|
@ -6,6 +6,7 @@ import logging
|
|||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
from types import FunctionType
|
from types import FunctionType
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
|
||||||
class CustomFormatter(logging.Formatter):
|
class CustomFormatter(logging.Formatter):
|
||||||
@ -206,3 +207,33 @@ class Timer(object):
|
|||||||
|
|
||||||
def elapsed(self):
|
def elapsed(self):
|
||||||
return time.monotonic() - self.start
|
return time.monotonic() - self.start
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorHandler(contextlib.AbstractContextManager):
|
||||||
|
def __init__(self):
|
||||||
|
self.error = ""
|
||||||
|
self.returncode = 0
|
||||||
|
self.traceback = None
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self, self.handler
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if exc_type:
|
||||||
|
self.error = exc_value.args
|
||||||
|
self.returncode = 1
|
||||||
|
self.traceback = traceback
|
||||||
|
|
||||||
|
def handle(self, obj):
|
||||||
|
def inner(*args, **kwargs):
|
||||||
|
try:
|
||||||
|
output = obj(*args, **kwargs)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.error = e.args
|
||||||
|
self.returncode = 1
|
||||||
|
|
||||||
|
else:
|
||||||
|
return output
|
||||||
|
|
||||||
|
return inner
|
||||||
|
@ -269,7 +269,7 @@ def plotDraw(clicks, execution, structure, direction, data):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not direction == "all":
|
if not direction == "all":
|
||||||
query = qeury.where(models.Shape.direction == json.loads(direction))
|
query = query.where(models.Shape.direction == json.loads(direction))
|
||||||
|
|
||||||
with db:
|
with db:
|
||||||
if query.exists():
|
if query.exists():
|
||||||
|
@ -4,9 +4,11 @@
|
|||||||
|
|
||||||
from netgen.occ import OCCGeometry
|
from netgen.occ import OCCGeometry
|
||||||
from netgen import meshing
|
from netgen import meshing
|
||||||
from numpy import array
|
import numpy
|
||||||
|
from numpy import array, ndarray, linalg
|
||||||
import os
|
import os
|
||||||
from .utils import extractPoints, extractCells
|
from .utils import extractNetgenPoints, extractNetgenCells
|
||||||
|
from . import metrics
|
||||||
import meshio
|
import meshio
|
||||||
|
|
||||||
|
|
||||||
@ -19,154 +21,130 @@ class NotSupportedMeshFormat(Exception):
|
|||||||
def __init__(self, msg):
|
def __init__(self, msg):
|
||||||
super().__init__(self, msg)
|
super().__init__(self, msg)
|
||||||
|
|
||||||
|
|
||||||
|
class MeshingParameters(object):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.preset(2, **kwargs)
|
||||||
|
|
||||||
|
def preset(self, key: int, **kwargs):
|
||||||
|
"""Apply predefined parameters.
|
||||||
|
|
||||||
|
:param key:
|
||||||
|
0: very_coarse
|
||||||
|
1: coarse
|
||||||
|
2: moderate
|
||||||
|
3: fine
|
||||||
|
4: very_fine
|
||||||
|
"""
|
||||||
|
self.maxh = kwargs.get("maxh", 0.2)
|
||||||
|
self.curvaturesafety = kwargs.get("curvaturesafety", [1, 1.5, 2, 3, 5][key])
|
||||||
|
self.segmentsperedge = kwargs.get("segmentsperedge", [0.3, 0.5, 1, 2, 3][key])
|
||||||
|
self.grading = kwargs.get("grading", [0.7, 0.5, 0.3, 0.2, 0.1][key])
|
||||||
|
self.chartdistfac = kwargs.get("chartdistfac", [0.8, 1, 1.5, 2, 5][key])
|
||||||
|
self.linelengthfac = kwargs.get("linelengthfac", [0.2, 0.35, 0.5, 1.5, 3][key])
|
||||||
|
self.closeedgefac = kwargs.get("closeedgefac", [0.5, 1, 2, 3.5, 5][key])
|
||||||
|
self.minedgelen = kwargs.get("minedgelen", [0.002, 0.02, 0.2, 1.0, 2.0][key])
|
||||||
|
self.surfmeshcurvfac = kwargs.get("surfmeshcurvfac", [1, 1.5, 2, 3, 5.0][key])
|
||||||
|
self.optsteps2d = kwargs.get("optsteps2d", 5)
|
||||||
|
self.optsteps3d = kwargs.get("optsetps3d", 5)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
|
def get(self) -> meshing.MeshingParameters:
|
||||||
|
return meshing.MeshingParameters(**self.__dict__)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return str(self.__dict__)
|
||||||
|
|
||||||
|
|
||||||
class Mesh(object):
|
class Mesh(object):
|
||||||
def __init__(self, shape: OCCGeometry = None):
|
def __init__(self, shape = None):
|
||||||
self.geometry = OCCGeometry(shape) if shape else None
|
self.shape = shape
|
||||||
self.mesh = None
|
self.mesh = None
|
||||||
|
|
||||||
# Parameters
|
self.points = []
|
||||||
self.maxh = 0.2
|
self.cells = []
|
||||||
self.curvaturesafety = 5
|
self.boundary = []
|
||||||
self.segmentsperedge = 3
|
|
||||||
self.grading = 0.1
|
|
||||||
self.chartdistfac = 5
|
|
||||||
self.linelengthfac = 3
|
|
||||||
self.closeedgefac = 5
|
|
||||||
self.minedgelen = 2.0
|
|
||||||
self.surfmeshcurvfac = 5.0
|
|
||||||
self.optsteps2d = 5
|
|
||||||
self.optsteps3d = 5
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def parameters(self):
|
def geometry(self):
|
||||||
return meshing.MeshingParameters(
|
return OCCGeometry(self.shape)
|
||||||
maxh = self.maxh,
|
|
||||||
curvaturesafety = self.curvaturesafety,
|
|
||||||
segmentsperedge = self.segmentsperedge,
|
|
||||||
grading = self.grading,
|
|
||||||
chartdistfac = self.chartdistfac,
|
|
||||||
linelengthfac = self.linelengthfac,
|
|
||||||
closeedgefac = self.closeedgefac,
|
|
||||||
minedgelen = self.minedgelen,
|
|
||||||
surfmeshcurvfac = self.surfmeshcurvfac,
|
|
||||||
optsteps2d = self.optsteps2d,
|
|
||||||
optsteps3d = self.optsteps3d
|
|
||||||
)
|
|
||||||
|
|
||||||
def build(self):
|
def generate(self, parameters: MeshingParameters = None, refineSteps: int = 0, scale: float = 0):
|
||||||
if self.geometry:
|
if not self.geometry:
|
||||||
self.mesh = self.geometry.GenerateMesh(self.parameters)
|
raise NoGeometrySpecified("Cannot build mesh without geometry")
|
||||||
|
|
||||||
|
parameters = parameters or MeshingParameters()
|
||||||
|
mesh = self.geometry.GenerateMesh(parameters.get())
|
||||||
|
|
||||||
else:
|
if refineSteps > 0:
|
||||||
raise NoGeometrySpecified("Specify a geometry to build a mesh")
|
for n in range(refineSteps):
|
||||||
|
mesh.Refine()
|
||||||
|
|
||||||
|
mesh.OptimizeMesh2d(parameters)
|
||||||
|
mesh.OptimizeVolumeMesh(parameters)
|
||||||
|
|
||||||
|
if scale > 0:
|
||||||
|
mesh.Scale(scale)
|
||||||
|
|
||||||
|
self.points = extractNetgenPoints(mesh)
|
||||||
|
self.cells = []
|
||||||
|
|
||||||
formats = {
|
for dim in range(4):
|
||||||
"vol": "Netgen Format",
|
self.cells.extend(extractNetgenCells(dim, mesh))
|
||||||
"mesh": "Neutral Format",
|
|
||||||
"msh": "Gmsh2 Format"
|
|
||||||
}
|
|
||||||
|
|
||||||
def load(self, filename: str):
|
return self
|
||||||
|
|
||||||
|
|
||||||
|
def read(self, filename: str):
|
||||||
"""Import a mesh.
|
"""Import a mesh.
|
||||||
|
|
||||||
Use `Mesh.formats` to see supported formats.
|
|
||||||
|
|
||||||
:param filename:
|
:param filename:
|
||||||
Name of the file to store the given mesh in.
|
Name of the mesh file.
|
||||||
"""
|
"""
|
||||||
ext = os.path.splitext(filename)[1][1: ]
|
mesh = meshio.read(filename)
|
||||||
|
|
||||||
if ext in self.formats.keys():
|
self.points = mesh.points
|
||||||
self.mesh = meshing.Mesh()
|
self.cells = mesh.cells
|
||||||
self.mesh.Load(filename)
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise NotSupportedMeshFormat(f"Mesh format '{ ext }' is not supported")
|
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def export(self, filename: str):
|
def write(self, filename: str):
|
||||||
"""Export a mesh.
|
"""Export a mesh.
|
||||||
|
|
||||||
Use `Mesh.formats` to see supported formats.
|
|
||||||
|
|
||||||
:param filename:
|
:param filename:
|
||||||
Name of the file to store the given mesh in.
|
Name of the file to store the given mesh in.
|
||||||
|
"""
|
||||||
|
mesh = meshio.Mesh(self.points, self.cells)
|
||||||
|
mesh.write(filename)
|
||||||
|
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volumes(self) -> list[ndarray]:
|
||||||
|
points = []
|
||||||
|
|
||||||
|
for cellBlock in self.cells:
|
||||||
|
if cellBlock.dim == 3:
|
||||||
|
points.extend([ *self.points[cellBlock.data] ])
|
||||||
|
|
||||||
|
return points
|
||||||
|
|
||||||
|
@property
|
||||||
|
def volume(self) -> float:
|
||||||
|
"""Volume of whole mesh.
|
||||||
|
|
||||||
:return:
|
:return:
|
||||||
Output, error messages and returncode
|
Volume.
|
||||||
"""
|
"""
|
||||||
out, err, returncode = "", "", 0
|
return sum([ metrics.volume(cell) for cell in self.volumes ])
|
||||||
ext = os.path.splitext(filename)[1][1: ]
|
|
||||||
|
|
||||||
try:
|
|
||||||
if ext == "vol":
|
|
||||||
self.mesh.Save(filename)
|
|
||||||
|
|
||||||
elif ext in self.formats.keys():
|
|
||||||
self.mesh.Export(filename, self.formats[ext])
|
|
||||||
|
|
||||||
else:
|
|
||||||
raise NotSupportedMeshFormat(f"Mesh format '{ ext }' is not supported")
|
|
||||||
|
|
||||||
except (NotSupportedMeshFormat, Exception) as e:
|
|
||||||
err = e
|
|
||||||
returncode = 1
|
|
||||||
|
|
||||||
return out, err, returncode
|
|
||||||
|
|
||||||
def to_meshio(self):
|
|
||||||
points = extractPoints(self.mesh.Points())
|
|
||||||
cells = []
|
|
||||||
|
|
||||||
if len(self.mesh.Elements1D()) > 0:
|
|
||||||
cells.extend([ cells_ for cells_ in extractCells(1, self.mesh.Elements1D()).items() ])
|
|
||||||
|
|
||||||
if len(self.mesh.Elements2D()) > 0:
|
|
||||||
cells.extend([ cells_ for cells_ in extractCells(2, self.mesh.Elements2D()).items() ])
|
|
||||||
|
|
||||||
if len(self.mesh.Elements3D()) > 0:
|
|
||||||
cells.extend([ cells_ for cells_ in extractCells(3, self.mesh.Elements3D()).items() ])
|
|
||||||
|
|
||||||
return meshio.Mesh(points, cells)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def volumeTetra(points: array) -> float:
|
|
||||||
return 1 / 6 * linalg.det(numpy.append(points.transpose(), numpy.array([[1, 1, 1, 1]]), axis = 0))
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volumes(self) -> array:
|
def faces(self) -> list[ndarray]:
|
||||||
points = []
|
points = []
|
||||||
|
|
||||||
for cells in self.cells:
|
for cellBlock in self.cells:
|
||||||
if cells.dim == 3:
|
if cellBlock.dim == 2:
|
||||||
points.extend([ self.mesh.points[cell] for cell in cells.data ])
|
points.extend([ *self.points[cellBlock.data] ])
|
||||||
|
|
||||||
return array(points)
|
return points
|
||||||
|
|
||||||
@property
|
|
||||||
def faces(self) -> array:
|
|
||||||
points = []
|
|
||||||
|
|
||||||
for cells in self.cells:
|
|
||||||
if cells.dim == 2:
|
|
||||||
points.extend([ self.mesh.points[cell] for cell in cells.data ])
|
|
||||||
|
|
||||||
return array(points)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def edges(self) -> array:
|
|
||||||
points = []
|
|
||||||
|
|
||||||
for cells in self.cells:
|
|
||||||
if cells.dim == 1:
|
|
||||||
points.extend([ self.mesh.points[cell] for cell in cells.data ])
|
|
||||||
|
|
||||||
return array(points)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# tetras = numpy.array([ [ [ vertex for vertex in mesh[index] ] for index in element.vertices ] for element in self.mesh.Elements3D() ])
|
|
||||||
# volumes = numpy.array([ 1 / 6 * linalg.det(numpy.append(tetra.transpose(), numpy.array([[1, 1, 1, 1]]), axis = 0)) for tetra in tetras ])
|
|
||||||
|
|
||||||
|
@ -2,4 +2,18 @@
|
|||||||
# This file is part of anisotropy.
|
# This file is part of anisotropy.
|
||||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
from numpy import ndarray
|
||||||
|
from numpy import linalg
|
||||||
|
from .utils import detectCellType
|
||||||
|
|
||||||
|
|
||||||
|
def volume(points: ndarray) -> ndarray:
|
||||||
|
cellType = detectCellType(3, len(points))
|
||||||
|
|
||||||
|
if cellType == "tetra":
|
||||||
|
return 1 / 6 * linalg.det(numpy.append(points.transpose(), numpy.array([[1, 1, 1, 1]]), axis = 0))
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise Exception(f"Not supported cell type '{ cellType }'")
|
||||||
|
|
||||||
|
@ -1,31 +1,68 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
from netgen import meshing
|
||||||
from meshio._mesh import topological_dimension
|
from meshio._mesh import topological_dimension
|
||||||
from meshio._common import num_nodes_per_cell
|
from meshio._common import num_nodes_per_cell
|
||||||
from numpy import array
|
from numpy import array, asarray, ndarray
|
||||||
|
|
||||||
|
def detectCellType(dimension: int, num_nodes: int):
|
||||||
def detectTopology(dimension: dict, num_nodes: dict):
|
|
||||||
for dim in topological_dimension.keys():
|
for dim in topological_dimension.keys():
|
||||||
for num in num_nodes_per_cell.keys():
|
for num in num_nodes_per_cell.keys():
|
||||||
if topological_dimension[dim] == dimension and num_nodes_per_cell[num] == num_nodes and dim == num:
|
if topological_dimension[dim] == dimension and num_nodes_per_cell[num] == num_nodes and dim == num:
|
||||||
return dim
|
return dim
|
||||||
|
|
||||||
|
class CellBlock:
|
||||||
|
def __init__(self, cellType: str, data: list | ndarray, tags: list[str] | None = None):
|
||||||
|
self.type = cellType
|
||||||
|
self.data = data
|
||||||
|
|
||||||
def extractPoints(points):
|
if cellType.startswith("polyhedron"):
|
||||||
return array([ point.p for point in points ], dtype = float)
|
self.dim = 3
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.data = asarray(self.data)
|
||||||
|
self.dim = topological_dimension[cellType]
|
||||||
|
|
||||||
|
self.tags = [] if tags is None else tags
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
items = [
|
||||||
|
"CellBlock",
|
||||||
|
f"type: { self.type }",
|
||||||
|
f"num cells: { len(self.data) }",
|
||||||
|
f"tags: { self.tags }",
|
||||||
|
]
|
||||||
|
return "<" + ", ".join(items) + ">"
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self.data)
|
||||||
|
|
||||||
|
def extractNetgenPoints(mesh: meshing.Mesh) -> ndarray:
|
||||||
|
return array([ point.p for point in mesh.Points() ], dtype = float)
|
||||||
|
|
||||||
|
|
||||||
def extractCells(dimension: int, elements):
|
def extractNetgenCells(dim: int, mesh: meshing.Mesh) -> list[CellBlock]:
|
||||||
cellsNew = {}
|
cellsDict = {}
|
||||||
|
elements = {
|
||||||
|
0: mesh.Elements0D(),
|
||||||
|
1: mesh.Elements1D(),
|
||||||
|
2: mesh.Elements2D(),
|
||||||
|
3: mesh.Elements3D()
|
||||||
|
}[dim]
|
||||||
|
|
||||||
|
if len(elements) == 0:
|
||||||
|
return []
|
||||||
|
|
||||||
for cell in elements:
|
for cell in elements:
|
||||||
cellTopo = detectTopology(dimension, len(cell.points))
|
cellType = detectCellType(dim, len(cell.points))
|
||||||
# shift indicies, they should starts from zero
|
# shift indicies, they should start from zero
|
||||||
cellNew = array([ pointId.nr for pointId in cell.points ], dtype = int) - 1
|
cellNew = array([ pointId.nr for pointId in cell.points ], dtype = int) - 1
|
||||||
|
|
||||||
if cellsNew.get(cellTopo):
|
if cellsDict.get(cellType):
|
||||||
cellsNew[cellTopo].append(cellNew)
|
cellsDict[cellType].append(cellNew)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
cellsNew[cellTopo] = [ cellNew ]
|
cellsDict[cellType] = [ cellNew ]
|
||||||
|
|
||||||
return cellsNew
|
cells = [ CellBlock(key, value) for key, value in cellsDict.items() ]
|
||||||
|
|
||||||
|
return cells
|
||||||
|
@ -6,6 +6,8 @@ from netgen.occ import *
|
|||||||
import numpy
|
import numpy
|
||||||
from numpy import linalg
|
from numpy import linalg
|
||||||
import os
|
import os
|
||||||
|
from os import PathLike
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class ShapeError(Exception):
|
class ShapeError(Exception):
|
||||||
@ -17,7 +19,7 @@ class Shape(object):
|
|||||||
self.groups = {}
|
self.groups = {}
|
||||||
self.shape = None
|
self.shape = None
|
||||||
|
|
||||||
def export(self, filename: str):
|
def write(self, filename: PathLike):
|
||||||
"""Export a shape.
|
"""Export a shape.
|
||||||
|
|
||||||
Supported formats: step.
|
Supported formats: step.
|
||||||
@ -29,11 +31,12 @@ class Shape(object):
|
|||||||
Output, error messages and returncode
|
Output, error messages and returncode
|
||||||
"""
|
"""
|
||||||
out, err, returncode = "", "", 0
|
out, err, returncode = "", "", 0
|
||||||
ext = os.path.splitext(filename)[1][1: ]
|
path = Path(filename).resolve()
|
||||||
|
ext = path.suffix[1: ]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if ext == "step":
|
if ext == "step":
|
||||||
self.shape.WriteStep(filename)
|
self.shape.WriteStep(path)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"{ ext } is not supported")
|
raise NotImplementedError(f"{ ext } is not supported")
|
||||||
@ -48,11 +51,12 @@ class Shape(object):
|
|||||||
|
|
||||||
return out, err, returncode
|
return out, err, returncode
|
||||||
|
|
||||||
def load(self, filename: str):
|
def read(self, filename: PathLike):
|
||||||
ext = os.path.splitext(filename)[1][1:]
|
path = Path(filename).resolve()
|
||||||
|
ext = path.suffix[1: ]
|
||||||
|
|
||||||
if ext in ["step", "iges", "brep"]:
|
if ext in ["step", "iges", "brep"]:
|
||||||
self.shape = OCCGeometry(filename).shape
|
self.shape = OCCGeometry(path).shape
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError(f"Shape format '{ext}' is not supported")
|
raise NotImplementedError(f"Shape format '{ext}' is not supported")
|
||||||
|
@ -135,25 +135,14 @@ class OnePhaseFlow(FoamCase):
|
|||||||
u
|
u
|
||||||
])
|
])
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def facesToPatches(faces: tuple[int, str]):
|
def createPatches(self, patches: dict):
|
||||||
# initial 43 unnamed patches ->
|
# initial 43 unnamed patches ->
|
||||||
# 6 named patches (inlet, outlet, wall, symetry0 - 3/5) ->
|
# 6 named patches (inlet, outlet, wall, symetry0 - 3/5) ->
|
||||||
# 4 inGroups (inlet, outlet, wall, symetry)
|
# 4 inGroups (inlet, outlet, wall, symetry)
|
||||||
|
|
||||||
createPatchDict = F.CreatePatchDict()
|
createPatchDict = F.CreatePatchDict()
|
||||||
createPatchDict["patches"] = []
|
createPatchDict["patches"] = []
|
||||||
patches = {}
|
|
||||||
|
|
||||||
for n, name in faces:
|
|
||||||
# shifted index
|
|
||||||
n += 1
|
|
||||||
|
|
||||||
if patches.get(name):
|
|
||||||
patches[name].append(f"patch{n}")
|
|
||||||
|
|
||||||
else:
|
|
||||||
patches[name] = [f"patch{n}"]
|
|
||||||
|
|
||||||
for name in patches.keys():
|
for name in patches.keys():
|
||||||
if name == "inlet":
|
if name == "inlet":
|
||||||
@ -182,7 +171,7 @@ class OnePhaseFlow(FoamCase):
|
|||||||
"patches": patches[name]
|
"patches": patches[name]
|
||||||
})
|
})
|
||||||
|
|
||||||
return createPatchDict
|
self.append(createPatchDict)
|
||||||
|
|
||||||
def build(self) -> tuple[str, str, int]:
|
def build(self) -> tuple[str, str, int]:
|
||||||
# TODO: configure working directory (FoamCase)
|
# TODO: configure working directory (FoamCase)
|
||||||
|
Loading…
Reference in New Issue
Block a user