Skip to content

Commit

Permalink
Merge pull request #14 from exabl/enh/snake-nek
Browse files Browse the repository at this point in the history
More Nek5000 params
  • Loading branch information
ashwinvis committed Dec 27, 2020
2 parents 5b42be7 + b62e6bf commit 5ff43c1
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 34 deletions.
1 change: 1 addition & 0 deletions .git_archival.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref-names: :%D$
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.git_archival.txt export-subst
13 changes: 10 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ jobs:
uses: actions/checkout@v2
with:
submodules: recursive
fetch-depth: 0

- name: Install apt packages
run: |
Expand All @@ -44,16 +45,22 @@ jobs:
restore-keys: |
${{ runner.os }}-pip-
- name: Verify Python environment
run: |
pip list
pip cache list
- name: Install dependencies
run: |
python -m pip install --upgrade pip wheel
pip install .[tests]
- name: Run tests
run: |
source activate.sh
pytest -s
pytest -s --cov-report=xml --cov-report=term-missing
- name: Upload coverage to codecov
if: ${{ success() }}
run: bash <(curl -s https://codecov.io/bash)
uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
# snek5000
<div align="center">
<h1>snek5000</h1>

[![PyPI](https://img.shields.io/pypi/v/snek5000)](https://pypi.org/project/snek5000/)
[![Tests Status](https://github.com/exabl/snek5000/workflows/Tests/badge.svg)](https://github.com/exabl/snek5000/actions?workflow=Tests)
[![Documentation Status](https://readthedocs.org/projects/snek5000/badge/?version=latest)](https://snek5000.readthedocs.io/en/latest/?badge=latest)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=exabl_snek5000&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=exabl_snek5000)
[![Code coverage](https://codecov.io/gh/exabl/snek5000/branch/master/graph/badge.svg?token=WzGnN0dfbw)](https://codecov.io/gh/exabl/snek5000)

Python framework for Nek5000.
Python framework for Nek5000
</div>

**Warning:** This framework is experimental and of beta-quality. The API can also change.
**Warning:** This framework is experimental and of beta-quality.

**Documentation**: https://snek5000.readthedocs.io/

Expand Down Expand Up @@ -49,6 +52,13 @@ Check out the
[tutorials](https://snek5000.readthedocs.io/en/latest/tutorials.html) to learn
how to use `snek5000`.

<details>
<summary>
<b>
Need more reasons to use snek5000?
</b>
</summary>

#### Advantages

- Saves you from the trouble in setting up multiple source files (`.box`, `.par`, `SIZE`)
Expand Down Expand Up @@ -95,6 +105,8 @@ how to use `snek5000`.
templates](https://jinja.palletsprojects.com/en/2.11.x/templates/) (which are
not so hard, btw)

</details>

## Contributing

Contributions are welcome! You can help by testing out the code, filing issues
Expand Down
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[build-system]
requires = ["setuptools>=42.0.0", "wheel", "setuptools_scm[toml]>=3.4.2"]

requires = ["setuptools>=49.5.0", "wheel", "setuptools_scm[toml]>=3.4.2", "setuptools_scm_git_archive"]
build-backend = "setuptools.build_meta"

[tool.setuptools_scm]
write_to = "src/snek5000/_version.py"
write_to_template = "__version__ = \"{version}\"\n"

[tool.pytest.ini_options]
addopts = "--cov=src --cov=tests"
addopts = "--cov=snek5000 --cov=tests"
8 changes: 6 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@ packages=find:
install_requires =
snakemake != 5.23.*, != 5.24.*
fluidsim >= 0.3.3
entrypoints
jinja2
inflection
importlib_resources; python_version < "3.7"
setup_requires =
setuptools_scm

[options.entry_points]
snek5000.solvers =
nek = snek5000.solvers.base
kth = snek5000.solvers.kth

[options.extras_require]
docs =
Expand Down
34 changes: 34 additions & 0 deletions src/snek5000/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import importlib_resources as resources

import os
from pathlib import Path

from fluiddyn.util import mpi # noqa: F401

Expand Down Expand Up @@ -66,3 +67,36 @@ def get_asset(asset_name):
"""Fetches path of an asset from subpackage ``snek5000.assets``."""
asset = next(resources.path("snek5000.assets", asset_name).gen)
return asset


def load_simul(path_dir):
path_dir = Path(path_dir)

from warnings import warn

info_solver_xml = path_dir / "info_solver.xml"
if info_solver_xml.exists():
from snek5000.info import InfoSolverNek

info_solver = InfoSolverNek(path_file=info_solver_xml)
solver = info_solver.short_name
else:
warn(f"The {info_solver_xml} file is missing!")
solver = path_dir.name.split("_")[0]

# Load simulation class
from snek5000.solvers import import_solver

Simul = import_solver(solver)

# Load parameters
params_xml = path_dir / "params.xml"
try:
params_par = next(path_dir.glob("*.par"))
except StopIteration:
warn(f"The {path_dir}/*.par file is missing!")
params_par = None

params = Simul.load_params_from_file(path_xml=params_xml, path_par=params_par)
sim = Simul(params, existing_path_run=path_dir)
return sim
8 changes: 6 additions & 2 deletions src/snek5000/make.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ def list(self):
with change_dir(self.path_run):
return snakemake(self.file, listrules=True, log_handler=self.log_handler)

def exec(self, rules=("run",), dryrun=False):
def exec(self, rules=("run",), dryrun=False, **kwargs):
"""Execute snakemake rules in sequence.
:returns: True if workflow execution was successful.
"""
with change_dir(self.path_run):
return snakemake(
self.file, targets=rules, dryrun=dryrun, log_handler=self.log_handler
self.file,
targets=rules,
dryrun=dryrun,
log_handler=self.log_handler,
**kwargs
)
19 changes: 19 additions & 0 deletions src/snek5000/solvers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,22 @@
kth
"""


def available_solvers():
"""Returns a dictionary of all registered solvers registered as an
entrypoint_.
_entrypoint: https://packaging.python.org/guides/creating-and-discovering-plugins/#using-package-metadata
"""
import entrypoints

return entrypoints.get_group_named("snek5000.solvers")


def import_solver(name):
"""Import the Simul class of a solver."""
solvers = available_solvers()
module = solvers[name].load()
return module.Simul
55 changes: 42 additions & 13 deletions src/snek5000/solvers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,19 @@
class SimulNek(SimulBase):
"""Simulation class
.. code-block:: python
from snek5000.solvers.base import Simul
params = Simul.create_default_params()
sim = Simul(params)
Parameters
----------
params: Parameters
Input parameters for the simulation run.
write_files: bool, optional
Write simulation files, including usr, box, SIZE files. By default
all files are written.
Example
-------
>>> from snek5000.solvers.base import Simul
>>> params = Simul.create_default_params()
>>> sim = Simul(params)
"""

Expand Down Expand Up @@ -115,6 +123,7 @@ def _complete_params_with_default(params):
write_control="timeStep",
write_interval=10,
filtering=None,
filter_modes=2,
filter_cutoff_ratio=0.65,
filter_weight=12.0,
write_double_precision=True,
Expand All @@ -134,17 +143,32 @@ def _complete_params_with_default(params):
stress_formulation=False,
)
)
common = dict(residual_tol=math.nan, residual_proj=False)
common = dict(
residual_tol=math.nan, residual_proj=False, write_to_field_file=True
)
params_nek.velocity._set_attribs(common)
params_nek.pressure._set_attribs(common)
params_nek.temperature._set_attribs(common)
params_nek.scalar01._set_attribs(common)

common_ts = dict(solver="helm", advection=True, absolute_tol=math.nan)
params_nek.temperature._set_attribs(common_ts)
params_nek.scalar01._set_attribs(common_ts)

params_nek.mesh._set_attrib("write_to_field_file", True)
params_nek.velocity._set_attribs(dict(viscosity=math.nan, density=math.nan))
params_nek.pressure._set_attrib("preconditioner", "semg_xxt")
params_nek.temperature._set_attribs(
dict(
conjugate_heat_transfer=False,
conductivity=math.nan,
rho_cp=math.nan,
)
)
params_nek.scalar01._set_attribs(dict(density=math.nan, diffusivity=math.nan))
return params

def __init__(self, params):
def __init__(self, params, existing_path_run=None):
np.seterr(all="warn")
np.seterr(under="ignore")

Expand Down Expand Up @@ -177,10 +201,14 @@ def __init__(self, params):
setattr(self, cls_name.lower(), Class(self))

if "Output" in dict_classes:
# path_run would be initialized by the Output instance if available
# See self.output._init_name_run()
self.path_run = Path(self.output.path_run)
self.output.copy(self.path_run)
if existing_path_run:
self.path_run = self.output.path_run = Path(existing_path_run)
else:
# path_run would be initialized by the Output instance if available
# See self.output._init_name_run()
self.path_run = Path(self.output.path_run)
if mpi.rank == 0:
self.output.copy(self.path_run)
else:
par_file = None
self.path_run = None
Expand All @@ -193,14 +221,15 @@ def __init__(self, params):
logger.info(f"solver: {self.__class__}")
logger.info(f"path_run: {self.path_run}")
logger.info("*" * _banner_length)
if self.path_run:
if self.path_run and not existing_path_run:
par_file = self.path_run / f"{self.output.name_pkg}.par"
logger.info(f"Writing params files... {par_file}, params.xml")
with open(par_file, "w") as fp:
params.nek._write_par(fp)

params._save_as_xml(
self.path_run / "params.xml", f"eTurb version {__version__}"
self.path_run / "params.xml",
f"snek5000 version {__version__}",
)


Expand Down
20 changes: 17 additions & 3 deletions src/snek5000/solvers/kth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,30 @@
==================
"""
from ..info import InfoSolverMake
# FIXME: This breaks because a different layout is used?
# from ..info import InfoSolverMake as _InfoSolver
from ..info import InfoSolverNek as _InfoSolver
from ..util import docstring_params
from .base import SimulNek


class InfoSolverKTH(_InfoSolver):
"""Contain the information on a :class:`snek5000.solvers.kth.SimulKTH`
instance.
"""

def _init_root(self):
super()._init_root()
self.module_name = "snek5000.solvers.kth"
self.class_name = "Simul"
self.short_name = "kth"


class SimulKTH(SimulNek):
"""A base class which incorporates parameters for KTH toolbox also."""

# FIXME: This breaks because a different layout is used?
# InfoSolver = InfoSolverMake
InfoSolver = InfoSolverKTH

@staticmethod
def _complete_params_with_default(params):
Expand Down
8 changes: 3 additions & 5 deletions src/snek5000/util/archive.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,7 @@ def exec_tar(tarball, items, remove):
else:
output = tar

main = shlex.split(
tar_cmd(compress_format, remove=remove, append=output.exists())
)
main = shlex.split(tar_cmd(compress_format, remove=remove, append=output.exists()))

# run command
items = [str(i) for i in items]
Expand Down Expand Up @@ -152,7 +150,7 @@ def parse_args_from_filename(tarball):

def compress_cmd(compress_format):
compress_program = {
".gz": "pigz",
".gz": "pigz" if shutil.which("pigz") else "gzip",
".xz": "xz",
".lz4": "lz4",
".zst": "zstdmt --rm",
Expand All @@ -164,7 +162,7 @@ def compress_cmd(compress_format):
def tar_cmd(compress_format="", remove=False, append=False):
archive_program = {
"": "tar",
".gz": "tar --use-compress-program pigz",
".gz": f"tar --use-compress-program {compress_cmd('.gz')}",
".xz": "tar",
".lz4": "tar --use-compress-program lz4",
".zst": "tar --use-compress-program zstdmt",
Expand Down
20 changes: 20 additions & 0 deletions tests/test_solver.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
from snek5000 import load_simul
from snek5000.solvers import available_solvers, import_solver


def test_entrypoints():
solvers = available_solvers()
assert "nek" in solvers
assert "kth" in solvers
assert "phill" in solvers


def test_init_base():
from snek5000.solvers.base import Simul
Expand All @@ -11,3 +21,13 @@ def test_init_kth():

params = Simul.create_default_params()
Simul(params)


def test_import_solver():
from phill.solver import Simul

assert Simul is import_solver("phill")
params = Simul.create_default_params()
sim1 = Simul(params)
sim2 = load_simul(sim1.path_run)
assert isinstance(sim1, Simul) and isinstance(sim2, Simul)

0 comments on commit 5ff43c1

Please sign in to comment.