Mod: new tests
Mod: working core runner
This commit is contained in:
parent
c56504f120
commit
8f4c664a74
@ -2,4 +2,5 @@
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
|
||||
from .config import Config, DefaultConfig
|
||||
from .runner import UltimateRunner
|
||||
|
@ -96,14 +96,14 @@ class DefaultConfig(Config):
|
||||
self.options = deepcopy(self.content["options"])
|
||||
|
||||
labels = ["simple", "bodyCentered", "faceCentered"]
|
||||
thetas = array([[0.01, 0.28], [0.01, 0.17], [0.01, 0.13]], dtype = float)
|
||||
thetas = [[0.01, 0.28], [0.01, 0.17], [0.01, 0.13]]
|
||||
|
||||
for label, theta in zip(labels, thetas):
|
||||
self.content["structures"].append({
|
||||
"label": label,
|
||||
"theta": theta,
|
||||
"thetaStep": 0.01,
|
||||
"directions": array([[1, 0, 0], [0, 0, 1], [1, 1, 1]], dtype = float),
|
||||
"directions": [[1, 0, 0], [0, 0, 1], [1, 1, 1]],
|
||||
"r0": 1,
|
||||
"filletsEnabled": True
|
||||
})
|
||||
|
@ -1,405 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
import os
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
|
||||
from anisotropy import env
|
||||
from anisotropy.core.utils import setupLogger
|
||||
from anisotropy.core.models import (
|
||||
db, JOIN,
|
||||
Structure,
|
||||
Mesh, SubMesh, MeshResult,
|
||||
Flow, FlowApproximation, FlowResult
|
||||
)
|
||||
from peewee import OperationalError
|
||||
|
||||
logger = logging.getLogger(env["logger_name"])
|
||||
#setupLogger(logger, logging.INFO, env["LOG"])
|
||||
|
||||
# NOTE: Temporary solution for multiprocessing mode when database is locked
|
||||
def tryUntilDone(func):
|
||||
def inner(*args, **kwargs):
|
||||
done = False
|
||||
|
||||
while not done:
|
||||
try:
|
||||
ret = func(*args, **kwargs)
|
||||
done = True
|
||||
|
||||
except OperationalError as e:
|
||||
logger.error(e)
|
||||
|
||||
return ret
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
class Database(object):
|
||||
def __init__(self, name: str, filepath: str):
|
||||
self.name = name
|
||||
self.filepath = filepath
|
||||
self.__db = db
|
||||
|
||||
|
||||
def setup(self):
|
||||
os.makedirs(self.filepath, exist_ok = True)
|
||||
|
||||
fullpath = os.path.join(self.filepath, "{}.db".format(self.name))
|
||||
self.__db.init(fullpath)
|
||||
|
||||
if not os.path.exists(fullpath):
|
||||
self.__db.create_tables([
|
||||
Structure,
|
||||
Mesh,
|
||||
SubMesh,
|
||||
MeshResult,
|
||||
Flow,
|
||||
FlowApproximation,
|
||||
FlowResult
|
||||
])
|
||||
|
||||
|
||||
def isempty(self) -> bool:
|
||||
"""Checks DB for main table existence (Structure)
|
||||
|
||||
:return: True if exists
|
||||
:rtype: bool
|
||||
"""
|
||||
query = Structure.select()
|
||||
|
||||
return not query.exists()
|
||||
|
||||
|
||||
def load(self, structure_type: str, structure_direction: list, structure_theta: float) -> dict:
|
||||
structureQuery = (
|
||||
Structure
|
||||
.select()
|
||||
.where(
|
||||
Structure.type == structure_type,
|
||||
Structure.direction == str(structure_direction),
|
||||
Structure.theta == structure_theta
|
||||
)
|
||||
)
|
||||
|
||||
params = {}
|
||||
|
||||
with self.__db.atomic():
|
||||
if structureQuery.exists():
|
||||
params["structure"] = structureQuery.dicts().get()
|
||||
|
||||
meshQuery = structureQuery.get().meshes
|
||||
|
||||
if meshQuery.exists():
|
||||
params["mesh"] = meshQuery.dicts().get()
|
||||
|
||||
submeshQuery = meshQuery.get().submeshes
|
||||
|
||||
if submeshQuery.exists():
|
||||
params["submesh"] = [ entry for entry in submeshQuery.dicts() ]
|
||||
|
||||
meshresultQuery = meshQuery.get().meshresults
|
||||
|
||||
if meshresultQuery.exists():
|
||||
params["meshresult"] = meshresultQuery.dicts().get()
|
||||
|
||||
flowQuery = structureQuery.get().flows
|
||||
|
||||
if flowQuery.exists():
|
||||
params["flow"] = flowQuery.dicts().get()
|
||||
|
||||
flowapproximationQuery = flowQuery.get().flowapproximations
|
||||
|
||||
if flowapproximationQuery.exists():
|
||||
params["flowapproximation"] = flowapproximationQuery.dicts().get()
|
||||
|
||||
flowresultsQuery = flowQuery.get().flowresults
|
||||
|
||||
if flowresultsQuery.exists():
|
||||
params["flowresult"] = flowresultsQuery.dicts().get()
|
||||
else:
|
||||
logger.error("Missed Structure table")
|
||||
|
||||
return params
|
||||
|
||||
|
||||
def loadGeneral(self, type: str = None, direction: list = None, theta: float = None) -> list:
|
||||
query = (
|
||||
Structure
|
||||
.select()
|
||||
.order_by(Structure.type, Structure.direction, Structure.theta)
|
||||
)
|
||||
response = []
|
||||
|
||||
if type:
|
||||
query = query.where(Structure.type == type)
|
||||
|
||||
if direction:
|
||||
query = query.where(Structure.direction == str(direction))
|
||||
|
||||
if theta:
|
||||
query = query.where(Structure.theta == theta)
|
||||
|
||||
for entry in query.dicts():
|
||||
response.append({ "structure": entry })
|
||||
|
||||
return response
|
||||
|
||||
|
||||
def update(self, params: dict):
|
||||
if not params:
|
||||
logger.error("Trying to update db from empty parameters")
|
||||
return
|
||||
|
||||
query = (
|
||||
Structure
|
||||
.select(Structure, Mesh, Flow)
|
||||
.join(
|
||||
Mesh,
|
||||
JOIN.INNER,
|
||||
on = (Mesh.structure_id == Structure.structure_id)
|
||||
)
|
||||
.join(
|
||||
Flow,
|
||||
JOIN.INNER,
|
||||
on = (Flow.structure_id == Structure.structure_id)
|
||||
)
|
||||
.where(
|
||||
Structure.type == params["structure"]["type"],
|
||||
Structure.direction == str(params["structure"]["direction"]),
|
||||
Structure.theta == params["structure"]["theta"]
|
||||
)
|
||||
)
|
||||
|
||||
structureID = tryUntilDone(self._updateStructure)(params.get("structure", {}), query)
|
||||
|
||||
meshID = tryUntilDone(self._updateMesh)(params.get("mesh", {}), query, structureID)
|
||||
|
||||
for submeshParams in params.get("submesh", []):
|
||||
tryUntilDone(self._updateSubMesh)(submeshParams, query, meshID)
|
||||
|
||||
tryUntilDone(self._updateMeshResult)(params.get("meshresult", {}), query, meshID)
|
||||
|
||||
flowID = tryUntilDone(self._updateFlow)(params.get("flow", {}), query, structureID)
|
||||
|
||||
tryUntilDone(self._updateFlowApproximation)(params.get("flowapproximation", {}), query, flowID)
|
||||
|
||||
tryUntilDone(self._updateFlowResult)(params.get("flowresult", {}), query, flowID)
|
||||
|
||||
|
||||
def search(self, args: list):
|
||||
result = {}
|
||||
query = (
|
||||
Structure
|
||||
.select(Structure, Mesh, SubMesh, MeshResult, Flow, FlowApproximation, FlowResult)
|
||||
.join(
|
||||
Mesh,
|
||||
JOIN.INNER,
|
||||
on = (Mesh.structure_id == Structure.structure_id)
|
||||
)
|
||||
.join(
|
||||
SubMesh,
|
||||
JOIN.INNER,
|
||||
on = (SubMesh.mesh_id == Mesh.mesh_id)
|
||||
)
|
||||
.join(
|
||||
MeshResult,
|
||||
JOIN.INNER,
|
||||
on = (MeshResult.mesh_id == Mesh.mesh_id)
|
||||
)
|
||||
.join(
|
||||
Flow,
|
||||
JOIN.INNER,
|
||||
on = (Flow.structure_id == Structure.structure_id)
|
||||
)
|
||||
.join(
|
||||
FlowApproximation,
|
||||
JOIN.INNER,
|
||||
on = (FlowApproximation.flow_id == Flow.flow_id)
|
||||
)
|
||||
.join(
|
||||
FlowResult,
|
||||
JOIN.INNER,
|
||||
on = (FlowResult.flow_id == Flow.flow_id)
|
||||
)
|
||||
)
|
||||
|
||||
for arg in args:
|
||||
query = query.where(arg)
|
||||
|
||||
|
||||
with self.__db.atomic():
|
||||
if not self.isempty():
|
||||
result = [ entry for entry in query.dicts() ]
|
||||
|
||||
else:
|
||||
logger.error("Missed Structure table")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def _updateStructure(self, src: dict, queryMain) -> int:
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not queryMain.exists():
|
||||
tabID = Structure.create(**raw)
|
||||
|
||||
else:
|
||||
req = queryMain.dicts().get()
|
||||
tabID = req["structure_id"]
|
||||
|
||||
query = (
|
||||
Structure.update(**raw)
|
||||
.where(
|
||||
Structure.type == req["type"],
|
||||
Structure.direction == str(req["direction"]),
|
||||
Structure.theta == req["theta"]
|
||||
)
|
||||
)
|
||||
query.execute()
|
||||
|
||||
return tabID
|
||||
|
||||
def _updateMesh(self, src: dict, queryMain, structureID) -> int:
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not queryMain.exists():
|
||||
tabID = Mesh.create(
|
||||
structure_id = structureID,
|
||||
**raw
|
||||
)
|
||||
|
||||
else:
|
||||
req = queryMain.dicts().get()
|
||||
tabID = req["mesh_id"]
|
||||
|
||||
query = (
|
||||
Mesh.update(**raw)
|
||||
.where(
|
||||
Mesh.structure_id == structureID
|
||||
)
|
||||
)
|
||||
query.execute()
|
||||
|
||||
return tabID
|
||||
|
||||
def _updateSubMesh(self, src: dict, queryMain, meshID):
|
||||
#if not src:
|
||||
# return
|
||||
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not SubMesh.select().where(SubMesh.mesh_id == meshID).exists():
|
||||
tabID = SubMesh.create(
|
||||
mesh_id = meshID,
|
||||
**raw
|
||||
)
|
||||
logger.debug(f"[ DB ] Created SubMesh entry { tabID }")
|
||||
|
||||
else:
|
||||
query = (
|
||||
SubMesh.update(**raw)
|
||||
.where(
|
||||
SubMesh.mesh_id == meshID,
|
||||
SubMesh.name == src["name"]
|
||||
)
|
||||
)
|
||||
query.execute()
|
||||
|
||||
def _updateMeshResult(self, src: dict, queryMain, meshID):
|
||||
#if not src:
|
||||
# return
|
||||
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not MeshResult.select().where(MeshResult.mesh_id == meshID).exists():
|
||||
tabID = MeshResult.create(
|
||||
mesh_id = meshID,
|
||||
**raw
|
||||
)
|
||||
logger.debug(f"[ DB ] Created MeshResult entry { tabID }")
|
||||
|
||||
else:
|
||||
query = (
|
||||
MeshResult.update(**raw)
|
||||
.where(
|
||||
MeshResult.mesh_id == meshID
|
||||
)
|
||||
)
|
||||
query.execute()
|
||||
|
||||
def _updateFlow(self, src: dict, queryMain, structureID):
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not queryMain.exists():
|
||||
tabID = Flow.create(
|
||||
structure_id = structureID,
|
||||
**raw
|
||||
)
|
||||
|
||||
else:
|
||||
req = queryMain.dicts().get()
|
||||
tabID = req["flow_id"]
|
||||
|
||||
query = (
|
||||
Flow.update(**raw)
|
||||
.where(
|
||||
Flow.structure_id == structureID
|
||||
)
|
||||
)
|
||||
query.execute()
|
||||
|
||||
return tabID
|
||||
|
||||
def _updateFlowApproximation(self, src: dict, queryMain, flowID):
|
||||
#if not src:
|
||||
# return
|
||||
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not FlowApproximation.select().where(FlowApproximation.flow_id == flowID).exists():
|
||||
tabID = FlowApproximation.create(
|
||||
flow_id = flowID,
|
||||
**raw
|
||||
)
|
||||
logger.debug(f"[ DB ] Created FlowApproximation entry { tabID }")
|
||||
|
||||
else:
|
||||
query = (
|
||||
FlowApproximation.update(**raw)
|
||||
.where(
|
||||
FlowApproximation.flow_id == flowID
|
||||
)
|
||||
)
|
||||
query.execute()
|
||||
|
||||
def _updateFlowResult(self, src: dict, queryMain, flowID):
|
||||
#if not src:
|
||||
# return
|
||||
|
||||
raw = deepcopy(src)
|
||||
|
||||
with self.__db.atomic():
|
||||
if not FlowResult.select().where(FlowResult.flow_id == flowID).exists():
|
||||
tabID = FlowResult.create(
|
||||
flow_id = flowID,
|
||||
**raw
|
||||
)
|
||||
logger.debug(f"[ DB ] Created FlowResult entry { tabID }")
|
||||
|
||||
else:
|
||||
query = (
|
||||
FlowResult.update(**raw)
|
||||
.where(
|
||||
FlowResult.flow_id == flowID
|
||||
)
|
||||
)
|
||||
query.execute()
|
@ -1,185 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
from peewee import (
|
||||
SqliteDatabase, JOIN,
|
||||
Model, Field,
|
||||
AutoField, ForeignKeyField,
|
||||
TextField, FloatField,
|
||||
IntegerField, BooleanField,
|
||||
TimeField, DateTimeField
|
||||
)
|
||||
import json
|
||||
|
||||
db = SqliteDatabase(
|
||||
None,
|
||||
pragmas = { "foreign_keys": 1 },
|
||||
field_types = { "list": "text" }
|
||||
)
|
||||
|
||||
class BaseModel(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
|
||||
|
||||
class ListField(Field):
|
||||
field_type = "list"
|
||||
|
||||
def db_value(self, value):
|
||||
return str(value)
|
||||
|
||||
def python_value(self, value):
|
||||
pval = []
|
||||
|
||||
for entry in value[1 : -1].split(","):
|
||||
try:
|
||||
pval.append(float(entry))
|
||||
|
||||
except:
|
||||
pval.append(entry.strip().replace("'", ""))
|
||||
|
||||
return pval
|
||||
|
||||
|
||||
class JSONField(TextField):
|
||||
def db_value(self, value):
|
||||
return json.dumps(value)
|
||||
|
||||
def python_value(self, value):
|
||||
if value is not None:
|
||||
return json.loads(value)
|
||||
|
||||
|
||||
class Execution(Model):
|
||||
execution_id = AutoField()
|
||||
|
||||
date = DateTimeField()
|
||||
executionTime = TimeField()
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
db_table = "executions"
|
||||
|
||||
|
||||
class Structure(BaseModel):
|
||||
structure_id = AutoField()
|
||||
|
||||
type = TextField()
|
||||
direction = ListField()
|
||||
theta = FloatField()
|
||||
|
||||
r0 = FloatField(null = True)
|
||||
L = FloatField(null = True)
|
||||
radius = FloatField(null = True)
|
||||
|
||||
filletsEnabled = BooleanField(null = True)
|
||||
fillets = FloatField(null = True)
|
||||
#path = TextField()
|
||||
|
||||
|
||||
class Mesh(BaseModel):
|
||||
mesh_id = AutoField()
|
||||
structure_id = ForeignKeyField(Structure, backref = "meshes")
|
||||
|
||||
maxSize = FloatField(null = True)
|
||||
minSize = FloatField(null = True)
|
||||
|
||||
fineness = IntegerField(null = True)
|
||||
growthRate = FloatField(null = True)
|
||||
nbSegPerEdge = FloatField(null = True)
|
||||
nbSegPerRadius = FloatField(null = True)
|
||||
|
||||
chordalErrorEnabled = BooleanField(null = True)
|
||||
chordalError = FloatField(null = True)
|
||||
|
||||
secondOrder = BooleanField(null = True)
|
||||
optimize = BooleanField(null = True)
|
||||
quadAllowed = BooleanField(null = True)
|
||||
useSurfaceCurvature = BooleanField(null = True)
|
||||
fuseEdges = BooleanField(null = True)
|
||||
checkChartBoundary = BooleanField(null = True)
|
||||
|
||||
viscousLayers = BooleanField(null = True)
|
||||
thickness = FloatField(null = True)
|
||||
numberOfLayers = IntegerField(null = True)
|
||||
stretchFactor = FloatField(null = True)
|
||||
isFacesToIgnore = BooleanField(null = True)
|
||||
facesToIgnore = ListField(null = True)
|
||||
#faces = []
|
||||
extrusionMethod = TextField(null = True)
|
||||
|
||||
|
||||
class SubMesh(BaseModel):
|
||||
submesh_id = AutoField()
|
||||
mesh_id = ForeignKeyField(Mesh, backref = "submeshes")
|
||||
name = TextField()
|
||||
|
||||
maxSize = FloatField(null = True)
|
||||
minSize = FloatField(null = True)
|
||||
|
||||
fineness = IntegerField(null = True)
|
||||
growthRate = FloatField(null = True)
|
||||
nbSegPerEdge = FloatField(null = True)
|
||||
nbSegPerRadius = FloatField(null = True)
|
||||
|
||||
chordalErrorEnabled = BooleanField(null = True)
|
||||
chordalError = FloatField(null = True)
|
||||
|
||||
secondOrder = BooleanField(null = True)
|
||||
optimize = BooleanField(null = True)
|
||||
quadAllowed = BooleanField(null = True)
|
||||
useSurfaceCurvature = BooleanField(null = True)
|
||||
fuseEdges = BooleanField(null = True)
|
||||
checkChartBoundary = BooleanField(null = True)
|
||||
|
||||
|
||||
class MeshResult(BaseModel):
|
||||
meshresult_id = AutoField()
|
||||
mesh_id = ForeignKeyField(Mesh, backref = "meshresults")
|
||||
|
||||
surfaceArea = FloatField(null = True)
|
||||
volume = FloatField(null = True)
|
||||
volumeCell = FloatField(null = True)
|
||||
|
||||
elements = IntegerField(null = True)
|
||||
edges = IntegerField(null = True)
|
||||
faces = IntegerField(null = True)
|
||||
volumes = IntegerField(null = True)
|
||||
tetrahedrons = IntegerField(null = True)
|
||||
prisms = IntegerField(null = True)
|
||||
pyramids = IntegerField(null = True)
|
||||
|
||||
meshStatus = TextField(null = True, default = "Idle")
|
||||
meshCalculationTime = TimeField(null = True)
|
||||
|
||||
|
||||
class Flow(BaseModel):
|
||||
flow_id = AutoField()
|
||||
structure_id = ForeignKeyField(Structure, backref = "flows")
|
||||
|
||||
scale = ListField(null = True)
|
||||
pressure = JSONField(null = True)
|
||||
velocity = JSONField(null = True)
|
||||
transportProperties = JSONField(null = True)
|
||||
|
||||
|
||||
class FlowApproximation(BaseModel):
|
||||
flow_approximation_id = AutoField()
|
||||
flow_id = ForeignKeyField(Flow, backref = "flowapproximations")
|
||||
|
||||
pressure = JSONField(null = True)
|
||||
velocity = JSONField(null = True)
|
||||
transportProperties = JSONField(null = True)
|
||||
|
||||
|
||||
class FlowResult(BaseModel):
|
||||
flowresult_id = AutoField()
|
||||
flow_id = ForeignKeyField(Flow, backref = "flowresults")
|
||||
|
||||
flowRate = FloatField(null = True)
|
||||
porosity = FloatField(null = True)
|
||||
permeability = FloatField(null = True)
|
||||
|
||||
flowStatus = TextField(null = True, default = "Idle")
|
||||
flowCalculationTime = TimeField(null = True)
|
@ -28,44 +28,48 @@ class UltimateRunner(object):
|
||||
self.mesh = None
|
||||
self.flow = None
|
||||
|
||||
def casePath(self):
|
||||
case = self.config.cases[0]
|
||||
def casepath(self):
|
||||
params = self.config.cases[0]
|
||||
|
||||
return path.join(
|
||||
return path.abspath(path.join(
|
||||
self.config["build"],
|
||||
case["label"],
|
||||
"direction-[{},{},{}]".format(*[ str(d) for d in case["direction"] ]),
|
||||
"theta-{}".format(case["theta"])
|
||||
)
|
||||
params["label"],
|
||||
"direction-[{},{},{}]".format(*[ str(d) for d in params["direction"] ]),
|
||||
"theta-{}".format(params["theta"])
|
||||
))
|
||||
|
||||
def computeShape(self):
|
||||
case = self.config.cases[0]
|
||||
params = self.config.cases[0]
|
||||
filename = "shape.step"
|
||||
|
||||
match case["label"]:
|
||||
match params["label"]:
|
||||
case "simple":
|
||||
self.shape = Simple(case["direction"])
|
||||
self.shape = Simple(params["direction"])
|
||||
|
||||
case "bodyCentered":
|
||||
self.shape = BodyCentered(case["direction"])
|
||||
self.shape = BodyCentered(params["direction"])
|
||||
|
||||
case "faceCentered":
|
||||
self.shape = FaceCentered(case["direction"])
|
||||
self.shape = FaceCentered(params["direction"])
|
||||
|
||||
self.shape.build()
|
||||
self.shape.export(path.join(case, filename))
|
||||
|
||||
os.makedirs(self.casepath(), exist_ok = True)
|
||||
self.shape.export(path.join(self.casepath(), filename))
|
||||
|
||||
def computeMesh(self):
|
||||
case = self.config.cases[0]
|
||||
params = self.config.cases[0]
|
||||
filename = "mesh.mesh"
|
||||
|
||||
self.mesh = Mesh(self.shape.shape)
|
||||
self.mesh.build()
|
||||
self.mesh.export(path.join(case, filename))
|
||||
|
||||
os.makedirs(self.casepath(), exist_ok = True)
|
||||
self.mesh.export(path.join(self.casepath(), filename))
|
||||
|
||||
def computeFlow(self):
|
||||
case = self.config.cases[0]
|
||||
flow = OnePhaseFlow()
|
||||
params = self.config.cases[0]
|
||||
flow = OnePhaseFlow(path = self.casepath())
|
||||
|
||||
# initial 43 unnamed patches ->
|
||||
# 6 named patches (inlet, outlet, wall, symetry0 - 3/5) ->
|
||||
@ -75,13 +79,15 @@ class UltimateRunner(object):
|
||||
patches = {}
|
||||
|
||||
for n, patch in enumerate(self.shape.shape.faces):
|
||||
# shifted index
|
||||
n += 1
|
||||
name = patch.name
|
||||
|
||||
if patches.get(name):
|
||||
patches[name].append(n)
|
||||
patches[name].append(f"patch{ n }")
|
||||
|
||||
else:
|
||||
patches[name] = [n]
|
||||
patches[name] = [ f"patch{ n }" ]
|
||||
|
||||
for name in patches.keys():
|
||||
match name:
|
||||
|
@ -20,13 +20,13 @@ class Database(object):
|
||||
|
||||
def setup(self):
|
||||
path = os.path.abspath(self.filename)
|
||||
os.makedirs(path, exist_ok = True)
|
||||
#os.makedirs(path, exist_ok = True)
|
||||
|
||||
self.database.init(path)
|
||||
|
||||
if not os.path.exists(path):
|
||||
self.database.create_tables([Execution])
|
||||
self.database.create_tables([
|
||||
Execution,
|
||||
Physics,
|
||||
Shape,
|
||||
Mesh,
|
||||
|
@ -26,7 +26,7 @@ class Execution(Model):
|
||||
|
||||
class Meta:
|
||||
database = sqliteDB
|
||||
db_table = "executions"
|
||||
table_name = "executions"
|
||||
|
||||
|
||||
class Physics(Model):
|
||||
@ -43,7 +43,7 @@ class Physics(Model):
|
||||
|
||||
class Meta:
|
||||
database = sqliteDB
|
||||
db_table = "physics"
|
||||
table_name = "physics"
|
||||
depends_on = Execution
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ class Shape(Model):
|
||||
|
||||
class Meta:
|
||||
database = sqliteDB
|
||||
db_table = "shapes"
|
||||
table_name = "shapes"
|
||||
depends_on = Execution
|
||||
|
||||
|
||||
@ -85,7 +85,7 @@ class Mesh(Model):
|
||||
|
||||
class Meta:
|
||||
database = sqliteDB
|
||||
db_table = "meshes"
|
||||
table_name = "meshes"
|
||||
depends_on = Execution
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ class Flow(Model):
|
||||
|
||||
class Meta:
|
||||
database = sqliteDB
|
||||
db_table = "flows"
|
||||
table_name = "flows"
|
||||
depends_on = Execution
|
||||
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
|
||||
from .meshConversion import ideasUnvToFoam
|
||||
from .meshConversion import ideasUnvToFoam, netgenNeutralToFoam
|
||||
from .meshManipulation import createPatch, transformPoints, checkMesh, renumberMesh
|
||||
from .miscellaneous import foamDictionary
|
||||
from .parallelProcessing import decomposePar
|
||||
|
@ -1,5 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
|
@ -1,216 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
import logging
|
||||
|
||||
GEOM_IMPORTED = False
|
||||
|
||||
try:
|
||||
import GEOM
|
||||
from salome.geom import geomBuilder
|
||||
|
||||
GEOM_IMPORTED = True
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class StructureGeometry(object):
|
||||
def __init__(
|
||||
self,
|
||||
direction: list = None,
|
||||
theta: float = None,
|
||||
r0: float = 1,
|
||||
#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.
|
||||
"""
|
||||
# Geometry parameters
|
||||
self.direction = direction
|
||||
self.theta = theta
|
||||
self.r0 = r0
|
||||
self.filletsEnabled = filletsEnabled
|
||||
#self.fillets = fillets
|
||||
self.filletScale = 0.8
|
||||
|
||||
# General attributes
|
||||
self.shape = None
|
||||
self.groups = []
|
||||
self.shapeCell = None
|
||||
self.shapeLattice = None
|
||||
|
||||
# Geometry module
|
||||
if not GEOM_IMPORTED:
|
||||
raise ImportError("Cannot find the salome geometry modules.")
|
||||
|
||||
else:
|
||||
self.geo = geomBuilder.New()
|
||||
|
||||
@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
|
||||
|
||||
def export(
|
||||
filename: str,
|
||||
deflection: float = 0.001
|
||||
):
|
||||
"""Export a shape.
|
||||
|
||||
Supported formats: step, vtk.
|
||||
|
||||
:param filename:
|
||||
Name of the file to store the given shape in.
|
||||
|
||||
:param deflection:
|
||||
vtk: Deflection of the given shape.
|
||||
|
||||
:return:
|
||||
Output, error messages and returncode
|
||||
"""
|
||||
out, err, returncode = "", "", 0
|
||||
ext = os.path.splitext(filename)[1][1: ]
|
||||
|
||||
try:
|
||||
if ext == "step":
|
||||
self.geo.ExportSTEP(self.shape, filename) # theUnit = GEOM.LU_METER)
|
||||
|
||||
elif ext == "vtk":
|
||||
self.geo.ExportVTK(self.shape, filename, theDeflection = deflection)
|
||||
|
||||
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
|
||||
|
@ -1,304 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
import logging
|
||||
|
||||
SMESH_IMPORTED = False
|
||||
|
||||
try:
|
||||
import SMESH
|
||||
from salome.smesh import smeshBuilder
|
||||
|
||||
SMESH_IMPORTED = True
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
class Mesh(object):
|
||||
def __init__(self, geom):
|
||||
|
||||
# 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, 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 compute(self):
|
||||
"""Compute mesh.
|
||||
"""
|
||||
isDone = self.mesh.Compute()
|
||||
out = ""
|
||||
err = self.mesh.GetComputeErrors()
|
||||
returncode = int(not isDone)
|
||||
|
||||
return out, err, returncode
|
||||
|
||||
def stats(self):
|
||||
return {
|
||||
"elements": self.mesh.NbElements(),
|
||||
"edges": self.mesh.NbEdges(),
|
||||
"faces": self.mesh.NbFaces(),
|
||||
"volumes": self.mesh.NbVolumes(),
|
||||
"tetrahedrons": self.mesh.NbTetras(),
|
||||
"prisms": self.mesh.NbPrisms(),
|
||||
"pyramids": self.mesh.NbPyramids()
|
||||
}
|
||||
|
||||
def 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
|
||||
):
|
||||
"""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 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
|
@ -1,112 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
import subprocess
|
||||
import logging
|
||||
import sys, os
|
||||
import re
|
||||
|
||||
|
||||
class SalomeNotFound(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class SalomeManager(object):
|
||||
def __init__(self):
|
||||
self.__port = None
|
||||
self.__lastproc = None
|
||||
|
||||
|
||||
def runner(self, cmdargs: list, **kwargs):
|
||||
timeout = kwargs.pop("timeout") if kwargs.get("timeout") else None
|
||||
|
||||
try:
|
||||
with subprocess.Popen(
|
||||
cmdargs,
|
||||
stdout = kwargs.pop("stdout") if kwargs.get("stdout") else subprocess.PIPE,
|
||||
stderr = kwargs.pop("stdout") if kwargs.get("stderr") else subprocess.PIPE,
|
||||
**kwargs
|
||||
) as proc:
|
||||
self.__lastproc = proc
|
||||
out, err = proc.communicate(timeout = timeout)
|
||||
|
||||
except FileNotFoundError:
|
||||
raise SalomeNotFound()
|
||||
|
||||
return out, err, proc.returncode
|
||||
|
||||
|
||||
def port(self) -> int:
|
||||
out, err, returncode = self.runner(["salome", "start", "--print-port"], text = True)
|
||||
|
||||
if returncode == 0:
|
||||
reg = re.search("(?!port:)([0-9]+)", out)
|
||||
|
||||
if reg:
|
||||
return int(reg.group())
|
||||
|
||||
return 2810
|
||||
|
||||
|
||||
def version(self) -> int:
|
||||
out, err, returncode = self.runner(["salome", "--version"], text = True)
|
||||
|
||||
return out.strip().split(" ")[-1]
|
||||
|
||||
|
||||
def kill(self, port: int = None):
|
||||
return self.runner(["salome", "kill", str(self.__port or port)])
|
||||
|
||||
|
||||
def killall(self):
|
||||
return self.runner(["salome", "killall"])
|
||||
|
||||
|
||||
def execute(self, scriptpath: str, *args, root: str = None, logpath: str = None, timeout: int = None, **kwargs):
|
||||
|
||||
if not root:
|
||||
root = os.environ["HOME"]
|
||||
|
||||
# ISSUE: salome removes commas from string list
|
||||
args = list(args)
|
||||
args.insert(1, root)
|
||||
|
||||
salomeargs = "args:"
|
||||
salomeargs += ",".join([ '"{}"'.format(str(arg)) for arg in args ])
|
||||
|
||||
if kwargs:
|
||||
salomeargs += "," + ",".join([ '{}="{}"'.format(k, v) for k, v in kwargs.items() ])
|
||||
|
||||
###
|
||||
self.__port = self.port()
|
||||
cmd = [
|
||||
"salome",
|
||||
"start",
|
||||
"-t",
|
||||
"--shutdown-servers=1",
|
||||
"--port", str(self.__port),
|
||||
scriptpath,
|
||||
salomeargs
|
||||
]
|
||||
|
||||
try:
|
||||
out, err, returncode = self.runner(cmd, timeout = timeout)
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
lastproc = self.__lastproc
|
||||
self.kill()
|
||||
|
||||
out, err = lastproc.communicate()
|
||||
returncode = lastproc.returncode
|
||||
|
||||
if logpath:
|
||||
os.makedirs(logpath, exist_ok = True)
|
||||
|
||||
with open(os.path.join(logpath, "salome.log"), "wb") as io:
|
||||
io.write(out)
|
||||
io.write(err)
|
||||
|
||||
return str(out, "utf-8"), str(err, "utf-8"), returncode
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 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
|
@ -1,251 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
from anisotropy.salomepl.geometry import StructureGeometry
|
||||
from numpy import pi, sqrt, cos, arccos, fix
|
||||
import logging
|
||||
|
||||
class BodyCentered(StructureGeometry):
|
||||
@property
|
||||
def name(self):
|
||||
"""Shape name.
|
||||
"""
|
||||
return "bodyCentered"
|
||||
|
||||
@property
|
||||
def L(self):
|
||||
return self.r0 * 4 / sqrt(3)
|
||||
|
||||
@property
|
||||
def thetaMin(self):
|
||||
return 0.01
|
||||
|
||||
@property
|
||||
def thetaMax(self):
|
||||
return 0.18
|
||||
|
||||
@property
|
||||
def fillets(self):
|
||||
analytical = self.r0 * (sqrt(2) / sqrt(1 - cos(pi - 2 * arccos(sqrt(2 / 3)))) -
|
||||
1 / (1 - self.theta))
|
||||
# ISSUE: MakeFilletAll : Fillet can't be computed on the given shape with the given radius.
|
||||
# Temporary solution: degrade the precision (minRound <= analytical).
|
||||
rTol = 3
|
||||
minRound = fix(10 ** rTol * analytical) * 10 ** -rTol
|
||||
|
||||
return minRound * self.filletScale
|
||||
|
||||
def build(self):
|
||||
###
|
||||
# Pore Cell
|
||||
##
|
||||
if self.direction in [[1, 0, 0], [0, 0, 1]]:
|
||||
###
|
||||
# Parameters
|
||||
##
|
||||
xn, yn, zn = 3, 3, 3
|
||||
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(2)
|
||||
height = self.L
|
||||
|
||||
point = []
|
||||
xl = sqrt(diag ** 2 + diag ** 2) * 0.5
|
||||
yw = xl
|
||||
zh = height
|
||||
|
||||
scale = 100
|
||||
oo = self.geo.MakeVertex(0, 0, 0)
|
||||
spos1 = (0, 0, 0)
|
||||
spos2 = (0, 0, 0)
|
||||
|
||||
###
|
||||
# Bounding box
|
||||
##
|
||||
if self.direction == [1, 0, 0]:
|
||||
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 = 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 = 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 = self.geo.MakeFaceWires([sk.wire()], True)
|
||||
vecflow = self.geo.GetNormal(inletface)
|
||||
poreCell = self.geo.MakePrismVecH(inletface, vecflow, zh)
|
||||
|
||||
|
||||
self.shapeCell = poreCell
|
||||
|
||||
inletface = self.geo.MakeScaleTransform(inletface, oo, scale)
|
||||
poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale)
|
||||
|
||||
faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False)
|
||||
symetryface = []
|
||||
|
||||
for face in faces:
|
||||
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
|
||||
|
||||
else:
|
||||
symetryface.append(face)
|
||||
|
||||
elif self.direction == [1, 1, 1]:
|
||||
###
|
||||
# Parameters
|
||||
##
|
||||
xn, yn, zn = 3, 3, 3
|
||||
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(2)
|
||||
height = diag / 3
|
||||
|
||||
point = []
|
||||
xl, yw, zh = -self.L / 4, -self.L / 4, -self.L / 4
|
||||
point.append((self.L / 3 + xl, self.L / 3 + yw, 4 * self.L / 3 + zh))
|
||||
point.append((self.L + xl, 0 + yw, self.L + zh))
|
||||
point.append((4 * self.L / 3 + xl, self.L / 3 + yw, self.L / 3 + zh))
|
||||
point.append((self.L + xl, self.L + yw, 0 + zh))
|
||||
point.append((self.L / 3 + xl, 4 * self.L / 3 + yw, self.L / 3 + zh))
|
||||
point.append((0 + xl, self.L + yw, self.L + zh))
|
||||
point.append((self.L / 3 + xl, self.L / 3 + yw, 4 * self.L / 3 + zh))
|
||||
|
||||
scale = 100
|
||||
oo = self.geo.MakeVertex(0, 0, 0)
|
||||
spos1 = (0, 0, 0)
|
||||
spos2 = (0, 0, 0)
|
||||
|
||||
###
|
||||
# Bounding box
|
||||
##
|
||||
sk = self.geo.Sketcher3D()
|
||||
|
||||
for p in point:
|
||||
sk.addPointsAbsolute(*p)
|
||||
|
||||
inletface = self.geo.MakeFaceWires([sk.wire()], False)
|
||||
vecflow = self.geo.GetNormal(inletface)
|
||||
poreCell = self.geo.MakePrismVecH(inletface, vecflow, self.L * sqrt(3))
|
||||
|
||||
self.shapeCell = poreCell
|
||||
|
||||
inletface = self.geo.MakeScaleTransform(inletface, oo, scale)
|
||||
poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale)
|
||||
|
||||
faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False)
|
||||
symetryface = []
|
||||
|
||||
for face in faces:
|
||||
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
|
||||
|
||||
else:
|
||||
symetryface.append(face)
|
||||
|
||||
else:
|
||||
raise Exception(f"Direction { self.direction } is not implemented")
|
||||
|
||||
###
|
||||
# Lattice
|
||||
##
|
||||
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 = 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)
|
||||
|
||||
lattice2 = self.geo.MakeTranslation(lattice1, -0.5 * self.L, -0.5 * self.L, -0.5 * self.L)
|
||||
|
||||
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 = self.geo.MakeScaleTransform(grains, oo, scale)
|
||||
grainsOrigin = None
|
||||
|
||||
if self.filletsEnabled:
|
||||
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
|
||||
##
|
||||
self.groups = []
|
||||
groupAll = self.createGroupAll(self.shape)
|
||||
|
||||
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:
|
||||
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"))
|
||||
|
||||
|
||||
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(...)
|
||||
|
||||
|
||||
|
@ -1,255 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# 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
|
||||
|
||||
class FaceCentered(StructureGeometry):
|
||||
@property
|
||||
def name(self):
|
||||
"""Shape name.
|
||||
"""
|
||||
return "faceCentered"
|
||||
|
||||
@property
|
||||
def L(self):
|
||||
return self.r0 * 4 / sqrt(2)
|
||||
|
||||
@property
|
||||
def thetaMin(self):
|
||||
return 0.01
|
||||
|
||||
@property
|
||||
def thetaMax(self):
|
||||
return 0.13
|
||||
|
||||
@property
|
||||
def fillets(self):
|
||||
analytical = self.r0 * (2 * sqrt(3) / 3 - 1 / (1 - self.theta))
|
||||
# ISSUE: MakeFilletAll : Fillet can't be computed on the given shape with the given radius.
|
||||
# Temporary solution: degrade the precision (minRound <= analytical).
|
||||
rTol = 3
|
||||
minRound = fix(10 ** rTol * analytical) * 10 ** -rTol
|
||||
|
||||
return minRound * self.filletScale
|
||||
|
||||
def build(self):
|
||||
###
|
||||
# Pore Cell
|
||||
##
|
||||
if self.direction in [[1, 0, 0], [0, 0, 1]]:
|
||||
###
|
||||
# Parameters
|
||||
##
|
||||
xn, yn, zn = 3, 3, 3
|
||||
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(3)
|
||||
height = diag / 3
|
||||
|
||||
point = []
|
||||
xl = sqrt(length ** 2 + length ** 2) * 0.5
|
||||
yw = xl
|
||||
zh = width
|
||||
|
||||
scale = 100
|
||||
oo = self.geo.MakeVertex(0, 0, 0)
|
||||
spos1 = (-width * (xn - 1), 0, -width * (zn - 2))
|
||||
spos2 = (-width * xn, 0, -width * (zn - 1))
|
||||
|
||||
###
|
||||
# Bounding box
|
||||
##
|
||||
if self.direction == [1, 0, 0]:
|
||||
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 = 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 = 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 = self.geo.MakeFaceWires([sk.wire()], True)
|
||||
vecflow = self.geo.GetNormal(inletface)
|
||||
poreCell = self.geo.MakePrismVecH(inletface, vecflow, 2 * zh)
|
||||
|
||||
self.shapeCell = poreCell
|
||||
|
||||
inletface = self.geo.MakeScaleTransform(inletface, oo, scale)
|
||||
poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale)
|
||||
|
||||
faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False)
|
||||
symetryface = []
|
||||
|
||||
for face in faces:
|
||||
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
|
||||
|
||||
else:
|
||||
symetryface.append(face)
|
||||
|
||||
elif self.direction == [1, 1, 1]:
|
||||
###
|
||||
# Parameters
|
||||
##
|
||||
xn, yn, zn = 3, 3, 3
|
||||
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(3)
|
||||
height = diag / 3
|
||||
|
||||
point = []
|
||||
xl, yw, zh = -(xn - 2) * self.L / 3, -(yn - 2) * self.L / 3, -(zn - 2) * self.L / 3
|
||||
point.append((-2 * width / 3 + xl, -2 * width / 3 + yw, width / 3 + zh))
|
||||
point.append((0 + xl, -width + yw, 0 + zh))
|
||||
point.append((width / 3 + xl, -2 * width / 3 + yw, -2 * width / 3 + zh))
|
||||
point.append((0 + xl, 0 + yw, -width + zh))
|
||||
point.append((-2 * width / 3 + xl, width / 3 + yw, -2 * width / 3 + zh))
|
||||
point.append((-width + xl, 0 + yw, 0 + zh))
|
||||
point.append((-2 * width / 3 + xl, -2 * width / 3 + yw, width / 3 + zh))
|
||||
|
||||
scale = 100
|
||||
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 = self.geo.Sketcher3D()
|
||||
|
||||
for p in point:
|
||||
sk.addPointsAbsolute(*p)
|
||||
|
||||
inletface = self.geo.MakeFaceWires([sk.wire()], False)
|
||||
vecflow = self.geo.GetNormal(inletface)
|
||||
poreCell = self.geo.MakePrismVecH(inletface, vecflow, self.L * sqrt(3))
|
||||
|
||||
self.shapeCell = poreCell
|
||||
|
||||
inletface = self.geo.MakeScaleTransform(inletface, oo, scale)
|
||||
poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale)
|
||||
|
||||
faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False)
|
||||
symetryface = []
|
||||
|
||||
for face in faces:
|
||||
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
|
||||
|
||||
else:
|
||||
symetryface.append(face)
|
||||
|
||||
else:
|
||||
raise Exception(f"Direction { self.direction } is not implemented")
|
||||
|
||||
###
|
||||
# Grains
|
||||
##
|
||||
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 = 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 = 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 = 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 = self.geo.MakeScaleTransform(grains, oo, scale)
|
||||
grainsOrigin = None
|
||||
|
||||
if self.filletsEnabled:
|
||||
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
|
||||
##
|
||||
#poreCell = self.geo.LimitTolerance(poreCell, 1e-12)
|
||||
#grains = self.geo.LimitTolerance(grains, 1e-12)
|
||||
|
||||
self.shape = self.geo.MakeCutList(poreCell, [grains])
|
||||
self.shape = self.geo.MakeScaleTransform(self.shape, oo, 1 / scale, theName = self.name)
|
||||
|
||||
isValid, msg = self.isValid()
|
||||
|
||||
if not isValid:
|
||||
logging.warning(msg)
|
||||
self.heal()
|
||||
|
||||
###
|
||||
# Groups
|
||||
#
|
||||
# inlet, outlet, simetry(N), strips(optional), wall
|
||||
##
|
||||
self.groups = []
|
||||
groupAll = self.createGroupAll(self.shape)
|
||||
|
||||
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:
|
||||
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"))
|
||||
|
||||
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(...)
|
||||
|
||||
|
||||
|
@ -1,405 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
from numpy import pi, sqrt, fix
|
||||
import logging
|
||||
|
||||
from anisotropy.salomepl.geometry import StructureGeometry
|
||||
from anisotropy.salomepl.mesh import Mesh
|
||||
|
||||
from anisotropy.openfoam.presets import (
|
||||
ControlDict, FvSchemes, FvSolution,
|
||||
TransportProperties, TurbulenceProperties, CreatePatchDict,
|
||||
P, U
|
||||
)
|
||||
from anisotropy.openfoam.foamcase import FoamCase
|
||||
import anisotropy.openfoam as openfoam
|
||||
|
||||
class _Geometry(StructureGeometry):
|
||||
@property
|
||||
def name(self):
|
||||
"""Shape name.
|
||||
"""
|
||||
return "simple"
|
||||
|
||||
@property
|
||||
def L(self):
|
||||
return 2 * self.r0
|
||||
|
||||
@property
|
||||
def thetaMin(self):
|
||||
return 0.01
|
||||
|
||||
@property
|
||||
def thetaMax(self):
|
||||
return 0.28
|
||||
|
||||
@property
|
||||
def fillets(self):
|
||||
analytical = self.r0 * (sqrt(2) - 1 / (1 - self.theta))
|
||||
# ISSUE: MakeFilletAll : Fillet can't be computed on the given shape with the given radius.
|
||||
# Temporary solution: degrade the precision (minRound <= analytical).
|
||||
rTol = 3
|
||||
minRound = fix(10 ** rTol * analytical) * 10 ** -rTol
|
||||
|
||||
return minRound * self.filletScale
|
||||
|
||||
def build(self):
|
||||
###
|
||||
# Pore Cell
|
||||
##
|
||||
if self.direction in [[1, 0, 0], [0, 0, 1]]:
|
||||
###
|
||||
# Parameters
|
||||
##
|
||||
xn, yn, zn = 3, 3, 3
|
||||
|
||||
length = self.L * sqrt(2)
|
||||
width = self.L * sqrt(2)
|
||||
height = self.L
|
||||
|
||||
xl = sqrt(length ** 2 * 0.5)
|
||||
yw = xl
|
||||
zh = height
|
||||
|
||||
scale = 100
|
||||
oo = self.geo.MakeVertex(0, 0, 0)
|
||||
|
||||
###
|
||||
# Bounding box
|
||||
##
|
||||
if self.direction == [1, 0, 0]:
|
||||
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 = 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 = 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 = self.geo.MakeFaceWires([sk.wire()], True)
|
||||
vecflow = self.geo.GetNormal(inletface)
|
||||
poreCell = self.geo.MakePrismVecH(inletface, vecflow, height)
|
||||
|
||||
self.shapeCell = poreCell
|
||||
|
||||
inletface = self.geo.MakeScaleTransform(inletface, oo, scale)
|
||||
poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale)
|
||||
|
||||
faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False)
|
||||
symetryface = []
|
||||
|
||||
for face in faces:
|
||||
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
|
||||
|
||||
else:
|
||||
symetryface.append(face)
|
||||
|
||||
elif self.direction == [1, 1, 1]:
|
||||
###
|
||||
# Parameters
|
||||
##
|
||||
xn, yn, zn = 3, 3, 3
|
||||
|
||||
length = self.L * sqrt(2)
|
||||
width = self.L * sqrt(2)
|
||||
height = self.L
|
||||
|
||||
point = []
|
||||
xl, yw, zh = -self.L - self.L / 6, -self.L - self.L / 6, -self.L / 6
|
||||
point.append((self.L + xl, self.L + yw, self.L + zh))
|
||||
point.append((5 * self.L / 3 + xl, 2 * self.L / 3 + yw, 2 * self.L / 3 + zh))
|
||||
point.append((2 * self.L + xl, self.L + yw, 0 + zh))
|
||||
point.append((5 * self.L / 3 + xl, 5 * self.L / 3 + yw, -self.L / 3 + zh))
|
||||
point.append((self.L + xl, 2 * self.L + yw, 0 + zh))
|
||||
point.append((2 * self.L / 3 + xl, 5 * self.L / 3 + yw, 2 * self.L / 3 + zh))
|
||||
point.append((self.L + xl, self.L + yw, self.L + zh))
|
||||
|
||||
scale = 100
|
||||
oo = self.geo.MakeVertex(0, 0, 0)
|
||||
|
||||
###
|
||||
# Bounding box
|
||||
##
|
||||
sk = self.geo.Sketcher3D()
|
||||
|
||||
for p in point:
|
||||
sk.addPointsAbsolute(*p)
|
||||
|
||||
inletface = self.geo.MakeFaceWires([sk.wire()], False)
|
||||
vecflow = self.geo.GetNormal(inletface)
|
||||
poreCell = self.geo.MakePrismVecH(inletface, vecflow, self.L * sqrt(3))
|
||||
|
||||
self.shapeCell = poreCell
|
||||
|
||||
inletface = self.geo.MakeScaleTransform(inletface, oo, scale)
|
||||
poreCell = self.geo.MakeScaleTransform(poreCell, oo, scale)
|
||||
|
||||
faces = self.geo.ExtractShapes(poreCell, self.geo.ShapeType["FACE"], False)
|
||||
symetryface = []
|
||||
|
||||
for face in faces:
|
||||
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
|
||||
|
||||
else:
|
||||
symetryface.append(face)
|
||||
|
||||
else:
|
||||
raise Exception(f"Direction { self.direction } is not implemented")
|
||||
|
||||
###
|
||||
# Grains
|
||||
##
|
||||
ox = self.geo.MakeVectorDXDYDZ(1, 0, 0)
|
||||
oy = self.geo.MakeVectorDXDYDZ(0, 1, 0)
|
||||
oz = self.geo.MakeVectorDXDYDZ(0, 0, 1)
|
||||
|
||||
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 = self.geo.ExtractShapes(lattice, self.geo.ShapeType["SOLID"], True)
|
||||
grains = self.geo.MakeFuseList(grains, False, False)
|
||||
|
||||
grains = self.geo.MakeScaleTransform(grains, oo, scale)
|
||||
grainsOrigin = None
|
||||
|
||||
if self.filletsEnabled:
|
||||
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, msg = self.isValid()
|
||||
|
||||
if not isValid:
|
||||
logging.warning(msg)
|
||||
self.heal()
|
||||
|
||||
###
|
||||
# Groups
|
||||
#
|
||||
# inlet, outlet, simetry(N), strips(optional), wall
|
||||
##
|
||||
self.groups = []
|
||||
groupAll = self.createGroupAll(self.shape)
|
||||
|
||||
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:
|
||||
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"))
|
||||
|
||||
|
||||
class _Mesh(Mesh):
|
||||
def __init__(self, shape):
|
||||
Mesh.__init__(self, shape)
|
||||
|
||||
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(...)
|
||||
|
||||
def build(self):
|
||||
out, err, returncode = self.mesh.compute()
|
||||
|
||||
if not returncode:
|
||||
self.mesh.removePyramids()
|
||||
self.mesh.createGroups()
|
||||
|
||||
|
||||
class _OnePhaseFlow(object):
|
||||
def __init__(self):
|
||||
controlDict = ControlDict()
|
||||
controlDict.update(
|
||||
startFrom = "latestTime",
|
||||
endTime = 5000,
|
||||
writeInterval = 100,
|
||||
runTimeModifiable = "true"
|
||||
)
|
||||
|
||||
fvSchemes = FvSchemes()
|
||||
|
||||
fvSolution = FvSolution()
|
||||
fvSolution["solvers"]["U"].update(
|
||||
nSweeps = 2,
|
||||
tolerance = 1e-08
|
||||
)
|
||||
fvSolution["solvers"]["Phi"] = dict(
|
||||
solver = "GAMG",
|
||||
smoother = "DIC",
|
||||
cacheAgglomeration = "yes",
|
||||
agglomerator = "faceAreaPair",
|
||||
nCellsInCoarsestLevel = 10,
|
||||
mergeLevels = 1,
|
||||
tolerance = 1e-06,
|
||||
relTol = 0.01
|
||||
)
|
||||
fvSolution["potentialFlow"] = dict(
|
||||
nNonOrthogonalCorrectors = 20,
|
||||
PhiRefCell = 0,
|
||||
PhiRefPoint = 0,
|
||||
PhiRefValue = 0,
|
||||
Phi = 0
|
||||
)
|
||||
fvSolution["cache"] = { "grad(U)": None }
|
||||
fvSolution["SIMPLE"].update(
|
||||
nNonOrthogonalCorrectors = 10,
|
||||
residualControl = dict(
|
||||
p = 1e-05,
|
||||
U = 1e-05
|
||||
)
|
||||
)
|
||||
fvSolution["relaxationFactors"]["equations"]["U"] = 0.5
|
||||
|
||||
transportProperties = TransportProperties()
|
||||
transportProperties.update(
|
||||
nu = 1e-06
|
||||
)
|
||||
|
||||
turbulenceProperties = TurbulenceProperties()
|
||||
turbulenceProperties.content = dict(
|
||||
simulationType = "laminar"
|
||||
)
|
||||
|
||||
createPatchDict = CreatePatchDict()
|
||||
createPatchDict["patches"] = []
|
||||
|
||||
for patch in ["inlet", "outlet", "wall", "strips", *[ f"symetry{ n }" for n in range(6) ]]:
|
||||
newPatch = dict(
|
||||
name = patch,
|
||||
patchInfo = dict(
|
||||
type = "patch",
|
||||
inGroups = [patch]
|
||||
),
|
||||
constructFrom = "patches",
|
||||
patches = [ f"smesh_{ patch }" ]
|
||||
)
|
||||
|
||||
match patch:
|
||||
case "wall" | "strips":
|
||||
newPatch["patchInfo"].update(
|
||||
type = "wall",
|
||||
inGroups = [ "wall" ]
|
||||
)
|
||||
|
||||
case patch if patch.find("symetry") == 0:
|
||||
newPatch["patchInfo"]["inGroups"] = [ "symetryPlane" ]
|
||||
|
||||
createPatchDict["patches"].append(newPatch)
|
||||
|
||||
|
||||
boundaries = [ "inlet", "outlet", "symetryPlane", "wall", "strips" ]
|
||||
p = P()
|
||||
p["boundaryField"] = {}
|
||||
u = U()
|
||||
u["boundaryField"] = {}
|
||||
|
||||
# ISSUE: add proxy from geometry direction to outlet boundaryField.
|
||||
for boundary in boundaries:
|
||||
match boundary:
|
||||
case "inlet":
|
||||
p["boundaryField"][boundary] = dict(
|
||||
type = "fixedValue",
|
||||
value = "uniform 1e-3"
|
||||
)
|
||||
u["boundaryField"][boundary] = dict(
|
||||
type = "fixedValue",
|
||||
value = "uniform (0 0 -6e-5)" # * direction
|
||||
)
|
||||
|
||||
case "outlet":
|
||||
p["boundaryField"][boundary] = dict(
|
||||
type = "fixedValue",
|
||||
value = "uniform 0"
|
||||
)
|
||||
u["boundaryField"][boundary] = dict(
|
||||
type = "zeroGradient",
|
||||
)
|
||||
|
||||
case _:
|
||||
p["boundaryField"][boundary] = dict(
|
||||
type = "zeroGradient"
|
||||
)
|
||||
u["boundaryField"][boundary] = dict(
|
||||
type = "fixedValue",
|
||||
value = "uniform (0 0 0)"
|
||||
)
|
||||
|
||||
self.solution = FoamCase([
|
||||
controlDict, fvSchemes, fvSolution, createPatchDict,
|
||||
transportProperties, turbulenceProperties,
|
||||
p, u
|
||||
])
|
||||
|
||||
def build(self):
|
||||
self.solution.write()
|
||||
|
||||
openfoam.ideasUnvToFoam("mesh.unv")
|
||||
openfoam.createPatch()
|
||||
openfoam.checkMesh()
|
||||
openfoam.transformPoints((1e-5, 1e-5, 1e-5))
|
||||
openfoam.renumberMesh()
|
||||
openfoam.potentialFoam()
|
||||
|
||||
self.solution.read()
|
||||
|
||||
self.solution.U["boundaryField"]["outlet"] = dict(
|
||||
type = "pressureInletVelocity",
|
||||
value = "uniform (0 0 0)" # * direction
|
||||
)
|
||||
self.solution.write()
|
||||
|
||||
openfoam.simpleFoam()
|
||||
|
||||
|
||||
class Simple(object):
|
||||
geometry = _Geometry
|
||||
mesh = _Mesh
|
||||
onephaseflow = _OnePhaseFlow
|
@ -70,7 +70,7 @@ class BodyCentered(Periodic):
|
||||
self.lattice = lattice.Scale(zero, self.minimize)
|
||||
|
||||
# Inlet face
|
||||
if self.direction in [[1, 0, 0], [0, 0, 1]]:
|
||||
if (self.direction == numpy.array([1., 0., 0.])).prod():
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(2)
|
||||
@ -80,25 +80,33 @@ class BodyCentered(Periodic):
|
||||
yw = xl
|
||||
zh = height
|
||||
|
||||
if self.direction == [1, 0, 0]:
|
||||
vertices = numpy.array([
|
||||
(xl, 0, 0),
|
||||
(0, yw, 0),
|
||||
(0, yw, zh),
|
||||
(xl, 0, zh)
|
||||
])
|
||||
extr = diag
|
||||
vertices = numpy.array([
|
||||
(xl, 0, 0),
|
||||
(0, yw, 0),
|
||||
(0, yw, zh),
|
||||
(xl, 0, zh)
|
||||
])
|
||||
extr = diag
|
||||
|
||||
elif self.direction == [0, 0, 1]:
|
||||
vertices = numpy.array([
|
||||
(0, yw, 0),
|
||||
(xl, 0, 0),
|
||||
(2 * xl, yw, 0),
|
||||
(xl, 2 * yw, 0)
|
||||
])
|
||||
extr = height
|
||||
elif (self.direction == numpy.array([0., 0., 1.])).prod():
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(2)
|
||||
height = self.L
|
||||
|
||||
elif self.direction == [1, 1, 1]:
|
||||
xl = sqrt(diag ** 2 + diag ** 2) * 0.5
|
||||
yw = xl
|
||||
zh = height
|
||||
|
||||
vertices = numpy.array([
|
||||
(0, yw, 0),
|
||||
(xl, 0, 0),
|
||||
(2 * xl, yw, 0),
|
||||
(xl, 2 * yw, 0)
|
||||
])
|
||||
extr = height
|
||||
|
||||
elif (self.direction == numpy.array([1., 1., 1.])).prod():
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(2)
|
||||
|
@ -77,7 +77,7 @@ class FaceCentered(Periodic):
|
||||
self.lattice = lattice.Scale(zero, self.minimize)
|
||||
|
||||
# Inlet face
|
||||
if self.direction in [[1, 0, 0], [0, 0, 1]]:
|
||||
if (self.direction == numpy.array([1., 0., 0.])).prod():
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(3)
|
||||
@ -87,25 +87,33 @@ class FaceCentered(Periodic):
|
||||
yw = xl
|
||||
zh = width
|
||||
|
||||
if self.direction == [1, 0, 0]:
|
||||
vertices = numpy.array([
|
||||
(0, 0, -zh),
|
||||
(-xl, yw, -zh),
|
||||
(-xl, yw, zh),
|
||||
(0, 0, zh)
|
||||
])
|
||||
extr = length
|
||||
vertices = numpy.array([
|
||||
(0, 0, -zh),
|
||||
(-xl, yw, -zh),
|
||||
(-xl, yw, zh),
|
||||
(0, 0, zh)
|
||||
])
|
||||
extr = length
|
||||
|
||||
elif self.direction == [0, 0, 1]:
|
||||
vertices = numpy.array([
|
||||
(0, 0, -zh),
|
||||
(xl, yw, -zh),
|
||||
(0, 2 * yw, -zh),
|
||||
(-xl, yw, -zh)
|
||||
])
|
||||
extr = 2 * width
|
||||
elif (self.direction == numpy.array([0., 0., 1.])).prod():
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(3)
|
||||
height = diag / 3
|
||||
|
||||
elif self.direction == [1, 1, 1]:
|
||||
xl = sqrt(length ** 2 + length ** 2) * 0.5
|
||||
yw = xl
|
||||
zh = width
|
||||
|
||||
vertices = numpy.array([
|
||||
(0, 0, -zh),
|
||||
(xl, yw, -zh),
|
||||
(0, 2 * yw, -zh),
|
||||
(-xl, yw, -zh)
|
||||
])
|
||||
extr = 2 * width
|
||||
|
||||
elif (self.direction == numpy.array([1., 1., 1.])).prod():
|
||||
length = 2 * self.r0
|
||||
width = self.L / 2
|
||||
diag = self.L * sqrt(3)
|
||||
@ -139,6 +147,7 @@ class FaceCentered(Periodic):
|
||||
# Boundaries
|
||||
symetryId = 0
|
||||
|
||||
# ISSUE: inlet and outlet faces have the same name and normal vector after extrusion
|
||||
for face in self.cell.faces:
|
||||
fNorm = self.normal(face)
|
||||
fAngle = self.angle(vecFlow, fNorm)
|
||||
|
27
anisotropy/shaping/occExtended.py
Normal file
27
anisotropy/shaping/occExtended.py
Normal file
@ -0,0 +1,27 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of anisotropy.
|
||||
# License: GNU GPL version 3, see the file "LICENSE" for details.
|
||||
|
||||
from functools import wraps
|
||||
from netgen import occ
|
||||
import numpy
|
||||
|
||||
|
||||
def add_method(cls):
|
||||
"""Add method to existing class. Use it as decorator.
|
||||
"""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
setattr(cls, func.__name__, wrapper)
|
||||
|
||||
return func
|
||||
return decorator
|
||||
|
||||
|
||||
@add_method(occ.gp_Pnt)
|
||||
def pos(self) -> numpy.array:
|
||||
return numpy.array([self.x, self.y, self.z])
|
||||
|
@ -6,8 +6,26 @@ from netgen.occ import *
|
||||
import numpy
|
||||
from numpy import pi, sqrt
|
||||
|
||||
from .occExtended import *
|
||||
from . import Periodic
|
||||
|
||||
def reconstruct(solid):
|
||||
solidNew = []
|
||||
|
||||
for face in solid.faces:
|
||||
vertices = numpy.array([ v.p.pos() for v in face.vertices ])
|
||||
# TODO: correct following vertices
|
||||
vertices = numpy.unique(vertices, axis = 0)
|
||||
|
||||
circuit = Wire([ Segment(Pnt(*v1), Pnt(*v2)) for v1, v2 in zip(vertices, numpy.roll(vertices, -1, axis = 0)) ])
|
||||
faceNew = Face(circuit)
|
||||
faceNew.name = face.name
|
||||
|
||||
solidNew.append(faceNew)
|
||||
|
||||
return numpy.array(solidNew).sum()
|
||||
|
||||
|
||||
class Simple(Periodic):
|
||||
def __init__(
|
||||
self,
|
||||
@ -64,7 +82,7 @@ class Simple(Periodic):
|
||||
self.lattice = lattice.Scale(zero, self.minimize)
|
||||
|
||||
# Inlet face
|
||||
if self.direction in [[1, 0, 0], [0, 0, 1]]:
|
||||
if (self.direction == numpy.array([1., 0., 0.])).prod():
|
||||
length = self.L * numpy.sqrt(2)
|
||||
width = self.L * numpy.sqrt(2)
|
||||
height = self.L
|
||||
@ -73,26 +91,32 @@ class Simple(Periodic):
|
||||
yw = xl
|
||||
zh = height
|
||||
|
||||
# TODO: correct compasion for arrays
|
||||
if self.direction == [1, 0, 0]:
|
||||
vertices = numpy.array([
|
||||
(xl, 0, 0),
|
||||
(0, yw, 0),
|
||||
(0, yw, zh),
|
||||
(xl, 0, zh)
|
||||
])
|
||||
extr = width
|
||||
vertices = numpy.array([
|
||||
(xl, 0, 0),
|
||||
(0, yw, 0),
|
||||
(0, yw, zh),
|
||||
(xl, 0, zh)
|
||||
])
|
||||
extr = width
|
||||
|
||||
elif self.direction == [0, 0, 1]:
|
||||
vertices = numpy.array([
|
||||
(0, yw, 0),
|
||||
(xl, 0, 0),
|
||||
(2 * xl, yw, 0),
|
||||
(xl, 2 * yw, 0)
|
||||
])
|
||||
extr = height
|
||||
elif (self.direction == numpy.array([0., 0., 1.])).prod():
|
||||
length = self.L * numpy.sqrt(2)
|
||||
width = self.L * numpy.sqrt(2)
|
||||
height = self.L
|
||||
|
||||
elif self.direction == [1, 1, 1]:
|
||||
xl = numpy.sqrt(length ** 2 * 0.5)
|
||||
yw = xl
|
||||
zh = height
|
||||
|
||||
vertices = numpy.array([
|
||||
(0, yw, 0),
|
||||
(xl, 0, 0),
|
||||
(2 * xl, yw, 0),
|
||||
(xl, 2 * yw, 0)
|
||||
])
|
||||
extr = height
|
||||
|
||||
elif (self.direction == numpy.array([1., 1., 1.])).prod():
|
||||
length = self.L * numpy.sqrt(2)
|
||||
width = self.L * numpy.sqrt(2)
|
||||
height = self.L
|
||||
@ -114,23 +138,33 @@ class Simple(Periodic):
|
||||
else:
|
||||
raise Exception(f"Direction { self.direction } is not implemented")
|
||||
|
||||
#
|
||||
|
||||
|
||||
# Cell
|
||||
circuit = Wire([ Segment(Pnt(*v1), Pnt(*v2)) for v1, v2 in zip(vertices, numpy.roll(vertices, -1, axis = 0)) ])
|
||||
inletface = Face(circuit)
|
||||
inletface.name = "inlet"
|
||||
|
||||
vecFlow = self.normal(inletface)
|
||||
# ISSUE: netgen.occ.Face.Extrude: the opposite face has the same name and normal vector as an initial face.
|
||||
self.cell = inletface.Extrude(extr)
|
||||
|
||||
self.cell = reconstruct(self.cell)
|
||||
print([ f.name for f in self.cell.faces ])
|
||||
# Boundaries
|
||||
symetryId = 0
|
||||
|
||||
for face in self.cell.faces:
|
||||
fNorm = self.normal(face)
|
||||
fAngle = self.angle(vecFlow, fNorm)
|
||||
print((face.center.pos() == inletface.center.pos()))
|
||||
print(fAngle)
|
||||
if fAngle == 0:
|
||||
if (face.center.pos() == inletface.center.pos()).prod():
|
||||
face.name = "inlet"
|
||||
|
||||
if fAngle == 0 and not face.center == inletface.center:
|
||||
face.name = "outlet"
|
||||
else:
|
||||
face.name = "outlet"
|
||||
|
||||
else:
|
||||
face.name = f"symetry{ symetryId }"
|
||||
|
@ -11,8 +11,8 @@ from anisotropy.openfoam.presets import (
|
||||
from anisotropy.openfoam.foamcase import FoamCase
|
||||
|
||||
class OnePhaseFlow(FoamCase):
|
||||
def __init__(self):
|
||||
FoamCase.__init__(self)
|
||||
def __init__(self, path: str = None):
|
||||
FoamCase.__init__(self, path = path)
|
||||
|
||||
controlDict = ControlDict()
|
||||
controlDict.update(
|
||||
@ -127,7 +127,7 @@ class OnePhaseFlow(FoamCase):
|
||||
|
||||
self.read()
|
||||
|
||||
self.solution.U["boundaryField"]["outlet"] = dict(
|
||||
self.U["boundaryField"]["outlet"] = dict(
|
||||
type = "pressureInletVelocity",
|
||||
value = "uniform (0 0 0)" # * direction
|
||||
)
|
||||
|
65
tests/test_core.py
Normal file
65
tests/test_core.py
Normal file
@ -0,0 +1,65 @@
|
||||
import os
|
||||
from os import path
|
||||
import unittest
|
||||
|
||||
unittest.TestLoader.sortTestMethodsUsing = None
|
||||
|
||||
class TestCore(unittest.TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
import netgen
|
||||
NETGEN_MODULE = True
|
||||
|
||||
except ImportError:
|
||||
NETGEN_MODULE = False
|
||||
|
||||
if not NETGEN_MODULE:
|
||||
self.skipTest("Missing Netgen.")
|
||||
|
||||
else:
|
||||
from anisotropy import core
|
||||
|
||||
self.core = core
|
||||
|
||||
self.outputPath = os.path.join(os.path.abspath("."), "tests/test_core_output")
|
||||
os.makedirs(self.outputPath, exist_ok = True)
|
||||
|
||||
def test_config(self):
|
||||
from copy import deepcopy
|
||||
|
||||
config = self.core.DefaultConfig()
|
||||
contentOld = deepcopy(config.content)
|
||||
filepath = os.path.join(self.outputPath, "test_config.toml")
|
||||
config.dump(filepath)
|
||||
|
||||
config = self.core.Config()
|
||||
config.load(filepath)
|
||||
|
||||
self.assertEqual(contentOld, config.content)
|
||||
|
||||
def test_runner(self):
|
||||
os.chdir(self.outputPath)
|
||||
|
||||
pathOld = os.path.abspath(".")
|
||||
config = self.core.DefaultConfig()
|
||||
config.expand()
|
||||
config.cases = [ config.cases[0] ]
|
||||
|
||||
runner = self.core.UltimateRunner(config = config, exec_id = True)
|
||||
|
||||
runner.computeShape()
|
||||
self.assertTrue(path.isfile(path.join(runner.casepath(), "shape.step")))
|
||||
|
||||
runner.computeMesh()
|
||||
self.assertTrue(path.isfile(path.join(runner.casepath(), "mesh.mesh")))
|
||||
|
||||
runner.computeFlow()
|
||||
#self.assertTrue(path.isfile(path.join(runner.casepath(), "mesh.mesh")))
|
||||
|
||||
os.chdir(pathOld)
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
39
tests/test_database.py
Normal file
39
tests/test_database.py
Normal file
@ -0,0 +1,39 @@
|
||||
import os
|
||||
import unittest
|
||||
|
||||
unittest.TestLoader.sortTestMethodsUsing = None
|
||||
|
||||
class TestDatabase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
from anisotropy import database
|
||||
|
||||
self.database = database
|
||||
|
||||
self.outputPath = os.path.join(os.path.abspath("."), "tests/test_database_output")
|
||||
os.makedirs(self.outputPath, exist_ok = True)
|
||||
|
||||
def test_setup(self):
|
||||
filepath = os.path.join(self.outputPath, "test_database.db")
|
||||
tables = [
|
||||
self.database.Execution,
|
||||
self.database.Physics,
|
||||
self.database.Shape,
|
||||
self.database.Mesh,
|
||||
self.database.Flow
|
||||
]
|
||||
db = self.database.Database(filepath)
|
||||
db.setup()
|
||||
|
||||
self.assertTrue(
|
||||
os.path.exists(filepath) and os.path.isfile(filepath),
|
||||
"database wasn't created"
|
||||
)
|
||||
|
||||
for table in tables:
|
||||
self.assertTrue(table.table_exists())
|
||||
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -7,10 +7,10 @@ class TestShaping(unittest.TestCase):
|
||||
def setUp(self):
|
||||
try:
|
||||
import netgen
|
||||
NETGEN_MODULE = False
|
||||
NETGEN_MODULE = True
|
||||
|
||||
except ImportError:
|
||||
NETGEN_MODULE = True
|
||||
NETGEN_MODULE = False
|
||||
|
||||
if not NETGEN_MODULE:
|
||||
self.skipTest("Missing Netgen.")
|
||||
|
Loading…
Reference in New Issue
Block a user