Mod: refactoring ultimate runner

This commit is contained in:
L-Nafaryus 2022-02-01 16:06:52 +05:00
parent 34313205d6
commit fb52e4c6c8
5 changed files with 138 additions and 149 deletions

View File

@ -33,7 +33,7 @@ env = {
def loadEnv(): def loadEnv():
prefix = "ANISOTROPY_" prefix = "AP_"
for k, v in env.items(): for k, v in env.items():
environ[f"{ prefix }{ k }"] = v environ[f"{ prefix }{ k }"] = v

View File

@ -1,37 +1,34 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from datetime import datetime
import os
from os import PathLike 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 import logging
from anisotropy.core.postProcess import PostProcess from anisotropy.database import Database, tables
from anisotropy.core.utils import Timer, ErrorHandler from anisotropy import shaping
from anisotropy.core.parallel import ParallelRunner from anisotropy import meshing
from anisotropy import solving
from . import config
from . import postprocess
from . import utils
from . import ParallelRunner
logger = logging.getLogger(__name__) 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): class UltimateRunner(object):
def __init__(self, config = None, exec_id: int = None, typo: str = "master"): def __init__(self, conf = None, exec_id: int = None, typo: str = "master"):
# Configuration file
self.config = config or DefaultConfig()
# Process recognition # Configuration file
self.typo = typo self.config = conf or config.DefaultConfig()
# Database preparation # Database preparation
self.database = Database(path = self.config["database"]) self.database = Database(self.config["database"])
self.exec_id = None self.exec_id = None
if exec_id: if exec_id:
@ -43,35 +40,38 @@ class UltimateRunner(object):
if not self.exec_id: if not self.exec_id:
with self.database: with self.database:
self.exec_id = T.Execution.create(date = datetime.now()) self.exec_id = tables.Execution.create(date = datetime.now())
# Parameters # Parameters
self.queue = [] self.queue = []
def createRow(self): def prepare_database(self):
# create a row in each table for the current case # create a row in each table for the current case
# shape
with self.database: with self.database:
shape = self.database.getShape(execution = self.exec_id, **self.config.params) shape = self.database.getShape(execution = self.exec_id, **self.config.params)
if not shape: 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) self.database.csave(shape)
# mesh
with self.database: with self.database:
mesh = self.database.getMesh(execution = self.exec_id, **self.config.params) mesh = self.database.getMesh(execution = self.exec_id, **self.config.params)
if not mesh: if not mesh:
mesh = T.Mesh(shape_id = shape.shape_id) mesh = tables.Mesh(shape_id = shape.shape_id)
self.database.csave(mesh) self.database.csave(mesh)
# onephase flow
with self.database: with self.database:
flow = self.database.getFlowOnephase(execution = self.exec_id, **self.config.params) flow = self.database.getFlowOnephase(execution = self.exec_id, **self.config.params)
if not flow: 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) self.database.csave(mesh)
def fill(self): def prepare_queue(self):
self.config.expand() self.config.expand()
logger.info(f"Preparing queue: { len(self.config.cases) }") logger.info(f"Preparing queue: { len(self.config.cases) }")
@ -101,7 +101,8 @@ class UltimateRunner(object):
@property @property
def casepath(self) -> PathLike: def casepath(self) -> PathLike:
params = self.config.params 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 /= "execution-{}".format(self.exec_id)
path /= "{}-[{},{},{}]-{}".format( path /= "{}-[{},{},{}]-{}".format(
params["label"], params["label"],
@ -111,7 +112,7 @@ class UltimateRunner(object):
return path.resolve() return path.resolve()
def computeShape(self): def compute_shape(self):
params = self.config.params params = self.config.params
shapeParams = self.database.getShape( shapeParams = self.database.getShape(
params["label"], params["label"],
@ -123,50 +124,53 @@ class UltimateRunner(object):
logger.info("Computing shape for {} with direction = {} and alpha = {}".format( logger.info("Computing shape for {} with direction = {} and alpha = {}".format(
params["label"], params["direction"], params["alpha"] params["label"], params["direction"], params["alpha"]
)) ))
shapeFile = self.casepath / "shape.step" shapefile = self.casepath / "shape.step"
timer = Timer() 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 ...") logger.info("Shape exists. Skipping ...")
return return
shapeSelected = { generate_shape = {
"simple": Simple, "simple": shaping.primitives.simple,
"bodyCentered": BodyCentered, "bodyCentered": shaping.primitives.bodyCentered,
"faceCentered": FaceCentered "faceCentered": shaping.primitives.faceCentered
}[shapeParams.label] }[shapeParams.label]
shape = shapeSelected( try:
direction = shapeParams.direction, # generate shape
alpha = shapeParams.alpha, shape = generate_shape(
shapeParams.alpha,
shapeParams.direction,
r0 = shapeParams.r0, r0 = shapeParams.r0,
filletsEnabled = shapeParams.filletsEnabled filletsEnabled = shapeParams.filletsEnabled
) )
with ErrorHandler() as (eh, handle): # export
handle(shape.build)()
if not eh.returncode:
self.casepath.mkdir(exist_ok = True) self.casepath.mkdir(exist_ok = True)
with ErrorHandler() as (eh, handle): shape.write(shapefile)
handle(shape.write)(shapeFile)
if not eh.returncode: except Exception as e:
logger.error(e)
shapeParams.shapeStatus = "failed"
else:
shapeParams.shapeStatus = "done" shapeParams.shapeStatus = "done"
shapeParams.volume = shape.shape.volume shapeParams.volume = shape.shape.volume
shapeParams.volumeCell = shape.cell.volume shapeParams.volumeCell = shape.cell.volume
shapeParams.porosity = shapeParams.volume / shapeParams.volumeCell shapeParams.porosity = shapeParams.volume / shapeParams.volumeCell
else: # commit parameters
shapeParams.shapeStatus = "failed"
logger.error(eh.error)
shapeParams.shapeExecutionTime = timer.elapsed() shapeParams.shapeExecutionTime = timer.elapsed()
self.database.csave(shapeParams) self.database.csave(shapeParams)
def computeMesh(self): def compute_mesh(self):
err, returncode = "", 0
params = self.config.params params = self.config.params
meshParams = self.database.getMesh( meshParams = self.database.getMesh(
params["label"], params["label"],
@ -178,59 +182,50 @@ class UltimateRunner(object):
logger.info("Computing mesh for {} with direction = {} and alpha = {}".format( logger.info("Computing mesh for {} with direction = {} and alpha = {}".format(
params["label"], params["direction"], params["alpha"] params["label"], params["direction"], params["alpha"]
)) ))
meshFile = self.casepath / "mesh.mesh" meshfile = self.casepath / "mesh.mesh"
timer = Timer() 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 ...") logger.info("Mesh exists. Skipping ...")
return return
# Shape # Shape
shape = None shape = None
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)
# Mesh
if not returncode:
mesh = Mesh(shape.shape)
try: try:
mesh.generate() # load shape
shapefile = self.casepath / "shape.step"
shape = shaping.Shape().read(shapefile)
except Exception as e: # generate mesh
err = e parameters = meshing.MeshingParameters()
returncode = 1 mesh = meshing.Mesh(shape)
mesh.generate(parameters)
if not returncode: # export
self.casepath.mkdir(exist_ok = True) self.casepath.mkdir(exist_ok = True)
try: mesh.write(meshfile)
mesh.write(meshFile)
mesh.write(self.casepath / "mesh.msh") mesh.write(self.casepath / "mesh.msh")
except Exception as e: except Exception as e:
err = e logger.error(e)
returncode = 1
if not returncode: meshParams.meshStatus = "failed"
else:
meshParams.meshStatus = "done" meshParams.meshStatus = "done"
meshParams.edges = len(self.mesh.edges) meshParams.edges = len(self.mesh.edges)
meshParams.faces = len(self.mesh.faces) meshParams.faces = len(self.mesh.faces)
meshParams.volumes = len(self.mesh.volumes) meshParams.volumes = len(self.mesh.volumes)
else: # commit parameters
logger.error(err)
meshParams.meshStatus = "failed"
with self.database:
meshParams.meshExecutionTime = timer.elapsed() meshParams.meshExecutionTime = timer.elapsed()
meshParams.save() self.database.csave(meshParams)
def computeFlow(self): def computeFlow(self):
params = self.config.params params = self.config.params
@ -245,55 +240,45 @@ class UltimateRunner(object):
logger.info("Computing flow for {} with direction = {} and alpha = {}".format( logger.info("Computing flow for {} with direction = {} and alpha = {}".format(
params["label"], params["direction"], params["alpha"] params["label"], params["direction"], params["alpha"]
)) ))
timer = Timer() timer = utils.Timer()
# precalculate some parameters
flowParams.viscosityKinematic = flowParams.viscosity / flowParams.density 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 = solving.FlowOnephase(
flow = OnePhaseFlow(
direction = params["direction"], direction = params["direction"],
**self.database.getFlowOnephase(*query, to_dict = True), **flowParamsDict,
path = self.casepath 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: 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: except Exception as e:
# out, err, returncode = "", e, 1 logger.error(e)
logger.error(e, exc_info = True)
if returncode == 0:
flowParams.flowStatus = "done"
else:
# logger.error(err)
flowParams.flowStatus = "failed" flowParams.flowStatus = "failed"
with self.database: else:
flowParams.flowExecutionTime = timer.elapsed() flowParams.flowStatus = "done"
flowParams.save()
def computePostProcess(self): # commit parameters
flowParams.flowExecutionTime = timer.elapsed()
self.database.csave(flowParams)
def compute_postprocess(self):
params = self.config.params params = self.config.params
flowParams = self.database.getFlowOnephase( flowParams = self.database.getFlowOnephase(
params["label"], params["label"],
@ -306,33 +291,30 @@ class UltimateRunner(object):
params["label"], params["direction"], params["alpha"] params["label"], params["direction"], params["alpha"]
)) ))
postProcess = PostProcess(self.casepath) postProcess = postprocess.PostProcess(self.casepath)
if flowParams.flowStatus == "done": if flowParams.flowStatus == "done":
flowParams.flowRate = postProcess.flowRate("outlet") flowParams.flowRate = postProcess.flowRate("outlet")
with self.database: self.database.csave(flowParams)
flowParams.save()
self.dispose()
def pipeline(self, stage: str = None): def pipeline(self, stage: str = None):
stage = stage or self.config["stage"] stage = stage or self.config["stage"]
if stage in ["shape", "all"]: if stage in ["shape", "all"]:
self.computeShape() self.compute_shape()
if stage in ["mesh", "all"]: if stage in ["mesh", "all"]:
self.computeMesh() self.compute_mesh()
if stage in ["flow", "all"]: if stage in ["flow", "all"]:
self.computeFlow() self.compute_flow()
if stage in ["postProcess", "all"]: if stage in ["postProcess", "all"]:
self.computePostProcess() self.compute_postprocess()
@staticmethod @staticmethod
def subrunner(*args, **kwargs): def subrunner(*args, **kwargs):
runner = UltimateRunner(config = kwargs["config"], exec_id = kwargs["exec_id"], typo = "worker") runner = UltimateRunner(config = kwargs["config"], exec_id = kwargs["exec_id"])
runner.createRow() runner.prepare_database()
runner.pipeline() runner.pipeline()

View File

@ -11,11 +11,11 @@ from . import tables
class Database(pw.SqliteDatabase): 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 """A Database object contains SQLite database with convient
properties and methods properties and methods
""" """
self.filepath = kwargs.get("path", None) self.path = filename
self.pragmas_ = kwargs.get("pragmas", { "foreign_keys": 1, "journal_mode": "wal" }) self.pragmas_ = kwargs.get("pragmas", { "foreign_keys": 1, "journal_mode": "wal" })
self.field_types_ = kwargs.get("field_types", { "list": "text" }) self.field_types_ = kwargs.get("field_types", { "list": "text" })
self.autoconnect_ = kwargs.get("autoconnect", False) self.autoconnect_ = kwargs.get("autoconnect", False)
@ -28,7 +28,7 @@ class Database(pw.SqliteDatabase):
autoconnect = self.autoconnect_ autoconnect = self.autoconnect_
) )
if self.filepath: if self.path:
self.setup() self.setup()
@property @property
@ -45,9 +45,9 @@ class Database(pw.SqliteDatabase):
:return: :return:
Self. Self.
""" """
self.filepath = pathlib.Path(filename or self.filepath).resolve() self.path = pathlib.Path(filename or self.path).resolve()
self.init( self.init(
self.filepath, self.path,
pragmas = self.pragmas_ pragmas = self.pragmas_
) )
tables.database_proxy.initialize(self) tables.database_proxy.initialize(self)

View File

@ -93,14 +93,21 @@ class Mesh:
return None if len(self.points) == 0 else self.points[0].size return None if len(self.points) == 0 else self.points[0].size
@property @property
def geometry(self) -> ng_occ.OCCGeometry: def geometry(self) -> ng_occ.OCCGeometry | None:
"""Netgen OCCGeometry object. """Netgen OCCGeometry object.
:return: :return:
OCCGeometry object that can be used for meshing OCCGeometry object that can be used for meshing
or None if shape is not defined. 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( def generate(
self, self,

View File

@ -6,8 +6,10 @@ from anisotropy.openfoam import FoamCase, uniform
from numpy import array from numpy import array
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class OnePhaseFlow(FoamCase): class OnePhaseFlow(FoamCase):
def __init__( def __init__(
self, self,
@ -133,7 +135,6 @@ class OnePhaseFlow(FoamCase):
u u
]) ])
def createPatches(self, patches: dict): def createPatches(self, patches: dict):
# initial 43 unnamed patches -> # initial 43 unnamed patches ->
# 6 named patches (inlet, outlet, wall, symetry0 - 3/5) -> # 6 named patches (inlet, outlet, wall, symetry0 - 3/5) ->
@ -172,7 +173,6 @@ class OnePhaseFlow(FoamCase):
self.append(createPatchDict) self.append(createPatchDict)
def build(self) -> tuple[str, str, int]: def build(self) -> tuple[str, str, int]:
# TODO: configure working directory (FoamCase)
with self: with self:
self.write() self.write()