diff --git a/ISSUES.rst b/ISSUES.rst new file mode 100644 index 0000000..d2ff61e --- /dev/null +++ b/ISSUES.rst @@ -0,0 +1,16 @@ +List of known issues +==================== + +* ``Click`` // can't hide subcommand from help, ``hidden = True`` doesn't work. +* ``ideasUnvToFoam`` // can't import mesh with '-case' flag (temporary used ``os.chdir``). +* ``salome`` // removes commas from string list (example: "[1, 0, 0]") in the cli arguments. +* ``geompyBuilder`` // missing groups from father object in study tree. +* ``Anisotropy`` // writes ``Done`` status for failed operations (detected on mesh operations). +* ``Database`` // ``WHERE ..`` peewee operation error on update function with all control parameters (type, direction, theta) but fields are written to the database correctly. +* ``Database`` // awkward arguments and their order in the class init function. +* ``Mesh`` // outdated class. +* ``genmesh`` // awkward function, move precalculation parameters to Mesh class. +* ``Anisotropy`` // outdated functions for porosity and etc. +* ``Anisotropy`` // not sufficiently used variable ``env``. +* ``openfoam`` // outdated module, add functionality for FoamFile parsing, ``postProcess`` utility?. +* ``Database`` // add flexibility. \ No newline at end of file diff --git a/anisotropy/core/main.py b/anisotropy/core/main.py index d4e8b3b..39845e4 100644 --- a/anisotropy/core/main.py +++ b/anisotropy/core/main.py @@ -303,9 +303,9 @@ class Anisotropy(object): faceCentered = FaceCentered )[p["structure"]["type"]] shapeGeometry = structure(**p["structure"]) - shape, groups = shapeGeometry.build() + shapeGeometry.build() - [length, surfaceArea, volume] = geompy.BasicProperties(shape, theTolerance = 1e-06) + [length, surfaceArea, volume] = geompy.BasicProperties(shapeGeometry.shape, theTolerance = 1e-06) ### @@ -316,7 +316,7 @@ class Anisotropy(object): mp = p["mesh"] lengths = [ - geompy.BasicProperties(edge)[0] for edge in geompy.SubShapeAll(shape, geompy.ShapeType["EDGE"]) + geompy.BasicProperties(edge)[0] for edge in geompy.SubShapeAll(shapeGeometry.shape, geompy.ShapeType["EDGE"]) ] meanSize = sum(lengths) / len(lengths) mp["maxSize"] = meanSize @@ -324,12 +324,12 @@ class Anisotropy(object): mp["chordalError"] = mp["maxSize"] / 2 faces = [] - for group in groups: + for group in shapeGeometry.groups: if group.GetName() in mp["facesToIgnore"]: faces.append(group) - mesh = salomepl.mesh.Mesh(shape) + mesh = salomepl.mesh.Mesh(shapeGeometry.shape) mesh.Tetrahedron(**mp) if mp["viscousLayers"]: @@ -339,7 +339,7 @@ class Anisotropy(object): smp = p["submesh"] for submesh in smp: - for group in groups: + for group in shapeGeometry.groups: if submesh["name"] == group.GetName(): subshape = group diff --git a/anisotropy/samples/bodyCentered.py b/anisotropy/samples/bodyCentered.py index bdeeb27..fceabe0 100644 --- a/anisotropy/samples/bodyCentered.py +++ b/anisotropy/samples/bodyCentered.py @@ -2,26 +2,22 @@ # This file is part of anisotropy. # License: GNU GPL version 3, see the file "LICENSE" for details. +from anisotropy.samples.structure import StructureGeometry from math import pi, sqrt -from anisotropy.salomepl import geometry - -class BodyCentered(object): - def __init__(self, **kwargs): - - self.direction = kwargs.get("direction", [1, 0, 0]) - self.theta = kwargs.get("theta", 0.01) - self.L = kwargs.get("L", 1) - self.r0 = kwargs.get("r0", self.L * sqrt(3) / 4) - self.radius = kwargs.get("radius", self.r0 / (1 - self.theta)) - self.filletsEnabled = kwargs.get("filletsEnabled", False) - self.fillets = kwargs.get("fillets", 0) - self.volumeCell = None +import logging +class BodyCentered(StructureGeometry): + @property + def name(self): + """Shape name. + """ + return "bodyCentered" + + @property + def L(self): + return self.r0 * 4 / sqrt(3) def build(self): - - geompy = geometry.getGeom() - ### # Pore Cell ## @@ -42,7 +38,7 @@ class BodyCentered(object): zh = height scale = 100 - oo = geompy.MakeVertex(0, 0, 0) + oo = self.geo.MakeVertex(0, 0, 0) spos1 = (0, 0, 0) spos2 = (0, 0, 0) @@ -50,40 +46,41 @@ class BodyCentered(object): # Bounding box ## if self.direction == [1, 0, 0]: - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() sk.addPointsAbsolute(xl, 0, 0) sk.addPointsAbsolute(0, yw, 0) sk.addPointsAbsolute(0, yw, zh) sk.addPointsAbsolute(xl, 0, zh) sk.addPointsAbsolute(xl, 0, 0) - inletface = geompy.MakeFaceWires([sk.wire()], True) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, diag) + inletface = self.geo.MakeFaceWires([sk.wire()], True) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, diag) elif self.direction == [0, 0, 1]: - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() sk.addPointsAbsolute(0, yw, 0) sk.addPointsAbsolute(xl, 0, 0) sk.addPointsAbsolute(2 * xl, yw, 0) sk.addPointsAbsolute(xl, 2 * yw, 0) sk.addPointsAbsolute(0, yw, 0) - inletface = geompy.MakeFaceWires([sk.wire()], True) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, zh) + inletface = self.geo.MakeFaceWires([sk.wire()], True) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, zh) - [_, _, self.volumeCell] = geompy.BasicProperties(poreCell, theTolerance = 1e-06) - inletface = geompy.MakeScaleTransform(inletface, oo, scale) - poreCell = geompy.MakeScaleTransform(poreCell, oo, scale) + self.shapeCell = poreCell + + inletface = self.geo.MakeScaleTransform(inletface, oo, scale) + poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale) - faces = geompy.ExtractShapes(poreCell, geompy.ShapeType["FACE"], False) + faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False) symetryface = [] for face in faces: - norm = geompy.GetNormal(face) - angle = round(geompy.GetAngle(norm, vecflow), 0) + norm = self.geo.GetNormal(face) + angle = round(self.geo.GetAngle(norm, vecflow), 0) if (angle == 0 or angle == 180) and not face == inletface: outletface = face @@ -113,33 +110,33 @@ class BodyCentered(object): point.append((self.L / 3 + xl, self.L / 3 + yw, 4 * self.L / 3 + zh)) scale = 100 - oo = geompy.MakeVertex(0, 0, 0) + oo = self.geo.MakeVertex(0, 0, 0) spos1 = (0, 0, 0) spos2 = (0, 0, 0) ### # Bounding box ## - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() for p in point: sk.addPointsAbsolute(*p) - inletface = geompy.MakeFaceWires([sk.wire()], False) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, self.L * sqrt(3)) + inletface = self.geo.MakeFaceWires([sk.wire()], False) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, self.L * sqrt(3)) - [_, _, self.volumeCell] = geompy.BasicProperties(poreCell, theTolerance = 1e-06) + self.shapeCell = poreCell - inletface = geompy.MakeScaleTransform(inletface, oo, scale) - poreCell = geompy.MakeScaleTransform(poreCell, oo, scale) + inletface = self.geo.MakeScaleTransform(inletface, oo, scale) + poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale) - faces = geompy.ExtractShapes(poreCell, geompy.ShapeType["FACE"], False) + faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False) symetryface = [] for face in faces: - norm = geompy.GetNormal(face) - angle = round(geompy.GetAngle(norm, vecflow), 0) + norm = self.geo.GetNormal(face) + angle = round(self.geo.GetAngle(norm, vecflow), 0) if (angle == 0 or angle == 180) and not face == inletface: outletface = face @@ -153,78 +150,63 @@ class BodyCentered(object): ### # Grains ## - ox = geompy.MakeVectorDXDYDZ(1, 0, 0) - oy = geompy.MakeVectorDXDYDZ(0, 1, 0) - oz = geompy.MakeVectorDXDYDZ(0, 0, 1) - xy = geompy.MakeVectorDXDYDZ(1, 1, 0) - xmy = geompy.MakeVectorDXDYDZ(1, -1, 0) + ox = self.geo.MakeVectorDXDYDZ(1, 0, 0) + oy = self.geo.MakeVectorDXDYDZ(0, 1, 0) + oz = self.geo.MakeVectorDXDYDZ(0, 0, 1) + xy = self.geo.MakeVectorDXDYDZ(1, 1, 0) + xmy = self.geo.MakeVectorDXDYDZ(1, -1, 0) - grain = geompy.MakeSpherePntR(geompy.MakeVertex(*spos1), self.radius) - lattice1 = geompy.MakeMultiTranslation2D(grain, ox, self.L, xn, oy, self.L, yn) - lattice1 = geompy.MakeMultiTranslation1D(lattice1, oz, self.L, zn) + grain = self.geo.MakeSpherePntR(self.geo.MakeVertex(*spos1), self.radius) + lattice1 = self.geo.MakeMultiTranslation2D(grain, ox, self.L, xn, oy, self.L, yn) + lattice1 = self.geo.MakeMultiTranslation1D(lattice1, oz, self.L, zn) - #grain = geompy.MakeSpherePntR(geompy.MakeVertex(*spos2), radius) - #lattice2 = geompy.MakeMultiTranslation2D(grain, xy, length, xn + 1, xmy, length, yn + 1) - #lattice2 = geompy.MakeMultiTranslation1D(lattice2, oz, L, zn) - lattice2 = geompy.MakeTranslation(lattice1, 0.5 * self.L, 0.5 * self.L, 0.5 * self.L) + #grain = self.geo.MakeSpherePntR(self.geo.MakeVertex(*spos2), radius) + #lattice2 = self.geo.MakeMultiTranslation2D(grain, xy, length, xn + 1, xmy, length, yn + 1) + #lattice2 = self.geo.MakeMultiTranslation1D(lattice2, oz, L, zn) + lattice2 = self.geo.MakeTranslation(lattice1, 0.5 * self.L, 0.5 * self.L, 0.5 * self.L) - grains = geompy.ExtractShapes(lattice1, geompy.ShapeType["SOLID"], True) - grains += geompy.ExtractShapes(lattice2, geompy.ShapeType["SOLID"], True) - grains = geompy.MakeFuseList(grains, False, False) + grains = self.geo.ExtractShapes(lattice1, self.geo.ShapeType["SOLID"], True) + grains += self.geo.ExtractShapes(lattice2, self.geo.ShapeType["SOLID"], True) + grains = self.geo.MakeFuseList(grains, False, False) - grains = geompy.MakeScaleTransform(grains, oo, scale) + grains = self.geo.MakeScaleTransform(grains, oo, scale) grainsOrigin = None if self.filletsEnabled: - grainsOrigin = geompy.MakeScaleTransform(grains, oo, 1 / scale) - grains = geompy.MakeFilletAll(grains, self.fillets * scale) + grainsOrigin = self.geo.MakeScaleTransform(grains, oo, 1 / scale) + grains = self.geo.MakeFilletAll(grains, self.fillets * scale) + self.shapeLattice = self.geo.MakeScaleTransform(grains, oo, 1 / scale) + + ### + # Shape + ## + self.shape = self.geo.MakeCutList(poreCell, [grains]) + self.shape = self.geo.MakeScaleTransform(self.shape, oo, 1 / scale, theName = self.name) + + isValid, _ = self.isValid() + + if not isValid: + self.heal() + ### # Groups + # + # inlet, outlet, simetry(N), strips(optional), wall ## - shape = geompy.MakeCutList(poreCell, [grains]) - shape = geompy.MakeScaleTransform(shape, oo, 1 / scale, theName = "bodyCentered") - - sall = geompy.CreateGroup(shape, geompy.ShapeType["FACE"]) - geompy.UnionIDs(sall, - geompy.SubShapeAllIDs(shape, geompy.ShapeType["FACE"])) - - inlet = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "inlet") - inletshape = geompy.MakeCutList(inletface, [grains]) - inletshape = geompy.MakeScaleTransform(inletshape, oo, 1 / scale) - geompy.UnionList(inlet, geompy.SubShapeAll( - geompy.GetInPlace(shape, inletshape, True), geompy.ShapeType["FACE"])) - - outlet = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "outlet") - outletshape = geompy.MakeCutList(outletface, [grains]) - outletshape = geompy.MakeScaleTransform(outletshape, oo, 1 / scale) - geompy.UnionList(outlet, geompy.SubShapeAll( - geompy.GetInPlace(shape, outletshape, True), geompy.ShapeType["FACE"])) + self.groups = [] + groupAll = self.createGroupAll(self.shape) - symetry = [] - for (n, face) in enumerate(symetryface): - name = "symetry" + str(n) - symetry.append(geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = name)) - symetryshape = geompy.MakeCutList(face, [grains]) - symetryshape = geompy.MakeScaleTransform(symetryshape, oo, 1 / scale) - geompy.UnionList(symetry[n], geompy.SubShapeAll( - geompy.GetInPlace(shape, symetryshape, True), geompy.ShapeType["FACE"])) - - groups = [] - groups.append(inlet) - groups.append(outlet) - groups.extend(symetry) + self.groups.append(self.createGroup(self.shape, inletface, "inlet", [grains], 1 / scale)) + self.groups.append(self.createGroup(self.shape, outletface, "outlet", [grains], 1 / scale)) + for n, face in enumerate(symetryface): + self.groups.append(self.createGroup(self.shape, face, f"symetry{ n }", [grains], 1 / scale)) + if self.filletsEnabled: - strips = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "strips") - shapeShell = geompy.ExtractShapes(shape, geompy.ShapeType["SHELL"], True) - stripsShape = geompy.MakeCutList(shapeShell[0], groups + [grainsOrigin]) - geompy.UnionList(strips, geompy.SubShapeAll( - geompy.GetInPlace(shape, stripsShape, True), geompy.ShapeType["FACE"])) - groups.append(strips) - - wall = geompy.CutListOfGroups([sall], groups, theName = "wall") - groups.append(wall) + shapeShell = self.geo.ExtractShapes(self.shape, self.geo.ShapeType["SHELL"], True)[0] + self.groups.append(self.createGroup(self.shape, shapeShell, "strips", self.groups + [grainsOrigin])) - return shape, groups + self.groups.append(self.geo.CutListOfGroups([groupAll], self.groups, theName = "wall")) + diff --git a/anisotropy/samples/faceCentered.py b/anisotropy/samples/faceCentered.py index b1d0b2b..e6782c7 100644 --- a/anisotropy/samples/faceCentered.py +++ b/anisotropy/samples/faceCentered.py @@ -2,26 +2,21 @@ # This file is part of anisotropy. # License: GNU GPL version 3, see the file "LICENSE" for details. +from anisotropy.samples.structure import StructureGeometry from math import pi, sqrt -from anisotropy.salomepl import geometry - -class FaceCentered(object): - def __init__(self, **kwargs): - - self.direction = kwargs.get("direction", [1, 0, 0]) - self.theta = kwargs.get("theta", 0.01) - self.L = kwargs.get("L", 1) - self.r0 = kwargs.get("r0", self.L * sqrt(2) / 4) - self.radius = kwargs.get("radius", self.r0 / (1 - self.theta)) - self.filletsEnabled = kwargs.get("filletsEnabled", False) - self.fillets = kwargs.get("fillets", 0) - self.volumeCell = None - +class FaceCentered(StructureGeometry): + @property + def name(self): + """Shape name. + """ + return "faceCentered" + + @property + def L(self): + return self.r0 * 4 / sqrt(2) + def build(self): - - geompy = geometry.getGeom() - ### # Pore Cell ## @@ -42,7 +37,7 @@ class FaceCentered(object): zh = width scale = 100 - oo = geompy.MakeVertex(0, 0, 0) + oo = self.geo.MakeVertex(0, 0, 0) spos1 = (-width * (xn - 1), 0, -width * (zn - 2)) spos2 = (-width * xn, 0, -width * (zn - 1)) @@ -50,40 +45,40 @@ class FaceCentered(object): # Bounding box ## if self.direction == [1, 0, 0]: - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() sk.addPointsAbsolute(0, 0, -zh) sk.addPointsAbsolute(-xl, yw, -zh) sk.addPointsAbsolute(-xl, yw, zh) sk.addPointsAbsolute(0, 0, zh) sk.addPointsAbsolute(0, 0, -zh) - inletface = geompy.MakeFaceWires([sk.wire()], True) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, length) + inletface = self.geo.MakeFaceWires([sk.wire()], True) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, length) elif self.direction == [0, 0, 1]: - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() sk.addPointsAbsolute(0, 0, -zh) sk.addPointsAbsolute(xl, yw, -zh) sk.addPointsAbsolute(0, 2 * yw, -zh) sk.addPointsAbsolute(-xl, yw, -zh) sk.addPointsAbsolute(0, 0, -zh) - inletface = geompy.MakeFaceWires([sk.wire()], True) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, 2 * zh) + inletface = self.geo.MakeFaceWires([sk.wire()], True) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, 2 * zh) - [_, _, self.volumeCell] = geompy.BasicProperties(poreCell, theTolerance = 1e-06) + self.shapeCell = poreCell - inletface = geompy.MakeScaleTransform(inletface, oo, scale) - poreCell = geompy.MakeScaleTransform(poreCell, oo, scale) + inletface = self.geo.MakeScaleTransform(inletface, oo, scale) + poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale) - faces = geompy.ExtractShapes(poreCell, geompy.ShapeType["FACE"], False) + faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False) symetryface = [] for face in faces: - norm = geompy.GetNormal(face) - angle = round(geompy.GetAngle(norm, vecflow), 0) + norm = self.geo.GetNormal(face) + angle = round(self.geo.GetAngle(norm, vecflow), 0) if (angle == 0 or angle == 180) and not face == inletface: outletface = face @@ -113,33 +108,33 @@ class FaceCentered(object): point.append((-2 * width / 3 + xl, -2 * width / 3 + yw, width / 3 + zh)) scale = 100 - oo = geompy.MakeVertex(0, 0, 0) + oo = self.geo.MakeVertex(0, 0, 0) spos1 = (-width * (xn - 1), 0, -width * (zn - 2)) spos2 = (-width * xn, 0, -width * (zn - 1)) ### # Bounding box ## - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() for p in point: sk.addPointsAbsolute(*p) - inletface = geompy.MakeFaceWires([sk.wire()], False) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, self.L * sqrt(3)) + inletface = self.geo.MakeFaceWires([sk.wire()], False) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, self.L * sqrt(3)) - [_, _, self.volumeCell] = geompy.BasicProperties(poreCell, theTolerance = 1e-06) + self.shapeCell = poreCell - inletface = geompy.MakeScaleTransform(inletface, oo, scale) - poreCell = geompy.MakeScaleTransform(poreCell, oo, scale) + inletface = self.geo.MakeScaleTransform(inletface, oo, scale) + poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale) - faces = geompy.ExtractShapes(poreCell, geompy.ShapeType["FACE"], False) + faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False) symetryface = [] for face in faces: - norm = geompy.GetNormal(face) - angle = round(geompy.GetAngle(norm, vecflow), 0) + norm = self.geo.GetNormal(face) + angle = round(self.geo.GetAngle(norm, vecflow), 0) if (angle == 0 or angle == 180) and not face == inletface: outletface = face @@ -153,78 +148,60 @@ class FaceCentered(object): ### # Grains ## - ox = geompy.MakeVectorDXDYDZ(1, 0, 0) - oy = geompy.MakeVectorDXDYDZ(0, 1, 0) - oz = geompy.MakeVectorDXDYDZ(0, 0, 1) - xy = geompy.MakeVectorDXDYDZ(1, 1, 0) - xmy = geompy.MakeVectorDXDYDZ(1, -1, 0) + ox = self.geo.MakeVectorDXDYDZ(1, 0, 0) + oy = self.geo.MakeVectorDXDYDZ(0, 1, 0) + oz = self.geo.MakeVectorDXDYDZ(0, 0, 1) + xy = self.geo.MakeVectorDXDYDZ(1, 1, 0) + xmy = self.geo.MakeVectorDXDYDZ(1, -1, 0) - grain = geompy.MakeSpherePntR(geompy.MakeVertex(*spos1), self.radius) - lattice1 = geompy.MakeMultiTranslation2D(grain, xy, length, xn, xmy, length, yn) - lattice1 = geompy.MakeMultiTranslation1D(lattice1, oz, self.L, zn - 1) + grain = self.geo.MakeSpherePntR(self.geo.MakeVertex(*spos1), self.radius) + lattice1 = self.geo.MakeMultiTranslation2D(grain, xy, length, xn, xmy, length, yn) + lattice1 = self.geo.MakeMultiTranslation1D(lattice1, oz, self.L, zn - 1) - grain = geompy.MakeSpherePntR(geompy.MakeVertex(*spos2), self.radius) - lattice2 = geompy.MakeMultiTranslation2D(grain, xy, length, xn + 1, xmy, length, yn + 1) - lattice2 = geompy.MakeMultiTranslation1D(lattice2, oz, self.L, zn) + grain = self.geo.MakeSpherePntR(self.geo.MakeVertex(*spos2), self.radius) + lattice2 = self.geo.MakeMultiTranslation2D(grain, xy, length, xn + 1, xmy, length, yn + 1) + lattice2 = self.geo.MakeMultiTranslation1D(lattice2, oz, self.L, zn) - grains = geompy.ExtractShapes(lattice1, geompy.ShapeType["SOLID"], True) - grains += geompy.ExtractShapes(lattice2, geompy.ShapeType["SOLID"], True) - grains = geompy.MakeFuseList(grains, False, False) + grains = self.geo.ExtractShapes(lattice1, self.geo.ShapeType["SOLID"], True) + grains += self.geo.ExtractShapes(lattice2, self.geo.ShapeType["SOLID"], True) + grains = self.geo.MakeFuseList(grains, False, False) - grains = geompy.MakeScaleTransform(grains, oo, scale) + grains = self.geo.MakeScaleTransform(grains, oo, scale) grainsOrigin = None if self.filletsEnabled: - grainsOrigin = geompy.MakeScaleTransform(grains, oo, 1 / scale) - grains = geompy.MakeFilletAll(grains, self.fillets * scale) + grainsOrigin = self.geo.MakeScaleTransform(grains, oo, 1 / scale) + grains = self.geo.MakeFilletAll(grains, self.fillets * scale) + self.shapeLattice = self.geo.MakeScaleTransform(grains, oo, 1 / scale) + + ### + # Shape + ## + self.shape = self.geo.MakeCutList(poreCell, [grains]) + self.shape = self.geo.MakeScaleTransform(self.shape, oo, 1 / scale, theName = self.name) + + isValid, _ = self.isValid() + + if not isValid: + self.heal() + ### # Groups + # + # inlet, outlet, simetry(N), strips(optional), wall ## - shape = geompy.MakeCutList(poreCell, [grains]) - shape = geompy.MakeScaleTransform(shape, oo, 1 / scale, theName = "faceCentered") + self.groups = [] + groupAll = self.createGroupAll(self.shape) - sall = geompy.CreateGroup(shape, geompy.ShapeType["FACE"]) - geompy.UnionIDs(sall, - geompy.SubShapeAllIDs(shape, geompy.ShapeType["FACE"])) - - inlet = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "inlet") - inletshape = geompy.MakeCutList(inletface, [grains]) - inletshape = geompy.MakeScaleTransform(inletshape, oo, 1 / scale) - geompy.UnionList(inlet, geompy.SubShapeAll( - geompy.GetInPlace(shape, inletshape, True), geompy.ShapeType["FACE"])) - - outlet = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "outlet") - outletshape = geompy.MakeCutList(outletface, [grains]) - outletshape = geompy.MakeScaleTransform(outletshape, oo, 1 / scale) - geompy.UnionList(outlet, geompy.SubShapeAll( - geompy.GetInPlace(shape, outletshape, True), geompy.ShapeType["FACE"])) - - symetry = [] - for (n, face) in enumerate(symetryface): - name = "symetry" + str(n) - symetry.append(geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = name)) - symetryshape = geompy.MakeCutList(face, [grains]) - symetryshape = geompy.MakeScaleTransform(symetryshape, oo, 1 / scale) - geompy.UnionList(symetry[n], geompy.SubShapeAll( - geompy.GetInPlace(shape, symetryshape, True), geompy.ShapeType["FACE"])) - - groups = [] - groups.append(inlet) - groups.append(outlet) - groups.extend(symetry) + self.groups.append(self.createGroup(self.shape, inletface, "inlet", [grains], 1 / scale)) + self.groups.append(self.createGroup(self.shape, outletface, "outlet", [grains], 1 / scale)) + for n, face in enumerate(symetryface): + self.groups.append(self.createGroup(self.shape, face, f"symetry{ n }", [grains], 1 / scale)) + if self.filletsEnabled: - strips = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "strips") - shapeShell = geompy.ExtractShapes(shape, geompy.ShapeType["SHELL"], True) - stripsShape = geompy.MakeCutList(shapeShell[0], groups + [grainsOrigin]) - geompy.UnionList(strips, geompy.SubShapeAll( - geompy.GetInPlace(shape, stripsShape, True), geompy.ShapeType["FACE"])) - groups.append(strips) - - wall = geompy.CutListOfGroups([sall], groups, theName = "wall") - groups.append(wall) - - return shape, groups - + shapeShell = self.geo.ExtractShapes(self.shape, self.geo.ShapeType["SHELL"], True)[0] + self.groups.append(self.createGroup(self.shape, shapeShell, "strips", self.groups + [grainsOrigin])) + self.groups.append(self.geo.CutListOfGroups([groupAll], self.groups, theName = "wall")) \ No newline at end of file diff --git a/anisotropy/samples/simple.py b/anisotropy/samples/simple.py index 000ecad..3788f69 100644 --- a/anisotropy/samples/simple.py +++ b/anisotropy/samples/simple.py @@ -2,26 +2,21 @@ # This file is part of anisotropy. # License: GNU GPL version 3, see the file "LICENSE" for details. +from anisotropy.samples.structure import StructureGeometry from math import pi, sqrt -from anisotropy.salomepl import geometry - -class Simple(object): - def __init__(self, **kwargs): - - self.direction = kwargs.get("direction", [1, 0, 0]) - self.theta = kwargs.get("theta", 0.01) - self.r0 = kwargs.get("r0", 1) - self.L = kwargs.get("L", 2 * self.r0) - self.radius = kwargs.get("radius", self.r0 / (1 - self.theta)) - self.filletsEnabled = kwargs.get("filletsEnabled", False) - self.fillets = kwargs.get("fillets", 0) - self.volumeCell = None +class Simple(StructureGeometry): + @property + def name(self): + """Shape name. + """ + return "simple" + + @property + def L(self): + return 2 * self.r0 def build(self): - - geompy = geometry.getGeom() - ### # Pore Cell ## @@ -40,46 +35,46 @@ class Simple(object): zh = height scale = 100 - oo = geompy.MakeVertex(0, 0, 0) + oo = self.geo.MakeVertex(0, 0, 0) ### # Bounding box ## if self.direction == [1, 0, 0]: - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() sk.addPointsAbsolute(xl, 0, 0) sk.addPointsAbsolute(0, yw, 0) sk.addPointsAbsolute(0, yw, zh) sk.addPointsAbsolute(xl, 0, zh) sk.addPointsAbsolute(xl, 0, 0) - inletface = geompy.MakeFaceWires([sk.wire()], True) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, width) + inletface = self.geo.MakeFaceWires([sk.wire()], True) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, width) elif self.direction == [0, 0, 1]: - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() sk.addPointsAbsolute(0, yw, 0) sk.addPointsAbsolute(xl, 0, 0) sk.addPointsAbsolute(2 * xl, yw, 0) sk.addPointsAbsolute(xl, 2 * yw, 0) sk.addPointsAbsolute(0, yw, 0) - inletface = geompy.MakeFaceWires([sk.wire()], True) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, height) + inletface = self.geo.MakeFaceWires([sk.wire()], True) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, height) - [_, _, self.volumeCell] = geompy.BasicProperties(poreCell, theTolerance = 1e-06) + self.shapeCell = poreCell - inletface = geompy.MakeScaleTransform(inletface, oo, scale) - poreCell = geompy.MakeScaleTransform(poreCell, oo, scale) + inletface = self.geo.MakeScaleTransform(inletface, oo, scale) + poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale) - faces = geompy.ExtractShapes(poreCell, geompy.ShapeType["FACE"], False) + faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False) symetryface = [] for face in faces: - norm = geompy.GetNormal(face) - angle = round(geompy.GetAngle(norm, vecflow), 0) + norm = self.geo.GetNormal(face) + angle = round(self.geo.GetAngle(norm, vecflow), 0) if (angle == 0 or angle == 180) and not face == inletface: outletface = face @@ -108,31 +103,31 @@ class Simple(object): point.append((self.L + xl, self.L + yw, self.L + zh)) scale = 100 - oo = geompy.MakeVertex(0, 0, 0) + oo = self.geo.MakeVertex(0, 0, 0) ### # Bounding box ## - sk = geompy.Sketcher3D() + sk = self.geo.Sketcher3D() for p in point: sk.addPointsAbsolute(*p) - inletface = geompy.MakeFaceWires([sk.wire()], False) - vecflow = geompy.GetNormal(inletface) - poreCell = geompy.MakePrismVecH(inletface, vecflow, self.L * sqrt(3)) + inletface = self.geo.MakeFaceWires([sk.wire()], False) + vecflow = self.geo.GetNormal(inletface) + poreCell = self.geo.MakePrismVecH(inletface, vecflow, self.L * sqrt(3)) - [_, _, self.volumeCell] = geompy.BasicProperties(poreCell, theTolerance = 1e-06) + self.shapeCell = poreCell - inletface = geompy.MakeScaleTransform(inletface, oo, scale) - poreCell = geompy.MakeScaleTransform(poreCell, oo, scale) + inletface = self.geo.MakeScaleTransform(inletface, oo, scale) + poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale) - faces = geompy.ExtractShapes(poreCell, geompy.ShapeType["FACE"], False) + faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False) symetryface = [] for face in faces: - norm = geompy.GetNormal(face) - angle = round(geompy.GetAngle(norm, vecflow), 0) + norm = self.geo.GetNormal(face) + angle = round(self.geo.GetAngle(norm, vecflow), 0) if (angle == 0 or angle == 180) and not face == inletface: outletface = face @@ -146,70 +141,53 @@ class Simple(object): ### # Grains ## - ox = geompy.MakeVectorDXDYDZ(1, 0, 0) - oy = geompy.MakeVectorDXDYDZ(0, 1, 0) - oz = geompy.MakeVectorDXDYDZ(0, 0, 1) + ox = self.geo.MakeVectorDXDYDZ(1, 0, 0) + oy = self.geo.MakeVectorDXDYDZ(0, 1, 0) + oz = self.geo.MakeVectorDXDYDZ(0, 0, 1) - grain = geompy.MakeSphereR(self.radius) - lattice = geompy.MakeMultiTranslation2D(grain, ox, self.L, xn, oy, self.L, yn) - lattice = geompy.MakeMultiTranslation1D(lattice, oz, self.L, zn) + grain = self.geo.MakeSphereR(self.radius) + lattice = self.geo.MakeMultiTranslation2D(grain, ox, self.L, xn, oy, self.L, yn) + lattice = self.geo.MakeMultiTranslation1D(lattice, oz, self.L, zn) - grains = geompy.ExtractShapes(lattice, geompy.ShapeType["SOLID"], True) - grains = geompy.MakeFuseList(grains, False, False) + grains = self.geo.ExtractShapes(lattice, self.geo.ShapeType["SOLID"], True) + grains = self.geo.MakeFuseList(grains, False, False) - grains = geompy.MakeScaleTransform(grains, oo, scale) + grains = self.geo.MakeScaleTransform(grains, oo, scale) grainsOrigin = None if self.filletsEnabled: - grainsOrigin = geompy.MakeScaleTransform(grains, oo, 1 / scale) - grains = geompy.MakeFilletAll(grains, self.fillets * scale) + grainsOrigin = self.geo.MakeScaleTransform(grains, oo, 1 / scale) + grains = self.geo.MakeFilletAll(grains, self.fillets * scale) + self.shapeLattice = self.geo.MakeScaleTransform(grains, oo, 1 / scale) + + ### + # Shape + ## + self.shape = self.geo.MakeCutList(poreCell, [grains]) + self.shape = self.geo.MakeScaleTransform(self.shape, oo, 1 / scale, theName = self.name) + + isValid, _ = self.isValid() + + if not isValid: + self.heal() + ### # Groups + # + # inlet, outlet, simetry(N), strips(optional), wall ## - shape = geompy.MakeCutList(poreCell, [grains]) - shape = geompy.MakeScaleTransform(shape, oo, 1 / scale, theName = "simple") - - sall = geompy.CreateGroup(shape, geompy.ShapeType["FACE"]) - geompy.UnionIDs(sall, - geompy.SubShapeAllIDs(shape, geompy.ShapeType["FACE"])) - - inlet = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "inlet") - inletshape = geompy.MakeCutList(inletface, [grains]) - inletshape = geompy.MakeScaleTransform(inletshape, oo, 1 / scale) - geompy.UnionList(inlet, geompy.SubShapeAll( - geompy.GetInPlace(shape, inletshape, True), geompy.ShapeType["FACE"])) - - outlet = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "outlet") - outletshape = geompy.MakeCutList(outletface, [grains]) - outletshape = geompy.MakeScaleTransform(outletshape, oo, 1 / scale) - geompy.UnionList(outlet, geompy.SubShapeAll( - geompy.GetInPlace(shape, outletshape, True), geompy.ShapeType["FACE"])) + self.groups = [] + groupAll = self.createGroupAll(self.shape) - symetry = [] - for (n, face) in enumerate(symetryface): - name = "symetry" + str(n) - symetry.append(geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = name)) - symetryshape = geompy.MakeCutList(face, [grains]) - symetryshape = geompy.MakeScaleTransform(symetryshape, oo, 1 / scale) - geompy.UnionList(symetry[n], geompy.SubShapeAll( - geompy.GetInPlace(shape, symetryshape, True), geompy.ShapeType["FACE"])) + self.groups.append(self.createGroup(self.shape, inletface, "inlet", [grains], 1 / scale)) + self.groups.append(self.createGroup(self.shape, outletface, "outlet", [grains], 1 / scale)) - groups = [] - groups.append(inlet) - groups.append(outlet) - groups.extend(symetry) - + for n, face in enumerate(symetryface): + self.groups.append(self.createGroup(self.shape, face, f"symetry{ n }", [grains], 1 / scale)) + if self.filletsEnabled: - strips = geompy.CreateGroup(shape, geompy.ShapeType["FACE"], theName = "strips") - shapeShell = geompy.ExtractShapes(shape, geompy.ShapeType["SHELL"], True) - stripsShape = geompy.MakeCutList(shapeShell[0], groups + [grainsOrigin]) - geompy.UnionList(strips, geompy.SubShapeAll( - geompy.GetInPlace(shape, stripsShape, True), geompy.ShapeType["FACE"])) - groups.append(strips) - - wall = geompy.CutListOfGroups([sall], groups, theName = "wall") - groups.append(wall) - - return shape, groups + shapeShell = self.geo.ExtractShapes(self.shape, self.geo.ShapeType["SHELL"], True)[0] + self.groups.append(self.createGroup(self.shape, shapeShell, "strips", self.groups + [grainsOrigin])) + self.groups.append(self.geo.CutListOfGroups([groupAll], self.groups, theName = "wall")) \ No newline at end of file diff --git a/anisotropy/samples/structure.py b/anisotropy/samples/structure.py new file mode 100644 index 0000000..bd02f31 --- /dev/null +++ b/anisotropy/samples/structure.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + +from anisotropy.salomepl import geometry + +class StructureGeometry(object): + def __init__( + self, + direction: list = None, + theta: float = None, + r0: float = None, + #L: float = None, + #radius: float = None, + filletsEnabled: bool = False, + fillets: float = None, + **kwargs + ): + """Constructor method. + + :param direction: + Flow vector that characterizes geometry. + + :param theta: + Spheres overlap parameter. + + :param r0: + Initial spheres radius. + + :param filletsEnabled: + Enable fillets beetween spheres. + + :param fillets: + Fillets radius. + """ + # Geometry parameters + self.direction = direction + self.theta = theta + self.r0 = r0 + self.filletsEnabled = filletsEnabled + self.fillets = fillets + + # General attributes + self.geo = geometry.getGeom() + self.shape = None + self.groups = [] + self.shapeCell = None + self.shapeLattice = None + + @property + def name(self): + """(Override) Shape name. + """ + pass + + @property + def L(self): + """(Override) Parameter depending on the ``r0``. + """ + pass + + @property + def radius(self): + """Spheres radius + """ + return self.r0 / (1 - self.theta) + + @property + def volumeCell(self): + """General volume of the cell. + """ + return self.geo.BasicProperties(self.shapeCell, theTolerance = 1e-06)[2] + + @property + def volume(self): + """Volume of the structure. + """ + return self.geo.BasicProperties(self.shape, theTolerance = 1e-06)[2] + + @property + def porosity(self): + """Porosity of the structure. + """ + return self.volume / self.volumeCell + + def build(self): + """(Override) Construct shape and physical groups. + """ + pass + + def isValid(self) -> (bool, str): + """Check a topology of the given shape. + + :return: + True, if the shape "seems to be valid" else False and description. + """ + return self.geo.CheckShape(self.shape, theIsCheckGeom = True, theReturnStatus = 1) + + def heal(self): + """Try to heal the shape. + """ + self.shape = self.geo.RemoveExtraEdges(self.shape, doUnionFaces = False) + + def createGroupAll(self, mainShape): + """Create group from all the shape faces. + + :param mainShape: + Input shape. + + :return: + Created group. + """ + group = self.geo.CreateGroup(mainShape, self.geo.ShapeType["FACE"]) + + self.geo.UnionIDs( + group, + self.geo.SubShapeAllIDs(mainShape, self.geo.ShapeType["FACE"]) + ) + + return group + + def createGroup(self, mainShape, subShape, name: str, cutShapes: list = None, scaleTransform: float = None): + """Create group from the sub shape. + + :param mainShape: + Input shape. + + :param subShape: + Input sub shape. + + :param name: + Name of the new group. + + :param cutShapes: + List of shapes for cut from the sub shape. + + :param scaleTransform: + Value of the scale transform regarding to the zero point. + + :return: + Created group. + """ + group = self.geo.CreateGroup(mainShape, self.geo.ShapeType["FACE"], theName = name) + + if cutShapes: + subShape = self.geo.MakeCutList(subShape, cutShapes) + + if scaleTransform: + subShape = self.geo.MakeScaleTransform(subShape, self.geo.MakeVertex(0, 0, 0), scaleTransform) + + self.geo.UnionList( + group, + self.geo.SubShapeAll( + self.geo.GetInPlace(mainShape, subShape, True), + self.geo.ShapeType["FACE"] + ) + ) + + return group \ No newline at end of file diff --git a/playground/analytics.ipynb b/playground/analytics.ipynb index 281a523..6bcd0d3 100644 --- a/playground/analytics.ipynb +++ b/playground/analytics.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 14, + "execution_count": 1, "id": "cbaf1c39-e423-47a3-a3ea-e78a528bc4c1", "metadata": {}, "outputs": [], @@ -21,7 +21,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 2, "id": "09b1bf83-6b42-4144-81a0-b57c661181b1", "metadata": {}, "outputs": [], @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 3, "id": "f341fbd4-797b-4d5d-8393-0f7cf2e67883", "metadata": {}, "outputs": [], @@ -53,7 +53,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 4, "id": "c2dbce30-d80a-4b78-b2a6-cf9c2075f5b5", "metadata": {}, "outputs": [], @@ -64,17 +64,17 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 5, "id": "d182461e-1e42-4e0b-8dd2-ba6425654ee7", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 18, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" }, @@ -100,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 6, "id": "7b7092aa-de0e-430c-9ed5-07483b278d45", "metadata": {}, "outputs": [ @@ -528,7 +528,7 @@ "83 simple [1.0, 1.0, 1.0] 0.28 Done Failed" ] }, - "execution_count": 36, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -546,7 +546,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 12, "id": "1a25020c-5bcc-4e46-985f-cfe55c30d117", "metadata": {}, "outputs": [ @@ -556,7 +556,7 @@ "" ] }, - "execution_count": 41, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, @@ -582,6 +582,37 @@ "seaborn.lineplot(data = simple3, x = \"theta\", y = \"maxSize\", color = \"black\", label = \"maxSize\")" ] }, + { + "cell_type": "code", + "execution_count": 13, + "id": "78e0c05f-3161-404a-9ed7-cd9ebc981a43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "seaborn.scatterplot(data = simple3.round({ \"fillets\": 2 }), x = \"theta\", y = \"fillets\", hue = \"meshStatus\")" + ] + }, { "cell_type": "code", "execution_count": 42, diff --git a/playground/woPrismaticLayer-2/anisotropy.db b/playground/woPrismaticLayer-2/anisotropy.db index b161e33..89dda2c 100644 Binary files a/playground/woPrismaticLayer-2/anisotropy.db and b/playground/woPrismaticLayer-2/anisotropy.db differ