From 6abbce9eb7fa097f7051d11a34d2e439dca151d4 Mon Sep 17 00:00:00 2001 From: L-Nafaryus Date: Wed, 11 Aug 2021 23:17:43 +0500 Subject: [PATCH] Mod: working mesh --- anisotropy/__init__.py | 22 +- anisotropy/core/cli.py | 35 +- anisotropy/core/database.py | 202 +++++++++++ anisotropy/core/main.py | 668 +++-------------------------------- anisotropy/core/models.py | 28 +- anisotropy/core/utils.py | 86 +++-- anisotropy/salomepl/utils.py | 11 +- tests/anisotropy-cli.py | 10 + tests/test_anisotropy.py | 8 +- 9 files changed, 396 insertions(+), 674 deletions(-) create mode 100644 anisotropy/core/database.py create mode 100644 tests/anisotropy-cli.py diff --git a/anisotropy/__init__.py b/anisotropy/__init__.py index 212c6cc..576aca2 100644 --- a/anisotropy/__init__.py +++ b/anisotropy/__init__.py @@ -10,4 +10,24 @@ __version__ = "1.1.0" __author__ = __maintainer = "George Kusayko" __email__ = "gkusayko@gmail.com" -#from anisotropy.core.main import main +### +# Environment +## +import os + +env = dict( + ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +) +env.update( + BUILD = os.path.join(env["ROOT"], "build"), + LOG = os.path.join(env["ROOT"], "logs"), + CONFIG = os.path.join(env["ROOT"], "anisotropy/config/default.toml") +) +env.update( + logger_name = "anisotropy", + db_path = env["BUILD"], + salome_port = 2810, + openfoam_template = os.path.join(env["ROOT"], "anisotropy/openfoam/template") +) + +del os diff --git a/anisotropy/core/cli.py b/anisotropy/core/cli.py index 1bc73d6..b4a4898 100644 --- a/anisotropy/core/cli.py +++ b/anisotropy/core/cli.py @@ -25,44 +25,51 @@ def compute(stage, params): from anisotropy.core.main import Anisotropy model = Anisotropy() - model.setupDB() + model.db.setup() - if model.isEmptyDB(): + if model.db.isempty(): paramsAll = model.loadFromScratch() for entry in paramsAll: - model.updateDB(entry) + model.db.update(entry) - model.loadDB(type, direction, theta) + (type, direction, theta) = ("simple", [1.0, 0.0, 0.0], 0.01) + + model.load(type, direction, theta) # TODO: merge cli params with db params here model.evalParams() - model.updateDB() + model.update() # TODO: do smth with output if stage == "all" or stage == "mesh": ((out, err, code), elapsed) = model.computeMesh(type, direction, theta) + model.load(type, direction, theta) + model.params["meshresult"]["calculationTime"] = elapsed + model.update() + if stage == "all" or stage == "flow": ((out, err, code), elapsed) = model.computeFlow(type, direction, theta) @anisotropy.command() @click.argument("root") -@click.argument("name") +@click.argument("type") @click.argument("direction") -@click.argument("theta", type = click.FLOAT) -def _compute_mesh(root, name, direction, theta): +@click.argument("theta") +def computemesh(root, type, direction, theta): # [Salome Environment] - + ### # Args ## - direction = list(map(lambda num: float(num), direction[1:-1].split(","))) + direction = [ float(num) for num in direction[1:-1].split(" ") if num ] + theta = float(theta) ### # Modules ## - import salome + import os, sys sys.path.extend([ root, @@ -73,8 +80,7 @@ def _compute_mesh(root, name, direction, theta): ### model = Anisotropy() - model.setupDB() - model.loadDB(type, direction, theta) + model.load(type, direction, theta) model.genmesh() @@ -82,4 +88,5 @@ def _compute_mesh(root, name, direction, theta): ### # CLI entry ## -#anisotropy() +if __name__ == "__main__": + anisotropy() diff --git a/anisotropy/core/database.py b/anisotropy/core/database.py new file mode 100644 index 0000000..dc2d641 --- /dev/null +++ b/anisotropy/core/database.py @@ -0,0 +1,202 @@ +# -*- 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 + +logger = logging.getLogger(env["logger_name"]) +setupLogger(logger, logging.INFO, env["LOG"]) + + +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 + ]) + + + def isempty(self) -> 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() + + return params + + + def update(self, params: dict): + if not params: + logger.error("Trying to update db from empty parameters") + return + + query = ( + Structure + .select(Structure, Mesh) + .join( + Mesh, + JOIN.INNER, + on = (Mesh.structure_id == Structure.structure_id) + ) + .where( + Structure.type == params["structure"]["type"], + Structure.direction == str(params["structure"]["direction"]), + Structure.theta == params["structure"]["theta"] + ) + ) + + structureID = self._updateStructure(params["structure"], query) + + meshID = self._updateMesh(params["mesh"], query, structureID) + + for submeshParams in params.get("submesh", []): + self._updateSubMesh(submeshParams, query, meshID) + + self._updateMeshResult(params.get("meshresult", {}), query, meshID) + + + 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() diff --git a/anisotropy/core/main.py b/anisotropy/core/main.py index bb52307..0894e19 100644 --- a/anisotropy/core/main.py +++ b/anisotropy/core/main.py @@ -1,65 +1,35 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import os, sys import time from datetime import timedelta, datetime import shutil import logging +from copy import deepcopy +from math import sqrt import toml -from copy import deepcopy -from anisotropy.core.models import db, Structure, Mesh, SubMesh, MeshResult -from anisotropy.core.utils import struct, deepupdate - - -### -# Environment variables and config -## -env = { "ROOT": os.path.abspath(".") } -env.update( - BUILD = os.path.join(env["ROOT"], "build"), - LOG = os.path.join(env["ROOT"], "logs"), - DEFAULT_CONFIG = os.path.join(env["ROOT"], "anisotropy/config/default.toml"), - CONFIG = os.path.join(env["ROOT"], "conf/config.toml") +from anisotropy import ( + __version__, env, + openfoam ) -env["db_path"] = env["BUILD"] -env["salome_port"] = 2810 +from anisotropy.core.utils import setupLogger, timer +from anisotropy.core.database import Database +from anisotropy import salomepl +import anisotropy.salomepl.utils +import anisotropy.salomepl.geometry +import anisotropy.salomepl.mesh +from anisotropy.samples import Simple, FaceCentered, BodyCentered -#if os.path.exists(env["CONFIG"]): -# config = toml.load(env["CONFIG"]) - -# for restricted in ["ROOT", "BUILD", "LOG", "CONFIG"]: -# if config.get(restricted): -# config.pop(restricted) - - # TODO: not working if custom config empty and etc -# for m, structure in enumerate(config["structures"]): -# for n, estructure in enumerate(env["structures"]): -# if estructure["name"] == structure["name"]: -# deepupdate(env["structures"][n], config["structures"][m]) - -# config.pop("structures") -# deepupdate(env, config) - - -### -# Logger -## -from anisotropy.core.utils import setupLogger -logger_env = env.get("logger", {}) - -logger = logging.getLogger(logger_env.get("name", "anisotropy")) -setupLogger(logger, logging.INFO) +logger = logging.getLogger(env["logger_name"]) +setupLogger(logger, logging.INFO, env["LOG"]) peeweeLogger = logging.getLogger("peewee") peeweeLogger.setLevel(logging.INFO) -from anisotropy.core.utils import timer -from anisotropy import __version__ -from anisotropy import salomepl -from anisotropy import openfoam -from anisotropy.samples import Simple, FaceCentered, BodyCentered -from math import sqrt -from peewee import JOIN class Anisotropy(object): """Ultimate class that organize whole working process""" @@ -68,10 +38,19 @@ class Anisotropy(object): """Constructor method""" self.env = env - self.db = None + self.db = Database("anisotropy", env["db_path"]) self.params = [] + def load(self, structure_type: str, structure_direction: list, structure_theta: float): + self.db.setup() + self.params = self.db.load(structure_type, structure_direction, structure_theta) + + def update(self, params: dict = None): + self.db.setup() + self.db.update(self.params if not params else params) + + @staticmethod def version(): """Returns versions of all used main programs @@ -103,11 +82,11 @@ class Anisotropy(object): :rtype: list """ - if not os.path.exists(self.env["DEFAULT_CONFIG"]): + if not os.path.exists(self.env["CONFIG"]): logger.error("Missed default configuration file") return - buf = toml.load(self.env["DEFAULT_CONFIG"]).get("structures") + buf = toml.load(self.env["CONFIG"]).get("structures") paramsAll = [] # TODO: custom config and merge @@ -224,237 +203,22 @@ class Anisotropy(object): ) - - - - - def getParams(self, structure: str, direction: list, theta: float): - for entry in self.params: - if entry["name"] == structure and \ - entry["geometry"]["direction"] == direction and \ - entry["geometry"]["theta"] == theta: - return entry - - - def setupDB(self): - os.makedirs(self.env["db_path"], exist_ok = True) - - dbname = os.path.join(self.env["db_path"], "anisotropy.db") - self.db = db - self.db.init(dbname) - - if not os.path.exists(dbname): - self.db.create_tables([ - Structure, - Mesh, - SubMesh, - MeshResult - ]) - - - 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 #req["structure_id"] - ) - ) - query.execute() - - return tabID - - def _updateSubMesh(self, src: dict, queryMain, meshID) -> None: - 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: - #req = queryMain.dicts().get() - #tabID = req["mesh_id"] - - query = ( - SubMesh.update(**raw) - .where( - SubMesh.mesh_id == meshID, #req["mesh_id"], - SubMesh.name == src["name"] - ) - ) - query.execute() - - def _updateMeshResult(self, src: dict, queryMain, meshID) -> None: - 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: - #req = queryMain.dicts().get() - #tabID = req["mesh_id"] - - query = ( - MeshResult.update(**raw) - .where( - MeshResult.mesh_id == meshID #req["mesh_id"] - ) - ) - query.execute() - - @timer - def updateDB(self, src: dict = None): - if src: - params = src - - elif self.params: - params = self.params - - else: - logger.error("Trying to update db from empty parameters") - return - - query = ( - Structure - .select(Structure, Mesh) - .join( - Mesh, - JOIN.INNER, - on = (Mesh.structure_id == Structure.structure_id) - ) - .where( - Structure.type == params["structure"]["type"], - Structure.direction == str(params["structure"]["direction"]), - Structure.theta == params["structure"]["theta"] - ) - ) - - structureID = self._updateStructure(params["structure"], query) - - meshID = self._updateMesh(params["mesh"], query, structureID) - - for submeshParams in params.get("submesh", []): - self._updateSubMesh(submeshParams, query, meshID) - - self._updateMeshResult(params.get("meshresults", {}), query, meshID) - - - def loadDB(self, structure_type: str, structure_direction: list, structure_theta: float): - structureQuery = ( - Structure - .select() - .where( - Structure.type == structure_type, - Structure.direction == str(structure_direction), - Structure.theta == structure_theta - ) - ) - - self.params = {} - - with self.db.atomic(): - if structureQuery.exists(): - self.params["structure"] = structureQuery.dicts().get() - - meshQuery = structureQuery.get().meshes - - if meshQuery.exists(): - self.params["mesh"] = meshQuery.dicts().get() - - submeshQuery = meshQuery.get().submeshes - - if submeshQuery.exists(): - self.params["submesh"] = [ entry for entry in submeshQuery.dicts() ] - - meshresultQuery = meshQuery.get().meshresults - - if meshresultQuery.exists(): - self.params["meshresult"] = meshresultQuery.dicts().get() - - - - # TODO: loadDB (one model), loadsDB (all models) - @timer - def updateFromDB(self): - squery = Structure.select().order_by(Structure.structure_id) - mquery = Mesh.select().order_by(Mesh.structure_id) - smquery = SubMesh.select() - mrquery = MeshResult.select().order_dy(MeshResult.mesh_id) - self.params = [] - - for s, m, mr in zip(squery.dicts(), mquery.dicts(), mrquery.dicts()): - name = s.pop("name") - path = s.pop("path") - - self.params.append(dict( - name = name, - path = path, - geometry = s, - mesh = m, - submesh = [ d for d in smquery.dicts() if d["mesh_id"] == m["mesh_id"] ], - meshresults = mr - )) - - self.params = sorted(self.params, key = lambda entry: f"{ entry['name'] } { entry['geometry']['direction'] } { entry['geometry']['theta'] }") - @timer def computeMesh(self, type, direction, theta): - scriptpath = os.path.join(self.env["ROOT"], "anisotropy/__main__.py") + scriptpath = os.path.join(self.env["ROOT"], "anisotropy/core/cli.py") port = 2900 - return salomepl.utils.runSalome(port, scriptpath, self.env["ROOT"], "_compute_mesh", type, direction, theta) + return salomepl.utils.runSalome( + self.env["salome_port"], + scriptpath, + self.env["ROOT"], + "computemesh", type, direction, theta, + logpath = os.path.join(self.env["LOG"], "salome.log") + ) def genmesh(self): + # ISSUE: double logger output + import salome p = self.params @@ -530,24 +294,24 @@ class Anisotropy(object): mesh.Triangle(subshape, **submesh) - model.updateDB() + self.update() returncode, errors = mesh.compute() if not returncode: mesh.removePyramids() mesh.assignGroups() - casePath = model.getCasePath() + casePath = self.getCasePath() os.makedirs(casePath, exist_ok = True) mesh.exportUNV(os.path.join(casePath, "mesh.unv")) meshStats = mesh.stats() - p["meshresults"] = dict( + p["meshresult"] = dict( surfaceArea = surfaceArea, volume = volume, **meshStats ) - model.updateDB() + self.update() logger.info("mesh stats:\n{}".format( "\n".join(map(lambda v: f"{ v[0] }:\t{ v[1] }", meshStats.items())) @@ -556,11 +320,11 @@ class Anisotropy(object): else: logger.error(errors) - p["meshresults"] = dict( + p["meshresult"] = dict( surfaceArea = surfaceArea, volume = volume ) - model.updateDB() + self.update() salome.salome_close() @@ -571,13 +335,15 @@ class Anisotropy(object): ## foamCase = [ "0", "constant", "system" ] + flow = self.params["flow"] + # ISSUE: ideasUnvToFoam cannot import mesh with '-case' flag so 'os.chdir' for that os.chdir(self.getCasePath()) openfoam.foamClean() for d in foamCase: shutil.copytree( - os.path.join(ROOT, "anisotropy/openfoam/template", d), + os.path.join(self.env["openfoam_template"], d), os.path.join(case, d) ) @@ -613,7 +379,7 @@ class Anisotropy(object): if out: logger.info(out) # TODO: replace all task variables - openfoam.transformPoints(task.flow.scale) + openfoam.transformPoints(flow["scale"]) ### # Decomposition and initial approximation @@ -621,15 +387,15 @@ class Anisotropy(object): openfoam.foamDictionary( "constant/transportProperties", "nu", - str(task.flow.constant.nu) + str(flow["constant"]["nu"]) ) openfoam.decomposePar() openfoam.renumberMesh() - pressureBF = task.flow.approx.pressure.boundaryField - velocityBF = task.flow.approx.velocity.boundaryField + pressureBF = flow["approx"]["pressure"]["boundaryField"] + velocityBF = flow["approx"]["velocity"]["boundaryField"] direction = { "[1, 0, 0]": 0, "[0, 0, 1]": 1, @@ -639,14 +405,14 @@ class Anisotropy(object): openfoam.foamDictionary( "0/p", "boundaryField.inlet.value", - openfoam.uniform(pressureBF.inlet.value) + openfoam.uniform(pressureBF["inlet"]["value"]) ) openfoam.foamDictionary( "0/p", "boundaryField.outlet.value", - openfoam.uniform(pressureBF.outlet.value) + openfoam.uniform(pressureBF["outlet"]["value"]) ) - + # TODO: flow variable openfoam.foamDictionary( "0/U", "boundaryField.inlet.value", @@ -710,325 +476,3 @@ class Anisotropy(object): pass -################################################################################### - - -### -# Main -## -def main(): - if checkEnv(): - return - - logger.info(f"args:\n\tconfig:\t{ configPath }\n\tmode:\t{ mode }") - - queue = createQueue() - - for n, case in enumerate(queue): - date = datetime.now() - logger.info("-" * 80) - logger.info(f"""main: - task:\t{ n + 1 } / { len(queue) } - cpu count:\t{ os.cpu_count() } - case:\t{ case } - date:\t{ date.date() } - time:\t{ date.time() }""") - - ### - # Compute mesh - ## - taskPath = os.path.join(case, "task.toml") - - task = struct(toml.load(taskPath)) - - if not task.status.mesh or mode == "all": - computeMesh(case) - - else: - logger.info("computeMesh: mesh already computed") - - task = struct(toml.load(taskPath)) - - if not task.status.mesh: - logger.critical("mesh not computed: skip flow computation") - continue - - ### - # Compute flow - ## - - if not task.status.flow or mode == "all": - computeFlow(case) - - else: - logger.info("computeFlow: flow already computed") - - -def createQueue(): - queue = [] - - ### - # Special values - ## - parameters_theta = {} - mesh_thickness = {} - - for structure in config.base.__dict__.keys(): - - theta = getattr(config, structure).geometry.theta - parameters_theta[structure] = [ n * theta[2] for n in range(int(theta[0] / theta[2]), int(theta[1] / theta[2]) + 1) ] - - thickness = getattr(config, structure).mesh.thickness - count = len(parameters_theta[structure]) - mesh_thickness[structure] = [ thickness[0] + n * (thickness[1] - thickness[0]) / (count - 1) for n in range(0, count) ] - - - ### - # structure type > flow direction > coefficient theta - ## - for structure in config.base.__dict__.keys(): - if getattr(config.base, structure): - for direction in getattr(config, structure).geometry.directions: - for n, theta in enumerate(parameters_theta[structure]): - # create dirs for case path - case = os.path.join( - f"{ BUILD }", - f"{ structure }", - "direction-{}{}{}".format(*direction), - f"theta-{ theta }" - ) - - taskPath = os.path.join(case, "task.toml") - - if os.path.exists(taskPath) and mode == "safe": - queue.append(case) - continue - - if not os.path.exists(case): - os.makedirs(case) - - # prepare configuration for task - task = { - "logger": dict(config.logger), - "structure": structure, - "status": { - "mesh": False, - "flow": False - }, - "statistics": { - "meshTime": 0, - "flowTime": 0 - }, - "geometry": { - "theta": theta, - "direction": direction, - "fillet": getattr(config, structure).geometry.fillet - }, - "mesh": dict(getattr(config, structure).mesh), - "flow": dict(config.flow) - } - - # reassign special values - task["mesh"]["thickness"] = mesh_thickness[structure][n] - - ## - with open(os.path.join(case, "task.toml"), "w") as io: - toml.dump(task, io) - - ## - queue.append(case) - - return queue - - -#from salomepl.utils import runExecute, salomeVersion - -def computeMesh(case): - scriptpath = os.path.join(ROOT, "salomepl/genmesh.py") - port = 2810 - stime = time.monotonic() - - returncode = runExecute(port, scriptpath, ROOT, case) - - task = struct(toml.load(os.path.join(case, "task.toml"))) - elapsed = time.monotonic() - stime - logger.info("computeMesh: elapsed time: {}".format(timedelta(seconds = elapsed))) - - if returncode == 0: - task.statistics.meshTime = elapsed - - with open(os.path.join(case, "task.toml"), "w") as io: - toml.dump(dict(task), io) - - - - -def computeFlow(case): - ### - # Case preparation - ## - foamCase = [ "0", "constant", "system" ] - - os.chdir(case) - task = struct(toml.load(os.path.join(case, "task.toml"))) - openfoam.foamClean() - - for d in foamCase: - shutil.copytree( - os.path.join(ROOT, "openfoam/template", d), - os.path.join(case, d) - ) - - stime = time.monotonic() - - ### - # Mesh manipulations - ## - if not os.path.exists("mesh.unv"): - logger.critical(f"computeFlow: missed 'mesh.unv'") - return - - _, returncode = openfoam.ideasUnvToFoam("mesh.unv") - - if returncode: - os.chdir(ROOT) - - return returncode - - openfoam.createPatch(dictfile = "system/createPatchDict") - - openfoam.foamDictionary( - "constant/polyMesh/boundary", - "entry0.defaultFaces.type", - "wall" - ) - openfoam.foamDictionary( - "constant/polyMesh/boundary", - "entry0.defaultFaces.inGroups", - "1 (wall)" - ) - - out = openfoam.checkMesh() - - if out: - logger.info(out) - - openfoam.transformPoints(task.flow.scale) - - ### - # Decomposition and initial approximation - ## - openfoam.foamDictionary( - "constant/transportProperties", - "nu", - str(task.flow.constant.nu) - ) - - openfoam.decomposePar() - - openfoam.renumberMesh() - - pressureBF = task.flow.approx.pressure.boundaryField - velocityBF = task.flow.approx.velocity.boundaryField - direction = { - "[1, 0, 0]": 0, - "[0, 0, 1]": 1, - "[1, 1, 1]": 2 - }[str(task.geometry.direction)] - - openfoam.foamDictionary( - "0/p", - "boundaryField.inlet.value", - openfoam.uniform(pressureBF.inlet.value) - ) - openfoam.foamDictionary( - "0/p", - "boundaryField.outlet.value", - openfoam.uniform(pressureBF.outlet.value) - ) - - openfoam.foamDictionary( - "0/U", - "boundaryField.inlet.value", - openfoam.uniform(velocityBF.inlet.value[direction]) - ) - - openfoam.potentialFoam() - - ### - # Main computation - ## - pressureBF = task.flow.main.pressure.boundaryField - velocityBF = task.flow.main.velocity.boundaryField - - for n in range(os.cpu_count()): - openfoam.foamDictionary( - f"processor{n}/0/U", - "boundaryField.inlet.type", - velocityBF.inlet.type - ) - openfoam.foamDictionary( - f"processor{n}/0/U", - "boundaryField.inlet.value", - openfoam.uniform(velocityBF.inlet.value[direction]) - ) - - returncode, out = openfoam.simpleFoam() - if out: - logger.info(out) - - ### - # Check results - ## - elapsed = time.monotonic() - stime - logger.info("computeFlow: elapsed time: {}".format(timedelta(seconds = elapsed))) - - if returncode == 0: - task.status.flow = True - task.statistics.flowTime = elapsed - - postProcessing = "postProcessing/flowRatePatch(name=outlet)/0/surfaceFieldValue.dat" - - with open(postProcessing, "r") as io: - lastLine = io.readlines()[-1] - flowRate = float(lastLine.replace(" ", "").replace("\n", "").split("\t")[1]) - - task.statistics.flowRate = flowRate - - with open(os.path.join(case, "task.toml"), "w") as io: - toml.dump(dict(task), io) - - os.chdir(ROOT) - - return returncode - - -def checkEnv(): - missed = False - - try: - pythonVersion = "Python {}".format(sys.version.split(" ")[0]) - salomeplVersion = salomeVersion() - openfoamVersion = openfoam.foamVersion() - - except Exception as e: - logger.critical("Missed environment %s", e) - missed = True - - else: - logger.info(f"environment:\n\t{pythonVersion}\n\t{salomeplVersion}\n\t{openfoamVersion}") - - finally: - return missed - - -def postprocessing(queue): - - pass - -### -# Main entry -## -if __name__ == "__main__": - main() - diff --git a/anisotropy/core/models.py b/anisotropy/core/models.py index ed3a588..b5a6d56 100644 --- a/anisotropy/core/models.py +++ b/anisotropy/core/models.py @@ -1,5 +1,9 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from peewee import ( - SqliteDatabase, + SqliteDatabase, JOIN, Model, Field, AutoField, ForeignKeyField, TextField, FloatField, @@ -7,6 +11,18 @@ from peewee import ( TimeField ) +db = SqliteDatabase( + None, + pragmas = { "foreign_keys": 1 }, + field_types = { "list": "text" } +) + + +class BaseModel(Model): + class Meta: + database = db + + class ListField(Field): field_type = "list" @@ -25,16 +41,6 @@ class ListField(Field): return pval -db = SqliteDatabase( - None, - pragmas = { "foreign_keys": 1 }, - field_types = { "list": "text" } -) - -class BaseModel(Model): - class Meta: - database = db - class Structure(BaseModel): structure_id = AutoField() diff --git a/anisotropy/core/utils.py b/anisotropy/core/utils.py index b80dfd3..b9ef4aa 100644 --- a/anisotropy/core/utils.py +++ b/anisotropy/core/utils.py @@ -8,42 +8,61 @@ from types import FunctionType import os class CustomFormatter(logging.Formatter): - grey = "\x1b[38;21m" - yellow = "\x1b[33;21m" - red = "\x1b[31;21m" - bold_red = "\x1b[31;1m" - reset = "\x1b[0m" - format = "[ %(asctime)s ] [ %(levelname)s ] %(message)s" + def _getFormat(self, level: int): + grey = "\x1b[38;21m" + yellow = "\x1b[33;21m" + red = "\x1b[31;21m" + bold_red = "\x1b[31;1m" + reset = "\x1b[0m" + format = "[ %(asctime)s ] [ %(levelname)s ] %(message)s" - FORMATS = { - logging.DEBUG: grey + format + reset, - logging.INFO: grey + format + reset, - logging.WARNING: yellow + format + reset, - logging.ERROR: red + format + reset, - logging.CRITICAL: bold_red + format + reset - } + formats = { + logging.DEBUG: grey + format + reset, + logging.INFO: grey + format + reset, + logging.WARNING: yellow + format + reset, + logging.ERROR: red + format + reset, + logging.CRITICAL: bold_red + format + reset + } + + return formats.get(level) def format(self, record): - log_fmt = self.FORMATS.get(record.levelno) - formatter = logging.Formatter(log_fmt) + log_fmt = self._getFormat(record.levelno) + time_fmt = "%H:%M:%S %d-%m-%y" + formatter = logging.Formatter(log_fmt, time_fmt) return formatter.format(record) -def setupLogger(logger, level: int): + +def setupLogger(logger, level: int, filepath: str = None): + """Applies settings to logger + + :param logger: Instance of :class:`logging.Logger` + :type logger: Instance of :class:`logging.Logger` + :param level: Logging level (logging.INFO, logging.WARNING, ..) + :type level: int + :param filepath: Path to directory + :type filepath: str, optional + """ + logger.setLevel(level) - sh = logging.StreamHandler() - sh.setLevel(level) - sh.setFormatter(CustomFormatter()) + streamhandler = logging.StreamHandler() + streamhandler.setLevel(level) + streamhandler.setFormatter(CustomFormatter()) + logger.addHandler(streamhandler) - fh = logging.FileHandler(os.path.join("logs", logger.name)) - fh.setLevel(level) - fh.setFormatter(CustomFormatter()) + if filepath: + if not os.path.exists(filepath): + os.makedirs(filepath, exist_ok = True) - logger.addHandler(sh) - logger.addHandler(fh) + filehandler = logging.FileHandler( + os.path.join(filepath, "{}.log".format(logger.name)) + ) + filehandler.setLevel(level) + filehandler.setFormatter(CustomFormatter()) + logger.addHandler(filehandler) - return logger class struct: def __init__(self, *args, **kwargs): @@ -104,17 +123,32 @@ def deepupdate(target, src): else: target[k] = copy.copy(v) +#if os.path.exists(env["CONFIG"]): +# config = toml.load(env["CONFIG"]) +# for restricted in ["ROOT", "BUILD", "LOG", "CONFIG"]: +# if config.get(restricted): +# config.pop(restricted) + + # TODO: not working if custom config empty and etc +# for m, structure in enumerate(config["structures"]): +# for n, estructure in enumerate(env["structures"]): +# if estructure["name"] == structure["name"]: +# deepupdate(env["structures"][n], config["structures"][m]) + +# config.pop("structures") +# deepupdate(env, config) def timer(func: FunctionType) -> (tuple, float): """(Decorator) Returns output of inner function and execution time :param func: inner function - :type: FunctionType + :type func: FunctionType :return: output, elapsed time :rtype: tuple(tuple, float) """ + def inner(*args, **kwargs): start = time.monotonic() ret = func(*args, **kwargs) diff --git a/anisotropy/salomepl/utils.py b/anisotropy/salomepl/utils.py index a2499cb..6425341 100644 --- a/anisotropy/salomepl/utils.py +++ b/anisotropy/salomepl/utils.py @@ -28,8 +28,8 @@ def version() -> str: return str(out, "utf-8").strip().split(" ")[-1] - -def runSalome(port: int, scriptpath: str, root: str, logpath: str = None, *args) -> int: +def runSalome(port: int, scriptpath: str, root: str, *args, logpath: str = None) -> int: + # ISSUE: salome removes commas from string list if os.environ.get("SALOME_PATH"): cmd = [ os.path.join(os.environ["SALOME_PATH"], "salome") ] @@ -40,9 +40,10 @@ def runSalome(port: int, scriptpath: str, root: str, logpath: str = None, *args) if not logpath: logpath = "/tmp/salome.log" - fullargs = list(args) - fullargs.extend([ root, logpath ]) - fmtargs = "args:{}".format(", ".join([ str(arg) for arg in args ])) + #fullargs = list(args) + args = list(args) + args.insert(1, root) + fmtargs = "args:{}".format(",".join([ '"{}"'.format(str(arg)) for arg in args ])) cmdargs = [ "start", "-t", "--shutdown-servers=1", diff --git a/tests/anisotropy-cli.py b/tests/anisotropy-cli.py new file mode 100644 index 0000000..2f848fa --- /dev/null +++ b/tests/anisotropy-cli.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import os, sys +sys.path.append(os.path.join(os.path.dirname(__file__), "..")) + +from anisotropy.core.cli import anisotropy + +anisotropy() + + diff --git a/tests/test_anisotropy.py b/tests/test_anisotropy.py index 784e56e..2f769a7 100644 --- a/tests/test_anisotropy.py +++ b/tests/test_anisotropy.py @@ -9,7 +9,7 @@ class TestAnisotropy(unittest.TestCase): self.model = Anisotropy() def test_01_create_db(self): - self.model.setupDB() + self.model.db.setup() path = os.path.join(self.model.env["db_path"], "anisotropy.db") self.assertTrue(os.path.exists(path)) @@ -19,10 +19,9 @@ class TestAnisotropy(unittest.TestCase): try: paramsAll = self.model.loadFromScratch() - self.model.setupDB() for entry in paramsAll: - self.model.updateDB(entry) + self.model.update(entry) except Exception as e: passed = False @@ -31,8 +30,7 @@ class TestAnisotropy(unittest.TestCase): self.assertTrue(passed) def test_03_load_db(self): - self.model.setupDB() - self.model.loadDB("simple", [1.0, 0.0, 0.0], 0.01) + self.model.load("simple", [1.0, 0.0, 0.0], 0.01) self.assertEqual(self.model.params["structure"]["type"], "simple")