diff --git a/anisotropy/__init__.py b/anisotropy/__init__.py index 90032e9..3915371 100644 --- a/anisotropy/__init__.py +++ b/anisotropy/__init__.py @@ -33,7 +33,7 @@ env = { def loadEnv(): - prefix = "ANISOTROPY_" + prefix = "AP_" for k, v in env.items(): environ[f"{ prefix }{ k }"] = v diff --git a/anisotropy/core/runner.py b/anisotropy/core/runner.py index 673f809..4e01bd0 100644 --- a/anisotropy/core/runner.py +++ b/anisotropy/core/runner.py @@ -1,37 +1,34 @@ # -*- coding: utf-8 -*- -from datetime import datetime -import os from os import PathLike -from pathlib import Path - -from anisotropy.core.config import DefaultConfig +from os import environ +from datetime import datetime +import pathlib import logging -from anisotropy.core.postProcess import PostProcess -from anisotropy.core.utils import Timer, ErrorHandler -from anisotropy.core.parallel import ParallelRunner +from anisotropy.database import Database, tables +from anisotropy import shaping +from anisotropy import meshing +from anisotropy import solving + +from . import config +from . import postprocess +from . import utils +from . import ParallelRunner + logger = logging.getLogger(__name__) -from anisotropy.database import Database, tables as T - -from anisotropy.shaping import Simple, BodyCentered, FaceCentered, Shape -from anisotropy.meshing import Mesh -from anisotropy.solving import OnePhaseFlow - class UltimateRunner(object): - def __init__(self, config = None, exec_id: int = None, typo: str = "master"): - # Configuration file - self.config = config or DefaultConfig() + def __init__(self, conf = None, exec_id: int = None, typo: str = "master"): - # Process recognition - self.typo = typo + # Configuration file + self.config = conf or config.DefaultConfig() # Database preparation - self.database = Database(path = self.config["database"]) + self.database = Database(self.config["database"]) self.exec_id = None if exec_id: @@ -43,35 +40,38 @@ class UltimateRunner(object): if not self.exec_id: with self.database: - self.exec_id = T.Execution.create(date = datetime.now()) + self.exec_id = tables.Execution.create(date = datetime.now()) # Parameters self.queue = [] - def createRow(self): + def prepare_database(self): # create a row in each table for the current case + # shape with self.database: shape = self.database.getShape(execution = self.exec_id, **self.config.params) if not shape: - shape = T.Shape(exec_id = self.exec_id, **self.config.params) + shape = tables.Shape(exec_id = self.exec_id, **self.config.params) self.database.csave(shape) + # mesh with self.database: mesh = self.database.getMesh(execution = self.exec_id, **self.config.params) if not mesh: - mesh = T.Mesh(shape_id = shape.shape_id) + mesh = tables.Mesh(shape_id = shape.shape_id) self.database.csave(mesh) + # onephase flow with self.database: flow = self.database.getFlowOnephase(execution = self.exec_id, **self.config.params) if not flow: - flow = T.FlowOnephase(mesh_id = mesh.mesh_id, **self.config.params) + flow = tables.FlowOnephase(mesh_id = mesh.mesh_id, **self.config.params) self.database.csave(mesh) - def fill(self): + def prepare_queue(self): self.config.expand() logger.info(f"Preparing queue: { len(self.config.cases) }") @@ -101,7 +101,8 @@ class UltimateRunner(object): @property def casepath(self) -> PathLike: params = self.config.params - path = Path(os.environ["ANISOTROPY_CWD"], os.environ["ANISOTROPY_BUILD_DIR"]) + + path = pathlib.Path(environ["AP_CWD"], environ["AP_BUILD_DIR"]) path /= "execution-{}".format(self.exec_id) path /= "{}-[{},{},{}]-{}".format( params["label"], @@ -111,7 +112,7 @@ class UltimateRunner(object): return path.resolve() - def computeShape(self): + def compute_shape(self): params = self.config.params shapeParams = self.database.getShape( params["label"], @@ -123,50 +124,53 @@ class UltimateRunner(object): logger.info("Computing shape for {} with direction = {} and alpha = {}".format( params["label"], params["direction"], params["alpha"] )) - shapeFile = self.casepath / "shape.step" - timer = Timer() + shapefile = self.casepath / "shape.step" + timer = utils.Timer() - if shapeFile.exists() and shapeParams.shapeStatus == "done" and not self.config["overwrite"]: + if ( + shapefile.exists() and + shapeParams.shapeStatus == "done" and + not self.config["overwrite"] + ): logger.info("Shape exists. Skipping ...") return - shapeSelected = { - "simple": Simple, - "bodyCentered": BodyCentered, - "faceCentered": FaceCentered + generate_shape = { + "simple": shaping.primitives.simple, + "bodyCentered": shaping.primitives.bodyCentered, + "faceCentered": shaping.primitives.faceCentered }[shapeParams.label] - - shape = shapeSelected( - direction = shapeParams.direction, - alpha = shapeParams.alpha, - r0 = shapeParams.r0, - filletsEnabled = shapeParams.filletsEnabled - ) - with ErrorHandler() as (eh, handle): - handle(shape.build)() + try: + # generate shape + shape = generate_shape( + shapeParams.alpha, + shapeParams.direction, + r0 = shapeParams.r0, + filletsEnabled = shapeParams.filletsEnabled + ) - if not eh.returncode: + # export self.casepath.mkdir(exist_ok = True) - with ErrorHandler() as (eh, handle): - handle(shape.write)(shapeFile) + shape.write(shapefile) - if not eh.returncode: + except Exception as e: + logger.error(e) + + shapeParams.shapeStatus = "failed" + + else: shapeParams.shapeStatus = "done" shapeParams.volume = shape.shape.volume shapeParams.volumeCell = shape.cell.volume shapeParams.porosity = shapeParams.volume / shapeParams.volumeCell - else: - shapeParams.shapeStatus = "failed" - logger.error(eh.error) - + # commit parameters shapeParams.shapeExecutionTime = timer.elapsed() self.database.csave(shapeParams) - def computeMesh(self): - err, returncode = "", 0 + def compute_mesh(self): params = self.config.params meshParams = self.database.getMesh( params["label"], @@ -178,59 +182,50 @@ class UltimateRunner(object): logger.info("Computing mesh for {} with direction = {} and alpha = {}".format( params["label"], params["direction"], params["alpha"] )) - meshFile = self.casepath / "mesh.mesh" - timer = Timer() + meshfile = self.casepath / "mesh.mesh" + timer = utils.Timer() - if meshFile.exists() and meshParams.meshStatus == "done" and not self.config["overwrite"]: + if ( + meshfile.exists() and + meshParams.meshStatus == "done" and + not self.config["overwrite"] + ): logger.info("Mesh exists. Skipping ...") return # Shape shape = None - shapeFile = self.casepath / "shape.step" - if not shapeFile.exists() and not shapeFile.is_file(): - err = f"File not found: { shapeFile }" - returncode = 2 + try: + # load shape + shapefile = self.casepath / "shape.step" + shape = shaping.Shape().read(shapefile) - if not returncode: - shape = Shape().read(shapeFile) + # generate mesh + parameters = meshing.MeshingParameters() + mesh = meshing.Mesh(shape) + mesh.generate(parameters) - # Mesh - if not returncode: - mesh = Mesh(shape.shape) - - try: - mesh.generate() - - except Exception as e: - err = e - returncode = 1 - - if not returncode: + # export self.casepath.mkdir(exist_ok = True) - try: - mesh.write(meshFile) - mesh.write(self.casepath / "mesh.msh") - - except Exception as e: - err = e - returncode = 1 + mesh.write(meshfile) + mesh.write(self.casepath / "mesh.msh") - if not returncode: + except Exception as e: + logger.error(e) + + meshParams.meshStatus = "failed" + + else: meshParams.meshStatus = "done" meshParams.edges = len(self.mesh.edges) meshParams.faces = len(self.mesh.faces) meshParams.volumes = len(self.mesh.volumes) - else: - logger.error(err) - meshParams.meshStatus = "failed" - - with self.database: - meshParams.meshExecutionTime = timer.elapsed() - meshParams.save() + # commit parameters + meshParams.meshExecutionTime = timer.elapsed() + self.database.csave(meshParams) def computeFlow(self): params = self.config.params @@ -245,55 +240,45 @@ class UltimateRunner(object): logger.info("Computing flow for {} with direction = {} and alpha = {}".format( params["label"], params["direction"], params["alpha"] )) - timer = Timer() + timer = utils.Timer() + # precalculate some parameters flowParams.viscosityKinematic = flowParams.viscosity / flowParams.density + self.database.csave(flowParams) - with self.database: - flowParams.save() + # + flowParamsDict = self.database.getFlowOnephase(*query, to_dict = True) - with self.database: - flow = OnePhaseFlow( - direction = params["direction"], - **self.database.getFlowOnephase(*query, to_dict = True), - path = self.casepath - ) + flow = solving.FlowOnephase( + direction = params["direction"], + **flowParamsDict, + path = self.casepath + ) - # Shape - shapeFile = self.casepath / "shape.step" - - if not shapeFile.exists() and not shapeFile.is_file(): - err = f"File not found: { shapeFile }" - returncode = 2 - - if not returncode: - shape = Shape().read(shapeFile) - - # Patches from occ to openfoam - patches = shape.patches(group = True, shiftIndex = True, prefix = "patch") - flow.createPatches(patches) - flow.write() - - # Build a flow try: - out, err, returncode = flow.build() + # load shape + shapefile = self.casepath / "shape.step" + shape = shaping.Shape().read(shapefile) + + patches = shape.patches(group = True, shiftIndex = True, prefix = "patch") + + # generate solution + flow.createPatches(patches) + flow.build() except Exception as e: - # out, err, returncode = "", e, 1 - logger.error(e, exc_info = True) + logger.error(e) - if returncode == 0: - flowParams.flowStatus = "done" - - else: - # logger.error(err) flowParams.flowStatus = "failed" - with self.database: - flowParams.flowExecutionTime = timer.elapsed() - flowParams.save() + else: + flowParams.flowStatus = "done" - def computePostProcess(self): + # commit parameters + flowParams.flowExecutionTime = timer.elapsed() + self.database.csave(flowParams) + + def compute_postprocess(self): params = self.config.params flowParams = self.database.getFlowOnephase( params["label"], @@ -306,33 +291,30 @@ class UltimateRunner(object): params["label"], params["direction"], params["alpha"] )) - postProcess = PostProcess(self.casepath) + postProcess = postprocess.PostProcess(self.casepath) if flowParams.flowStatus == "done": flowParams.flowRate = postProcess.flowRate("outlet") - with self.database: - flowParams.save() + self.database.csave(flowParams) - self.dispose() - def pipeline(self, stage: str = None): stage = stage or self.config["stage"] if stage in ["shape", "all"]: - self.computeShape() + self.compute_shape() if stage in ["mesh", "all"]: - self.computeMesh() + self.compute_mesh() if stage in ["flow", "all"]: - self.computeFlow() + self.compute_flow() if stage in ["postProcess", "all"]: - self.computePostProcess() + self.compute_postprocess() @staticmethod def subrunner(*args, **kwargs): - runner = UltimateRunner(config = kwargs["config"], exec_id = kwargs["exec_id"], typo = "worker") - runner.createRow() + runner = UltimateRunner(config = kwargs["config"], exec_id = kwargs["exec_id"]) + runner.prepare_database() runner.pipeline() diff --git a/anisotropy/database/database.py b/anisotropy/database/database.py index 48da9b1..875b4db 100644 --- a/anisotropy/database/database.py +++ b/anisotropy/database/database.py @@ -11,11 +11,11 @@ from . import tables class Database(pw.SqliteDatabase): - def __init__(self, *args, **kwargs): + def __init__(self, filename: str = None, *args, **kwargs): """A Database object contains SQLite database with convient properties and methods """ - self.filepath = kwargs.get("path", None) + self.path = filename self.pragmas_ = kwargs.get("pragmas", { "foreign_keys": 1, "journal_mode": "wal" }) self.field_types_ = kwargs.get("field_types", { "list": "text" }) self.autoconnect_ = kwargs.get("autoconnect", False) @@ -28,7 +28,7 @@ class Database(pw.SqliteDatabase): autoconnect = self.autoconnect_ ) - if self.filepath: + if self.path: self.setup() @property @@ -45,9 +45,9 @@ class Database(pw.SqliteDatabase): :return: Self. """ - self.filepath = pathlib.Path(filename or self.filepath).resolve() + self.path = pathlib.Path(filename or self.path).resolve() self.init( - self.filepath, + self.path, pragmas = self.pragmas_ ) tables.database_proxy.initialize(self) diff --git a/anisotropy/meshing/mesh.py b/anisotropy/meshing/mesh.py index 77b3b4e..17fb1d6 100644 --- a/anisotropy/meshing/mesh.py +++ b/anisotropy/meshing/mesh.py @@ -93,14 +93,21 @@ class Mesh: return None if len(self.points) == 0 else self.points[0].size @property - def geometry(self) -> ng_occ.OCCGeometry: + def geometry(self) -> ng_occ.OCCGeometry | None: """Netgen OCCGeometry object. :return: OCCGeometry object that can be used for meshing or None if shape is not defined. """ - return ng_occ.OCCGeometry(self.shape) if self.shape else None + if self.shape is not None: + if hasattr(self.shape, "geometry"): + return self.shape.geometry + + else: + return ng_occ.OCCGeometry(self.shape) + + return None def generate( self, diff --git a/anisotropy/solving/onephase.py b/anisotropy/solving/onephase.py index 5ff789b..94486f6 100644 --- a/anisotropy/solving/onephase.py +++ b/anisotropy/solving/onephase.py @@ -6,8 +6,10 @@ from anisotropy.openfoam import FoamCase, uniform from numpy import array import logging + logger = logging.getLogger(__name__) + class OnePhaseFlow(FoamCase): def __init__( self, @@ -132,7 +134,6 @@ class OnePhaseFlow(FoamCase): p, u ]) - def createPatches(self, patches: dict): # initial 43 unnamed patches -> @@ -172,7 +173,6 @@ class OnePhaseFlow(FoamCase): self.append(createPatchDict) def build(self) -> tuple[str, str, int]: - # TODO: configure working directory (FoamCase) with self: self.write() @@ -183,7 +183,7 @@ class OnePhaseFlow(FoamCase): "scale": [1e-5, 1e-5, 1e-5] }) R.renumberMesh() - #R.potentialFoam() + # R.potentialFoam() self.read()