diff --git a/anisotropy/__init__.py b/anisotropy/__init__.py index 576aca2..fb77c9e 100644 --- a/anisotropy/__init__.py +++ b/anisotropy/__init__.py @@ -7,7 +7,7 @@ __license__ = "GPL3" __version__ = "1.1.0" -__author__ = __maintainer = "George Kusayko" +__author__ = __maintainer__ = "George Kusayko" __email__ = "gkusayko@gmail.com" ### diff --git a/anisotropy/config/default.toml b/anisotropy/config/default.toml index 169e8c7..de06e31 100644 --- a/anisotropy/config/default.toml +++ b/anisotropy/config/default.toml @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + [logger] name = "anisotropy" format = "[ %(levelname)s ] %(message)s" diff --git a/anisotropy/core/__init__.py b/anisotropy/core/__init__.py index e69de29..e888062 100644 --- a/anisotropy/core/__init__.py +++ b/anisotropy/core/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + + diff --git a/anisotropy/core/cli.py b/anisotropy/core/cli.py index ba4a04d..87a8757 100644 --- a/anisotropy/core/cli.py +++ b/anisotropy/core/cli.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import click import ast @@ -41,7 +45,6 @@ class KeyValueOption(click.Option): return self._convert(ctx, value) -#pass_anisotropy = click.make_pass_decorator(Anisotropy) def version(): msg = "Missed package anisotropy" @@ -59,11 +62,34 @@ def version(): def anisotropy(): pass -@anisotropy.command() -@click.option("-s", "--stage", "stage", type = click.Choice(["all", "mesh", "flow"]), default = "all") -@click.option("-p", "--param", "params", metavar = "key=value", multiple = True, cls = KeyValueOption) -def compute(stage, params): +@anisotropy.command( + help = """Computes cases by chain (mesh -> flow) + + Control parameters: type, direction, theta (each parameter affects on a queue) + """ +) +@click.option( + "-s", "--stage", "stage", + type = click.Choice(["all", "mesh", "flow"]), + default = "all", + help = "Current computation stage" +) +@click.option( + "-P", "--parallel", "parallel", + type = click.INT, + default = 1, + help = "Count of parallel processes" +) +@click.option( + "-p", "--param", "params", + metavar = "key=value", + multiple = True, + cls = KeyValueOption, + help = "Overwrite existing parameter (except control variables)" +) +def compute(stage, parallel, params): from anisotropy.core.main import Anisotropy, logger + from anisotropy.core.utils import timer args = dict() @@ -81,7 +107,8 @@ def compute(stage, params): model.db.update(entry) type, direction, theta = args["type"], args["direction"], args["theta"] - + + model.load(type, direction, theta) # TODO: merge cli params with db params here model.evalParams() @@ -90,17 +117,21 @@ def compute(stage, params): # TODO: single compute / queue if stage == "all" or stage == "mesh": - ((out, err, code), elapsed) = model.computeMesh(type, direction, theta) + ((out, err, code), elapsed) = timer(model.computeMesh)() if out: click.echo(out) if err: click.echo(err) + if model.params.get("meshresult"): 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) + ((out, err, code), elapsed) = timer(model.computeFlow)() + + if out: click.echo(out) + if err: click.echo(err) if model.params.get("flowresult"): model.load(type, direction, theta) @@ -108,13 +139,15 @@ def compute(stage, params): model.update() -@anisotropy.command() +@anisotropy.command( + help = "! Not a user command" +) @click.argument("root") @click.argument("type") @click.argument("direction") @click.argument("theta") def computemesh(root, type, direction, theta): - # ISSUE: can't hide command from help, hidden = True doesn't work + # ISSUE: can't hide command from help, 'hidden = True' doesn't work # [Salome Environment] ### diff --git a/anisotropy/core/database.py b/anisotropy/core/database.py index 4df95d4..86c2265 100644 --- a/anisotropy/core/database.py +++ b/anisotropy/core/database.py @@ -45,6 +45,11 @@ class Database(object): def isempty(self) -> bool: + """Checks DB for main table existence (Structure) + + :return: True if exists + :rtype: bool + """ query = Structure.select() return not query.exists() @@ -96,6 +101,8 @@ class Database(object): if flowresultsQuery.exists(): params["flowresult"] = flowresultsQuery.dicts().get() + else: + logger.error("Missed Structure table") return params diff --git a/anisotropy/core/main.py b/anisotropy/core/main.py index 5965c6b..f21a0df 100644 --- a/anisotropy/core/main.py +++ b/anisotropy/core/main.py @@ -32,7 +32,7 @@ peeweeLogger.setLevel(logging.INFO) class Anisotropy(object): - """Ultimate class that organize whole working process""" + """Ultimate class that organizes whole working process""" def __init__(self): """Constructor method""" @@ -43,10 +43,18 @@ class Anisotropy(object): def load(self, structure_type: str, structure_direction: list, structure_theta: float): + """Shortcut for `Database.setup` and `Database.load`. + + See :class:`anisotropy.core.database.Database` for more details. + """ self.db.setup() self.params = self.db.load(structure_type, structure_direction, structure_theta) def update(self, params: dict = None): + """Shortcut for `Database.setup` and `Database.update`. + + See :class:`anisotropy.core.database.Database` for more details. + """ self.db.setup() self.db.update(self.params if not params else params) @@ -124,7 +132,7 @@ class Anisotropy(object): "flowapprox": deepcopy(entry["flowapprox"]) } - # For type = fixedValue only + # For `type = fixedValue` only _velocity = entryNew["flowapprox"]["velocity"]["boundaryField"]["inlet"]["value"] entryNew["flowapprox"]["velocity"]["boundaryField"]["inlet"]["value"] = [ val * _velocity for val in entryNew["structure"]["direction"] @@ -190,7 +198,6 @@ class Anisotropy(object): fillets = delta - Cf * (radius - r0) self.params["structure"].update( - #**structure, L = L, r0 = r0, radius = radius, @@ -198,7 +205,7 @@ class Anisotropy(object): ) def getCasePath(self) -> str: - """Constructs case path from main control parameters + """Constructs case path from control parameters :return: Absolute path to case :rtype: str @@ -217,20 +224,31 @@ class Anisotropy(object): ) - @timer - def computeMesh(self, type, direction, theta): + def computeMesh(self): + """Computes a mesh on shape via Salome + + :return: Process output, error messages and returncode + :rtype: tuple(str, str, int) + """ scriptpath = os.path.join(self.env["ROOT"], "anisotropy/core/cli.py") port = 2900 + p = self.params["structure"] + return salomepl.utils.runSalome( self.env["salome_port"], scriptpath, self.env["ROOT"], - "computemesh", type, direction, theta, + "computemesh", + p["type"], p["direction"], p["theta"], logpath = os.path.join(self.env["LOG"], "salome.log") ) def genmesh(self): + """Computes a mesh on shape + + Warning: Working only inside Salome Environment + """ # ISSUE: double logger output import salome @@ -309,7 +327,12 @@ class Anisotropy(object): self.update() - returncode, errors = mesh.compute() + out, err, returncode = mesh.compute() + + ### + # Results + ## + p["meshresult"] = dict() if not returncode: mesh.removePyramids() @@ -320,7 +343,8 @@ class Anisotropy(object): mesh.exportUNV(os.path.join(casePath, "mesh.unv")) meshStats = mesh.stats() - p["meshresult"] = dict( + p["meshresult"].update( + status = "Done", surfaceArea = surfaceArea, volume = volume, **meshStats @@ -334,7 +358,8 @@ class Anisotropy(object): else: logger.error(errors) - p["meshresult"] = dict( + p["meshresult"].update( + status = "Failed", surfaceArea = surfaceArea, volume = volume ) @@ -342,8 +367,13 @@ class Anisotropy(object): salome.salome_close() - @timer - def computeFlow(self, type, direction, theta): + + def computeFlow(self): + """Computes a flow on mesh via OpenFOAM + + :return: Process output, error messages and returncode + :rtype: tuple(str, str, int) + """ ### # Case preparation ## @@ -468,20 +498,29 @@ class Anisotropy(object): logger.info(out) ### - # Check results + # Results ## + self.params["flowresult"] = dict() - if returncode == 0: + if not returncode: postProcessing = "postProcessing/flowRatePatch(name=outlet)/0/surfaceFieldValue.dat" with open(os.path.join(casePath, postProcessing), "r") as io: lastLine = io.readlines()[-1] flowRate = float(lastLine.replace(" ", "").replace("\n", "").split("\t")[1]) - self.params["flowresult"] = dict( + self.params["flowresult"].update( + status = "Done", flowRate = flowRate ) + else: + self.params["flowresult"].update( + status = "Failed", + flowRate = flowRate + ) + + self.update() os.chdir(self.env["ROOT"]) @@ -489,10 +528,3 @@ class Anisotropy(object): return out, err, returncode - def _queue(self): - pass - - def computeAll(self): - pass - - diff --git a/anisotropy/core/models.py b/anisotropy/core/models.py index 070b1fe..b07dd5c 100644 --- a/anisotropy/core/models.py +++ b/anisotropy/core/models.py @@ -139,6 +139,7 @@ class MeshResult(BaseModel): prisms = IntegerField(null = True) pyramids = IntegerField(null = True) + status = TextField(null = True, default = "Idle") calculationTime = TimeField(null = True) class Flow(BaseModel): @@ -164,4 +165,6 @@ class FlowResult(BaseModel): flow_id = ForeignKeyField(Flow, backref = "flowresults") flowRate = FloatField(null = True) + + status = TextField(null = True, default = "Idle") calculationTime = TimeField(null = True) diff --git a/anisotropy/core/utils.py b/anisotropy/core/utils.py index 6e37708..c90bc8f 100644 --- a/anisotropy/core/utils.py +++ b/anisotropy/core/utils.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import logging from multiprocessing import Queue, Process, cpu_count diff --git a/anisotropy/openfoam/__init__.py b/anisotropy/openfoam/__init__.py index b617543..cd501a6 100644 --- a/anisotropy/openfoam/__init__.py +++ b/anisotropy/openfoam/__init__.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from .meshConversion import ideasUnvToFoam from .meshManipulation import createPatch, transformPoints, checkMesh, renumberMesh diff --git a/anisotropy/openfoam/application.py b/anisotropy/openfoam/application.py index 2a033f0..fa48e15 100644 --- a/anisotropy/openfoam/application.py +++ b/anisotropy/openfoam/application.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import os, sys import subprocess diff --git a/anisotropy/openfoam/meshConversion.py b/anisotropy/openfoam/meshConversion.py index 7e0666a..37ab55f 100644 --- a/anisotropy/openfoam/meshConversion.py +++ b/anisotropy/openfoam/meshConversion.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from .application import application def ideasUnvToFoam(mesh: str, case: str = None) -> (str, int): diff --git a/anisotropy/openfoam/meshManipulation.py b/anisotropy/openfoam/meshManipulation.py index 1b3c173..f01aadc 100644 --- a/anisotropy/openfoam/meshManipulation.py +++ b/anisotropy/openfoam/meshManipulation.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from .application import application import re diff --git a/anisotropy/openfoam/miscellaneous.py b/anisotropy/openfoam/miscellaneous.py index 35ab7c0..c113225 100644 --- a/anisotropy/openfoam/miscellaneous.py +++ b/anisotropy/openfoam/miscellaneous.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from .application import application def foamDictionary(filepath: str, entry: str, value: str = None, case: str = None): diff --git a/anisotropy/openfoam/parallelProcessing.py b/anisotropy/openfoam/parallelProcessing.py index e5e927c..5f0db32 100644 --- a/anisotropy/openfoam/parallelProcessing.py +++ b/anisotropy/openfoam/parallelProcessing.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from .application import application def decomposePar(case: str = None): diff --git a/anisotropy/openfoam/solvers.py b/anisotropy/openfoam/solvers.py index 04e3faa..6109b80 100644 --- a/anisotropy/openfoam/solvers.py +++ b/anisotropy/openfoam/solvers.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from .application import application import re diff --git a/anisotropy/openfoam/utils.py b/anisotropy/openfoam/utils.py index 75e94b4..6b28d6c 100644 --- a/anisotropy/openfoam/utils.py +++ b/anisotropy/openfoam/utils.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import os import shutil diff --git a/anisotropy/salomepl/__init__.py b/anisotropy/salomepl/__init__.py index 586a5fd..e888062 100644 --- a/anisotropy/salomepl/__init__.py +++ b/anisotropy/salomepl/__init__.py @@ -1,4 +1,5 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. diff --git a/anisotropy/salomepl/geometry.py b/anisotropy/salomepl/geometry.py index c10dbb5..4c14a01 100644 --- a/anisotropy/salomepl/geometry.py +++ b/anisotropy/salomepl/geometry.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import logging logger = logging.getLogger("anisotropy") diff --git a/anisotropy/salomepl/mesh.py b/anisotropy/salomepl/mesh.py index 7bbe252..ad1331d 100644 --- a/anisotropy/salomepl/mesh.py +++ b/anisotropy/salomepl/mesh.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + import logging logger = logging.getLogger("anisotropy") @@ -113,9 +117,9 @@ class Mesh(object): def compute(self): isDone = self.mesh.Compute() returncode = int(not isDone) - errors = self.mesh.GetComputeErrors() + err = self.mesh.GetComputeErrors() - return returncode, errors + return "", err, returncode def stats(self): return { diff --git a/anisotropy/salomepl/utils.py b/anisotropy/salomepl/utils.py index 6425341..666bbbf 100644 --- a/anisotropy/salomepl/utils.py +++ b/anisotropy/salomepl/utils.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + #import salome import subprocess import logging diff --git a/anisotropy/samples/__init__.py b/anisotropy/samples/__init__.py index b522cae..3859687 100644 --- a/anisotropy/samples/__init__.py +++ b/anisotropy/samples/__init__.py @@ -1,5 +1,6 @@ -#!/usr/bin/env python # -*- 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 diff --git a/anisotropy/samples/bodyCentered.py b/anisotropy/samples/bodyCentered.py index e70cc11..83bd052 100644 --- a/anisotropy/samples/bodyCentered.py +++ b/anisotropy/samples/bodyCentered.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from math import pi, sqrt from anisotropy.salomepl import geometry diff --git a/anisotropy/samples/faceCentered.py b/anisotropy/samples/faceCentered.py index 222537f..cb1bb02 100644 --- a/anisotropy/samples/faceCentered.py +++ b/anisotropy/samples/faceCentered.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from math import pi, sqrt from anisotropy.salomepl import geometry diff --git a/anisotropy/samples/simple.py b/anisotropy/samples/simple.py index d72d3e1..4779301 100644 --- a/anisotropy/samples/simple.py +++ b/anisotropy/samples/simple.py @@ -1,3 +1,7 @@ +# -*- coding: utf-8 -*- +# This file is part of anisotropy. +# License: GNU GPL version 3, see the file "LICENSE" for details. + from math import pi, sqrt from anisotropy.salomepl import geometry diff --git a/docs/source/anisotropy.core.rst b/docs/source/anisotropy.core.rst index d5fc98e..5fabf1a 100644 --- a/docs/source/anisotropy.core.rst +++ b/docs/source/anisotropy.core.rst @@ -12,6 +12,14 @@ anisotropy.core.cli module :undoc-members: :show-inheritance: +anisotropy.core.database module +------------------------------- + +.. automodule:: anisotropy.core.database + :members: + :undoc-members: + :show-inheritance: + anisotropy.core.main module --------------------------- diff --git a/docs/source/modules.rst b/docs/source/modules.rst index bfbe537..923dea1 100644 --- a/docs/source/modules.rst +++ b/docs/source/modules.rst @@ -6,3 +6,4 @@ anisotropy anisotropy setup + tests diff --git a/docs/source/tests.rst b/docs/source/tests.rst new file mode 100644 index 0000000..965eea2 --- /dev/null +++ b/docs/source/tests.rst @@ -0,0 +1,29 @@ +tests package +============= + +Submodules +---------- + +tests.anisotropy\-cli module +---------------------------- + +.. automodule:: tests.anisotropy-cli + :members: + :undoc-members: + :show-inheritance: + +tests.test\_anisotropy module +----------------------------- + +.. automodule:: tests.test_anisotropy + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: tests + :members: + :undoc-members: + :show-inheritance: