From b45954d1531d278228f2b3d03cb9e0079127bd17 Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Wed, 20 Oct 2021 21:48:59 +0500 Subject: [PATCH] Mod: improved mesh class Mod: mesh subclass for each sample New: foamfile, reader and writer --- anisotropy/core/main.py | 91 +---- anisotropy/openfoam/foamcase.py | 30 ++ anisotropy/openfoam/foamfile.py | 91 +++++ .../openfoam/template/system/fvSolution | 134 +++--- anisotropy/openfoam/utils.py | 2 +- anisotropy/salomepl/geometry.py | 2 +- anisotropy/salomepl/mesh.py | 381 ++++++++++++------ anisotropy/samples/__init__.py | 6 +- anisotropy/samples/bodyCentered.py | 23 ++ anisotropy/samples/faceCentered.py | 25 ++ anisotropy/samples/simple.py | 34 ++ playground/mesh.py | 237 +++++++++-- playground/salome-run.py | 7 +- requirements.txt | 1 + 14 files changed, 755 insertions(+), 309 deletions(-) create mode 100644 anisotropy/openfoam/foamcase.py create mode 100644 anisotropy/openfoam/foamfile.py diff --git a/anisotropy/core/main.py b/anisotropy/core/main.py index 25aaf75..b8b61de 100644 --- a/anisotropy/core/main.py +++ b/anisotropy/core/main.py @@ -291,104 +291,39 @@ class Anisotropy(object): setupLogger(logger, logging.INFO, self.env["LOG"]) p = self.params - ### - # Shape - ## - logger.info("Constructing shape ...") - - geompy = salomepl.geometry.getGeom() - structure = dict( - simple = Simple, - bodyCentered = BodyCentered, - faceCentered = FaceCentered + sGeometry, sMesh = dict( + simple = (Simple, SimpleMesh), + bodyCentered = (BodyCentered, BodyCenteredMesh), + faceCentered = (FaceCentered, FaceCenteredMesh) )[p["structure"]["type"]] - shapeGeometry = structure(**p["structure"]) - shapeGeometry.build() - [length, surfaceArea, volume] = geompy.BasicProperties(shapeGeometry.shape, theTolerance = 1e-06) + # Shape + logger.info("Constructing shape ...") + geometry = sGeometry(**p["structure"]) + geometry.build() - - ### # Mesh - ## logger.info("Prepairing mesh ...") + mesh = sMesh(geometry) + mesh.build() - params = db.Mesh() - mesh = mesh.Mesh(shapeGeometry) - algo3d = mesh.algo3d(Netgen3D) - algo3d.apply(**params) - - mesh = smesh.Mesh(shape) - algo3d = mesh.Tetrahedron(algo = smeshBuilder.NETGEN_3D) - algo2d = mesh.Triangle(algo = smeshBuilder.NETGEN_2D) - hypo2d = algo2d.MaxElementArea(0.197375) - algo1d = mesh.Segment() - hypo1d = algo1d.AutomaticLength(1) - - algo2d = mesh.Triangle(algo = smeshBuilder.NETGEN_2D, geom = strips) - hypo2d = algo2d.LengthFromEdges() - algo1d = mesh.Segment(algo = smeshBuilder.COMPOSITE, geom = strips) - hypo1d = algo1d.AutomaticLength(0.633882) - hypo1d.SetFineness( 1 ) - - mp = p["mesh"] - - lengths = [ - geompy.BasicProperties(edge)[0] for edge in geompy.SubShapeAll(shapeGeometry.shape, geompy.ShapeType["EDGE"]) - ] - meanSize = sum(lengths) / len(lengths) - mp["maxSize"] = meanSize - mp["minSize"] = meanSize * 1e-1 - mp["chordalError"] = mp["maxSize"] / 2 - - faces = [] - for group in shapeGeometry.groups: - if group.GetName() in mp["facesToIgnore"]: - faces.append(group) - - - mesh = salomepl.mesh.Mesh(shapeGeometry.shape) - mesh.Tetrahedron(**mp) - - if mp["viscousLayers"]: - mesh.ViscousLayers(**mp, faces = faces) - - # Submesh - smp = p["submesh"] - - for submesh in smp: - for group in shapeGeometry.groups: - if submesh["name"] == group.GetName(): - subshape = group - - submesh["maxSize"] = meanSize * 1e-1 - submesh["minSize"] = meanSize * 1e-3 - submesh["chordalError"] = submesh["minSize"] * 1e+1 - - mesh.Triangle(subshape, **submesh) - - - self.update() logger.info("Computing mesh ...") out, err, returncode = mesh.compute() - ### - # Results - ## - #p["meshresult"] = dict() if not returncode: mesh.removePyramids() - mesh.assignGroups() + mesh.createGroups() casePath = self.getCasePath(path) os.makedirs(casePath, exist_ok = True) logger.info("Exporting mesh ...") - returncode, err = mesh.exportUNV(os.path.join(casePath, "mesh.unv")) + out, err, returncode = mesh.export(os.path.join(casePath, "mesh.unv")) if returncode: logger.error(err) + # NOTE: edit from here meshStats = mesh.stats() p["meshresult"].update( surfaceArea = surfaceArea, diff --git a/anisotropy/openfoam/foamcase.py b/anisotropy/openfoam/foamcase.py new file mode 100644 index 0000000..566c4c1 --- /dev/null +++ b/anisotropy/openfoam/foamcase.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + +from anisotropy.openfoam.foamfile import FoamFile + +class ControlDict(FoamFile): + def __init__(self): + ff = FoamFile( + "system/controlDict", + _location = "system" + ) + self.header = ff.header + self.content = { + "application": "simpleFoam", + "startFrom": "startTime", + "startTime": 0, + "stopAt": "endTime", + "endTime": 2000, + "deltaT": 1, + "writeControl": "timeStep", + "writeInterval": 100, + "purgeWrite": 0, + "writeFormat": "ascii", + "writePrecision": 6, + "writeCompression": "off", + "timeFormat": "general", + "timePrecision": 6, + "runTimeModifiable": True + } diff --git a/anisotropy/openfoam/foamfile.py b/anisotropy/openfoam/foamfile.py new file mode 100644 index 0000000..f7b6e19 --- /dev/null +++ b/anisotropy/openfoam/foamfile.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + +from anisotropy.openfoam.utils import version +from PyFoam.RunDictionary.ParsedParameterFile import ParsedParameterFile +from PyFoam.Basics.FoamFileGenerator import FoamFileGenerator +import os + +class FoamFile(object): + def __init__(self, + filename, + _version = 2.0, + _format = "ascii", + _class = "dictionary", + _location = None, + _object = None + ): + + self.path = os.path.abspath(filename) + self.header = { + "version": _version, + "format": _format, + "class": _class, + "object": _object or os.path.split(filename)[1] + } + self.content = {} + + if _location: + self.header["location"] = f'"{ _location }"' + + def __getitem__(self, key): + return self.content[key] + + def __setitem__(self, key, value): + self.content[key] = value + + def __delitem__(self, key): + del self.content[key] + + def __len__(self): + return len(self.content) + + def __iter__(self): + for key in self.content: + yield key + + def read(self): + ppf = ParsedParameterFile(self.path) + + self.header = ppf.header + self.content = ppf.content + + def _template(self, header, content): + limit = 78 + desc = [ + "/*--------------------------------*- C++ -*----------------------------------*\\", + "| ========= | |", + "| \\\\ / F ield | OpenFOAM: The Open Source CFD Toolbox |", + "| \\\\ / O peration |", + "| \\\\ / A nd | |", + "| \\\\/ M anipulation | |", + "\*---------------------------------------------------------------------------*/" + ] + desc[3] += " Version: {}".format(version() or "missed") + desc[3] += " " * (limit - len(desc[3])) + "|" + afterheader = "// " + 37 * "* " + "//" + endfile = "// " + 73 * "*" + " //" + + return "\n".join([*desc, header, afterheader, content, endfile]) + + + def write(self): + header = FoamFileGenerator({}, header = self.header) + header = header.makeString()[ :-2] + header = header.replace("\n ", "\n" + 4 * " ") + + content = FoamFileGenerator(self.content) + content = content.makeString()[ :-1] + content = content.replace("\n ", "\n" + 4 * " ").replace(" \t// " + 73 * "*" + " //", "") + content = content.replace(" /* empty */ ", "") + + prepared = self._template(header, content) + + os.makedirs(os.path.split(self.path)[0], exist_ok = True) + + with open(self.path, "w") as io: + _ = io.write(prepared) + + + diff --git a/anisotropy/openfoam/template/system/fvSolution b/anisotropy/openfoam/template/system/fvSolution index 09e75c5..89b0a21 100644 --- a/anisotropy/openfoam/template/system/fvSolution +++ b/anisotropy/openfoam/template/system/fvSolution @@ -1,96 +1,84 @@ /*--------------------------------*- C++ -*----------------------------------*\ | ========= | | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | -| \\ / O peration | Version: v2012 | -| \\ / A nd | Website: www.openfoam.com | +| \\ / O peration | Version: missed | +| \\ / A nd | | | \\/ M anipulation | | \*---------------------------------------------------------------------------*/ FoamFile { - version 2.0; - format ascii; - class dictionary; - location "system"; - object fvSolution; + version 2.0; + format ascii; + class dictionary; + location "system"; + object fvSolution; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - solvers { - p - { - solver GAMG; - tolerance 1e-06; - relTol 0.1; - smoother GaussSeidel; - } - - U - { - solver smoothSolver; - smoother GaussSeidel; - nSweeps 2; - tolerance 1e-08; - relTol 0.1; - } - - /*Phi - { - solver GAMG; - smoother GaussSeidel; - tolerance 1e-08; - relTol 0.01; - }*/ - Phi - { - solver GAMG; - smoother DIC; - cacheAgglomeration on; - agglomerator faceAreaPair; - nCellsInCoarsestLevel 10; - mergeLevels 1; - - tolerance 1e-06; - relTol 0.01; - } + p + { + solver GAMG; + tolerance 1e-06; + relTol 0.1; + smoother GaussSeidel; + } + U + { + solver smoothSolver; + smoother GaussSeidel; + nSweeps 2; + tolerance 1e-08; + relTol 0.1; + } /*Phi + { + solver GAMG; + smoother GaussSeidel; + tolerance 1e-08; + relTol 0.01; + }*/ + Phi + { + solver GAMG; + smoother DIC; + cacheAgglomeration yes; + agglomerator faceAreaPair; + nCellsInCoarsestLevel 10; + mergeLevels 1; + tolerance 1e-06; + relTol 0.01; + } } - potentialFlow { - nNonOrthogonalCorrectors 20; - PhiRefCell 0; - PhiRefPoint 0; - PhiRefValue 0; - Phi 0; + nNonOrthogonalCorrectors 20; + PhiRefCell 0; + PhiRefPoint 0; + PhiRefValue 0; + Phi 0; } - cache { - grad(U); -} - + grad(p) /* empty */ ; +} SIMPLE { - nNonOrthogonalCorrectors 10; - - residualControl - { - p 1e-5; - U 1e-5; - } + nNonOrthogonalCorrectors 10; + residualControl + { + p 1e-05; + U 1e-05; + } } - relaxationFactors { - fields - { - p 0.3; - } - equations - { - U 0.5; - } + fields + { + p 0.3; + } + equations + { + U 0.5; + } } - - -// ************************************************************************* // +// ************************************************************************* // \ No newline at end of file diff --git a/anisotropy/openfoam/utils.py b/anisotropy/openfoam/utils.py index 7f4f681..2c77f9d 100644 --- a/anisotropy/openfoam/utils.py +++ b/anisotropy/openfoam/utils.py @@ -7,7 +7,7 @@ import shutil from .application import application def version() -> str: - return os.environ["WM_PROJECT_VERSION"] + return os.environ.get("WM_PROJECT_VERSION") def foamCleanCustom(case: str = None): diff --git a/anisotropy/salomepl/geometry.py b/anisotropy/salomepl/geometry.py index 090378f..59a3afb 100644 --- a/anisotropy/salomepl/geometry.py +++ b/anisotropy/salomepl/geometry.py @@ -58,7 +58,7 @@ class StructureGeometry(object): # Geometry module if not GEOM_IMPORTED: - raise ImportError("Cannot find the salome modules.") + raise ImportError("Cannot find the salome geometry modules.") else: self.geo = geomBuilder.New() diff --git a/anisotropy/salomepl/mesh.py b/anisotropy/salomepl/mesh.py index ad1331d..ea3ef5a 100644 --- a/anisotropy/salomepl/mesh.py +++ b/anisotropy/salomepl/mesh.py @@ -3,124 +3,77 @@ # License: GNU GPL version 3, see the file "LICENSE" for details. import logging - -logger = logging.getLogger("anisotropy") + +SMESH_IMPORTED = False try: import SMESH from salome.smesh import smeshBuilder -except ImportError: - logger.debug("Trying to get SALOME mesh modules outside SALOME environment. Modules won't be imported.") - -if globals().get("smeshBuilder"): - smesh = smeshBuilder.New() - -else: - smesh = None - -import enum - -class Fineness(enum.Enum): - VeryCoarse = 0 - Coarse = 1 - Moderate = 2 - Fine = 3 - VeryFine = 4 - Custom = 5 - - -def getSmesh(): - return smesh - - -def updateParams(old, new: dict): - old.SetMaxSize(new.get("maxSize", old.GetMaxSize())) - old.SetMinSize(new.get("minSize", old.GetMinSize())) - - old.SetFineness(new.get("fineness", old.GetFineness())) - old.SetGrowthRate(new.get("growthRate", old.GetGrowthRate())) - old.SetNbSegPerEdge(new.get("nbSegPerEdge", old.GetNbSegPerEdge())) - old.SetNbSegPerRadius(new.get("nbSegPerRadius", old.GetNbSegPerRadius())) - - old.SetChordalErrorEnabled(new.get("chordalErrorEnabled", old.GetChordalErrorEnabled())) - old.SetChordalError(new.get("chordalError", old.GetChordalError())) - - old.SetSecondOrder(new.get("secondOrder", old.GetSecondOrder())) - old.SetOptimize(new.get("optimize", old.GetOptimize())) - old.SetQuadAllowed(new.get("quadAllowed", old.GetQuadAllowed())) - old.SetUseSurfaceCurvature(new.get("useSurfaceCurvature", old.GetUseSurfaceCurvature())) - old.SetFuseEdges(new.get("fuseEdges", old.GetFuseEdges())) - old.SetCheckChartBoundary(new.get("checkChartBoundary", old.GetCheckChartBoundary())) + SMESH_IMPORTED = True +except: + pass class Mesh(object): - def __init__(self, shape, name = ""): - self.name = name if name else shape.GetName() - self.mesh = smesh.Mesh(shape, self.name) - self.geom = shape - self.algo = None - self.params = None - self.viscousLayers = None + def __init__(self, geom): + + # Mesh module + if not SMESH_IMPORTED: + raise ImportError("Cannot find the salome mesh modules.") - self.submeshes = [] + else: + self.smesh = smeshBuilder.New() + self.smeshBuilder = smeshBuilder + + # General attributes + self.geom = geom + self.mesh = self.smesh.Mesh(self.geom.shape, self.geom.name) - def Tetrahedron(self, **kwargs): - self.algo = self.mesh.Tetrahedron(algo = smeshBuilder.NETGEN_1D2D3D) - self.params = self.algo.Parameters() - - self.params = updateParams(self.params, kwargs) - - def _extrusionMethod(self, key: str): - return dict( - SURF_OFFSET_SMOOTH = smeshBuilder.SURF_OFFSET_SMOOTH, - FACE_OFFSET = smeshBuilder.FACE_OFFSET, - NODE_OFFSET = smeshBuilder.NODE_OFFSET, - ).get(key, smeshBuilder.SURF_OFFSET_SMOOTH) - - def ViscousLayers(self, - thickness = 1, - numberOfLayers = 1, - stretchFactor = 0, - faces = [], - isFacesToIgnore = True, - extrMethod = "SURF_OFFSET_SMOOTH", - **kwargs - ): - - self.viscousLayers = self.algo.ViscousLayers( - thickness, - numberOfLayers, - stretchFactor, - faces, - isFacesToIgnore, - self._extrusionMethod(extrMethod) - ) - - def Triangle(self, subshape, **kwargs): - submesh = Submesh(self.mesh, subshape) - submesh.algo = self.mesh.Triangle(algo = smeshBuilder.NETGEN_1D2D, geom = subshape) - submesh.mesh = submesh.algo.subm - submesh.params = submesh.algo.Parameters() - - submesh.params = updateParams(submesh.params, kwargs) - - self.submeshes.append(submesh) + def algo3d(self, algo, type = "tetrahedron"): + smeshAlgo = self.mesh.__dict__.get(type.capitalize()) + self.meshAlgorithm3d = algo() + self.meshAlgorithm3d.initialize(smeshAlgo(algo = self.meshAlgorithm3d.key)) + self.mesh.AddHypothesis(self.meshAlgorithm3d.hypo) + + return self.meshAlgorithm3d + + def algo2d(self, algo, type = "triangle"): + smeshAlgo = self.mesh.__dict__.get(type.capitalize()) + self.meshAlgorithm2d = algo() + self.meshAlgorithm2d.initialize(smeshAlgo(algo = self.meshAlgorithm2d.key)) + self.mesh.AddHypothesis(self.meshAlgorithm2d.hypo) + + return self.meshAlgorithm2d + + def algo1d(self, algo, type = "segment"): + smeshAlgo = self.mesh.__dict__.get(type.capitalize()) + self.meshAlgorithm1d = algo() + self.meshAlgorithm1d.initialize(smeshAlgo(algo = self.meshAlgorithm1d.key)) + self.mesh.AddHypothesis(self.meshAlgorithm1d.hypo) + + return self.meshAlgorithm1d + + def createGroups(self, prefix = None): + prefix = prefix or "" + + for group in self.shape.groups: + name = group.GetName() + + if name: + name = prefix + name + self.mesh.GroupOnGeom(group, name, SMESH.FACE) - def assignGroups(self, withPrefix = True): - prefix = "smesh_" if withPrefix else "" - - for group in self.mesh.geompyD.GetGroups(self.geom): - if group.GetName(): - self.mesh.GroupOnGeom(group, f"{ prefix }{ group.GetName() }", SMESH.FACE) - def compute(self): + """Compute mesh. + """ isDone = self.mesh.Compute() - returncode = int(not isDone) + out = "" err = self.mesh.GetComputeErrors() - - return "", err, returncode - + returncode = int(not isDone) + + return out, err, returncode + def stats(self): return { "elements": self.mesh.NbElements(), @@ -132,19 +85,6 @@ class Mesh(object): "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( @@ -159,15 +99,206 @@ class Mesh(object): self.mesh.SplitVolumesIntoTetra(pyramidVolumes, smesh.Hex_5Tet) self.mesh.RemoveGroup(pyramidGroup) - self.mesh.RenumberElements() + self.mesh.RenumberElements() + def export( + filename: str + ): + """Export a mesh. + + Supported formats: unv. + + :param filename: + Name of the file to store the given mesh in. + + :return: + Output, error messages and returncode + """ + out, err, returncode = "", "", 0 + ext = os.path.splitext(filename)[1][1: ] + + try: + if ext == "unv": + self.mesh.ExportUNV(self.mesh, filename) + + else: + raise NotImplementedError(f"{ ext } is not supported") + + except NotImplementedError as e: + err = e + returncode = 1 + + except Exception as e: + err = e.details.text + returncode = 1 + + return out, err, returncode + +class MeshAlgorithm(object): + pass -class Submesh(object): - def __init__(self, father, subshape, name = ""): - self.name = name if name else subshape.GetName() - self.mesh = None - self.geom = subshape - self.algo = None - self.params = None +class AlgorithmHypothesis(object): + pass +class Netgen3D(MeshAlgorithm): + """ + MaxElementVolume + Parameters + ViscousLayers + """ + def __init__(self, **kwargs): + self.key = smeshBuilder.NETGEN_3D + + def initialize(self, algo, hypo): #thesises: list): + self.algo = algo + #self.hypo = self.algo.Parameters() + + #for hypo in hypothesises: + + self.hypo = self.__dict__[hypo.__name__]() + + class ViscousLayers(AlgorithmHypothesis): + def __init__(self, + algo, + thickness = 1, + numberOfLayers = 1, + stretchFactor = 0, + faces = [], + isFacesToIgnore = True, + extrMethod = "SURF_OFFSET_SMOOTH", + **kwargs + ): + extrusionMethod = dict( + SURF_OFFSET_SMOOTH = smeshBuilder.SURF_OFFSET_SMOOTH, + FACE_OFFSET = smeshBuilder.FACE_OFFSET, + NODE_OFFSET = smeshBuilder.NODE_OFFSET, + ).get(extrMethod, smeshBuilder.SURF_OFFSET_SMOOTH) + + self.hypo = self.algo.ViscousLayers( + thickness, + numberOfLayers, + stretchFactor, + faces, + isFacesToIgnore, + extrusionMethod + ) + + class Parameters(AlgorithmHypothesis): + def __init__(self, algo): + self.hypo = self.algo.Parameters() + + @property + def minSize(self): + return self.hypo.GetMinSize() + + @minSize.setter + def minSize(self, value): + self.hypo.SetMinSize(value) + + @property + def maxSize(self): + return self.hypo.GetMaxSize() + + @maxSize.setter + def maxSize(self, value): + self.hypo.SetMaxSize(value) + + @property + def fineness(self): + return self.hypo.GetFineness() + + @fineness.setter + def fineness(self, value): + self.hypo.SetFineness(value) + + @property + def growthRate(self): + return self.hypo.GetGrowthRate() + + @growthRate.setter + def growthRate(self, value): + self.hypo.SetGrowthRate(value) + + @property + def nbSegPerEdge(self): + return self.hypo.GetNbSegPerEdge() + + @nbSegPerEdge.setter + def nbSegPerEdge(self, value): + self.hypo.SetNbSegPerEdge(value) + + @property + def nbSegPerRadius(self): + return self.hypo.GetNbSegPerRadius() + + @nbSegPerRadius.setter + def nbSegPerRadius(self, value): + self.hypo.SetNbSegPerRadius(value) + + @property + def chordalErrorEnabled(self): + return self.hypo.GetChordalErrorEnabled() + + @chordalErrorEnabled.setter + def chordalErrorEnabled(self, value): + self.hypo.SetChordalErrorEnabled(value) + + @property + def chordalError(self): + return self.hypo.GetChordalError() + + @chordalError.setter + def chordalError(self, value): + self.hypo.SetChordalError(value) + + @property + def secondOrder(self): + return self.hypo.GetSecondOrder() + + @secondOrder.setter + def secondOrder(self, value): + self.hypo.SetSecondOrder(value) + + @property + def optimize(self): + return self.hypo.GetOptimize() + + @optimize.setter + def optimize(self, value): + self.hypo.SetOptimize(value) + + @property + def quadAllowed(self): + return self.hypo.GetQuadAllowed() + + @quadAllowed.setter + def quadAllowed(self, value): + self.hypo.SetQuadAllowed(value) + + @property + def useSurfaceCurvature(self): + return self.hypo.GetUseSurfaceCurvature() + + @useSurfaceCurvature.setter + def useSurfaceCurvature(self, value): + self.hypo.SetUseSurfaceCurvature(value) + + @property + def fuseEdges(self): + return self.hypo.GetFuseEdges() + + @fuseEdges.setter + def fuseEdges(self, value): + self.hypo.SetFuseEdges(value) + + @property + def checkChartBoundary(self): + return self.hypo.GetCheckChartBoundary() + + @checkChartBoundary.setter + def GetCheckChartBoundary(self, value): + self.hypo.SetCheckChartBoundary(value) + +class MEFISTO(MeshAlgorithm): + pass diff --git a/anisotropy/samples/__init__.py b/anisotropy/samples/__init__.py index 3859687..74be2b3 100644 --- a/anisotropy/samples/__init__.py +++ b/anisotropy/samples/__init__.py @@ -2,6 +2,6 @@ # This file is part of anisotropy. # License: GNU GPL version 3, see the file "LICENSE" for details. -from anisotropy.samples.simple import Simple -from anisotropy.samples.bodyCentered import BodyCentered -from anisotropy.samples.faceCentered import FaceCentered +#from anisotropy.samples.simple import Simple +#from anisotropy.samples.bodyCentered import BodyCentered +#from anisotropy.samples.faceCentered import FaceCentered diff --git a/anisotropy/samples/bodyCentered.py b/anisotropy/samples/bodyCentered.py index 33f1c36..10567c8 100644 --- a/anisotropy/samples/bodyCentered.py +++ b/anisotropy/samples/bodyCentered.py @@ -226,3 +226,26 @@ class BodyCentered(StructureGeometry): self.groups.append(self.geo.CutListOfGroups([groupAll], self.groups, theName = "wall")) +class BodyCenteredMesh(Mesh): + def build(self): + algo2d = self.mesh.Triangle(algo = self.smeshBuilder.NETGEN_1D2D) + hypo2d = algo2d.Parameters() + hypo2d.SetMaxSize(0.1) + hypo2d.SetMinSize(0.001) + hypo2d.SetFineness(5) + hypo2d.SetGrowthRate(0.3) + hypo2d.SetNbSegPerEdge(2) + hypo2d.SetNbSegPerRadius(3) + hypo2d.SetChordalErrorEnabled(True) + hypo2d.SetChordalError(0.05) + hypo2d.SetOptimize(True) + hypo2d.SetUseSurfaceCurvature(True) + + algo3d = self.mesh.Tetrahedron(algo = self.smeshBuilder.NETGEN_3D) + #hypo3d = algo3d.Parameters() + + #faces = [ group for group in self.geom.groups if group.GetName() in ["inlet", "outlet"] ] + #hypo3dVL = algo3d.ViscousLayers(...) + + + diff --git a/anisotropy/samples/faceCentered.py b/anisotropy/samples/faceCentered.py index ee718aa..dcf7cda 100644 --- a/anisotropy/samples/faceCentered.py +++ b/anisotropy/samples/faceCentered.py @@ -3,6 +3,7 @@ # License: GNU GPL version 3, see the file "LICENSE" for details. from anisotropy.salomepl.geometry import StructureGeometry +from anisotropy.salomepl.mesh import Mesh from numpy import pi, sqrt, fix import logging @@ -228,3 +229,27 @@ class FaceCentered(StructureGeometry): self.groups.append(self.createGroup(self.shape, shapeShell, "strips", self.groups + [grainsOrigin])) self.groups.append(self.geo.CutListOfGroups([groupAll], self.groups, theName = "wall")) + +class FaceCenteredMesh(Mesh): + def build(self): + algo2d = self.mesh.Triangle(algo = self.smeshBuilder.NETGEN_1D2D) + hypo2d = algo2d.Parameters() + hypo2d.SetMaxSize(0.1) + hypo2d.SetMinSize(0.001) + hypo2d.SetFineness(5) + hypo2d.SetGrowthRate(0.3) + hypo2d.SetNbSegPerEdge(2) + hypo2d.SetNbSegPerRadius(3) + hypo2d.SetChordalErrorEnabled(True) + hypo2d.SetChordalError(0.05) + hypo2d.SetOptimize(True) + hypo2d.SetUseSurfaceCurvature(True) + + algo3d = self.mesh.Tetrahedron(algo = self.smeshBuilder.NETGEN_3D) + #hypo3d = algo3d.Parameters() + + #faces = [ group for group in self.geom.groups if group.GetName() in ["inlet", "outlet"] ] + #hypo3dVL = algo3d.ViscousLayers(...) + + + diff --git a/anisotropy/samples/simple.py b/anisotropy/samples/simple.py index 417580e..4ee8a75 100644 --- a/anisotropy/samples/simple.py +++ b/anisotropy/samples/simple.py @@ -3,6 +3,7 @@ # License: GNU GPL version 3, see the file "LICENSE" for details. from anisotropy.salomepl.geometry import StructureGeometry +from anisotropy.salomepl.mesh import Mesh from numpy import pi, sqrt, fix import logging @@ -211,3 +212,36 @@ class Simple(StructureGeometry): self.groups.append(self.createGroup(self.shape, shapeShell, "strips", self.groups + [grainsOrigin])) self.groups.append(self.geo.CutListOfGroups([groupAll], self.groups, theName = "wall")) + + +class SimpleMesh(Mesh): + def build(self): + algo2d = self.mesh.Triangle(algo = self.smeshBuilder.NETGEN_1D2D) + hypo2d = algo2d.Parameters() + hypo2d.SetMaxSize(0.1) + hypo2d.SetMinSize(0.001) + hypo2d.SetFineness(5) + hypo2d.SetGrowthRate(0.3) + hypo2d.SetNbSegPerEdge(2) + hypo2d.SetNbSegPerRadius(3) + hypo2d.SetChordalErrorEnabled(True) + hypo2d.SetChordalError(0.05) + hypo2d.SetOptimize(True) + hypo2d.SetUseSurfaceCurvature(True) + + algo3d = self.mesh.Tetrahedron(algo = self.smeshBuilder.NETGEN_3D) + #hypo3d = algo3d.Parameters() + + #faces = [ group for group in self.geom.groups if group.GetName() in ["inlet", "outlet"] ] + #hypo3dVL = algo3d.ViscousLayers(...) + + +from anisotropy.openfoam.foamcase import ControlDict + +class SimpleFlow(object): # FoamCase + def __init__(self): + controlDict = ControlDict() + controlDict["startFrom"] = "latestTime" + controlDict["endTime"] = 5000 + controlDict["writeInterval"] = 100 + diff --git a/playground/mesh.py b/playground/mesh.py index 5e49a80..ea3ef5a 100644 --- a/playground/mesh.py +++ b/playground/mesh.py @@ -1,35 +1,36 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. -class MeshAlgorithm(object): +import logging + +SMESH_IMPORTED = False + +try: + import SMESH + from salome.smesh import smeshBuilder + + SMESH_IMPORTED = True + +except: pass -class Netgen3D(MeshAlgorithm): - def __init__(self, **kwargs): - self.key = smeshBuilder.NETGEN_3D - - def initialize(self, algo): - self.algo = algo - self.hypo = self.algo.Parameters() - - - @property - def minSize(self): - return self.hypo.GetMinSize() - - @minSize.setter - def minSize(self, value): - self.hypo.SetMinSize(value) - -class MEFISTO(MeshAlgorithm): - pass - class Mesh(object): def __init__(self, geom): - self.smesh = smeshBuilder.New() + # Mesh module + if not SMESH_IMPORTED: + raise ImportError("Cannot find the salome mesh modules.") + + else: + self.smesh = smeshBuilder.New() + self.smeshBuilder = smeshBuilder + + # General attributes self.geom = geom self.mesh = self.smesh.Mesh(self.geom.shape, self.geom.name) - def algo3d(self, algo: MeshAlgorithm, type = "tetrahedron"): + def algo3d(self, algo, type = "tetrahedron"): smeshAlgo = self.mesh.__dict__.get(type.capitalize()) self.meshAlgorithm3d = algo() self.meshAlgorithm3d.initialize(smeshAlgo(algo = self.meshAlgorithm3d.key)) @@ -37,7 +38,7 @@ class Mesh(object): return self.meshAlgorithm3d - def algo2d(self, algo: MeshAlgorithm, type = "triangle"): + def algo2d(self, algo, type = "triangle"): smeshAlgo = self.mesh.__dict__.get(type.capitalize()) self.meshAlgorithm2d = algo() self.meshAlgorithm2d.initialize(smeshAlgo(algo = self.meshAlgorithm2d.key)) @@ -45,7 +46,7 @@ class Mesh(object): return self.meshAlgorithm2d - def algo1d(self, algo: MeshAlgorithm, type = "segment"): + def algo1d(self, algo, type = "segment"): smeshAlgo = self.mesh.__dict__.get(type.capitalize()) self.meshAlgorithm1d = algo() self.meshAlgorithm1d.initialize(smeshAlgo(algo = self.meshAlgorithm1d.key)) @@ -83,7 +84,23 @@ class Mesh(object): "prisms": self.mesh.NbPrisms(), "pyramids": self.mesh.NbPyramids() } - + + def removePyramids(self): + if self.mesh.NbPyramids() > 0: + pyramidCriterion = smesh.GetCriterion( + SMESH.VOLUME, + SMESH.FT_ElemGeomType, + SMESH.FT_Undefined, + SMESH.Geom_PYRAMID + ) + pyramidGroup = self.mesh.MakeGroupByCriterion("pyramids", pyramidCriterion) + pyramidVolumes = self.mesh.GetIDSource(pyramidGroup.GetIDs(), SMESH.VOLUME) + + self.mesh.SplitVolumesIntoTetra(pyramidVolumes, smesh.Hex_5Tet) + + self.mesh.RemoveGroup(pyramidGroup) + self.mesh.RenumberElements() + def export( filename: str ): @@ -117,3 +134,171 @@ class Mesh(object): return out, err, returncode + +class MeshAlgorithm(object): + pass + +class AlgorithmHypothesis(object): + pass + +class Netgen3D(MeshAlgorithm): + """ + MaxElementVolume + Parameters + ViscousLayers + """ + def __init__(self, **kwargs): + self.key = smeshBuilder.NETGEN_3D + + def initialize(self, algo, hypo): #thesises: list): + self.algo = algo + #self.hypo = self.algo.Parameters() + + #for hypo in hypothesises: + + self.hypo = self.__dict__[hypo.__name__]() + + class ViscousLayers(AlgorithmHypothesis): + def __init__(self, + algo, + thickness = 1, + numberOfLayers = 1, + stretchFactor = 0, + faces = [], + isFacesToIgnore = True, + extrMethod = "SURF_OFFSET_SMOOTH", + **kwargs + ): + extrusionMethod = dict( + SURF_OFFSET_SMOOTH = smeshBuilder.SURF_OFFSET_SMOOTH, + FACE_OFFSET = smeshBuilder.FACE_OFFSET, + NODE_OFFSET = smeshBuilder.NODE_OFFSET, + ).get(extrMethod, smeshBuilder.SURF_OFFSET_SMOOTH) + + self.hypo = self.algo.ViscousLayers( + thickness, + numberOfLayers, + stretchFactor, + faces, + isFacesToIgnore, + extrusionMethod + ) + + class Parameters(AlgorithmHypothesis): + def __init__(self, algo): + self.hypo = self.algo.Parameters() + + @property + def minSize(self): + return self.hypo.GetMinSize() + + @minSize.setter + def minSize(self, value): + self.hypo.SetMinSize(value) + + @property + def maxSize(self): + return self.hypo.GetMaxSize() + + @maxSize.setter + def maxSize(self, value): + self.hypo.SetMaxSize(value) + + @property + def fineness(self): + return self.hypo.GetFineness() + + @fineness.setter + def fineness(self, value): + self.hypo.SetFineness(value) + + @property + def growthRate(self): + return self.hypo.GetGrowthRate() + + @growthRate.setter + def growthRate(self, value): + self.hypo.SetGrowthRate(value) + + @property + def nbSegPerEdge(self): + return self.hypo.GetNbSegPerEdge() + + @nbSegPerEdge.setter + def nbSegPerEdge(self, value): + self.hypo.SetNbSegPerEdge(value) + + @property + def nbSegPerRadius(self): + return self.hypo.GetNbSegPerRadius() + + @nbSegPerRadius.setter + def nbSegPerRadius(self, value): + self.hypo.SetNbSegPerRadius(value) + + @property + def chordalErrorEnabled(self): + return self.hypo.GetChordalErrorEnabled() + + @chordalErrorEnabled.setter + def chordalErrorEnabled(self, value): + self.hypo.SetChordalErrorEnabled(value) + + @property + def chordalError(self): + return self.hypo.GetChordalError() + + @chordalError.setter + def chordalError(self, value): + self.hypo.SetChordalError(value) + + @property + def secondOrder(self): + return self.hypo.GetSecondOrder() + + @secondOrder.setter + def secondOrder(self, value): + self.hypo.SetSecondOrder(value) + + @property + def optimize(self): + return self.hypo.GetOptimize() + + @optimize.setter + def optimize(self, value): + self.hypo.SetOptimize(value) + + @property + def quadAllowed(self): + return self.hypo.GetQuadAllowed() + + @quadAllowed.setter + def quadAllowed(self, value): + self.hypo.SetQuadAllowed(value) + + @property + def useSurfaceCurvature(self): + return self.hypo.GetUseSurfaceCurvature() + + @useSurfaceCurvature.setter + def useSurfaceCurvature(self, value): + self.hypo.SetUseSurfaceCurvature(value) + + @property + def fuseEdges(self): + return self.hypo.GetFuseEdges() + + @fuseEdges.setter + def fuseEdges(self, value): + self.hypo.SetFuseEdges(value) + + @property + def checkChartBoundary(self): + return self.hypo.GetCheckChartBoundary() + + @checkChartBoundary.setter + def GetCheckChartBoundary(self, value): + self.hypo.SetCheckChartBoundary(value) + +class MEFISTO(MeshAlgorithm): + pass diff --git a/playground/salome-run.py b/playground/salome-run.py index 2e335e7..732d3f4 100644 --- a/playground/salome-run.py +++ b/playground/salome-run.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- import sys; path = "/home/nafaryus/projects/anisotropy"; sys.path.extend([path, path + "/env/lib/python3.10/site-packages"]) -from anisotropy.samples import Simple, FaceCentered, BodyCentered -s = Simple([1, 0, 0], 0.28, filletsEnabled = True) +from anisotropy.samples.faceCentered import FaceCentered, FaceCenteredMesh +fc = FaceCentered([1, 0, 0], 0.12, filletsEnabled = True) +fc.build() +fcm = FaceCenteredMesh(fc) +fcm.build() diff --git a/requirements.txt b/requirements.txt index dd56a31..72945d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,4 @@ pandas Click matplotlib pyqt5 +PyFoam