Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions foamlib/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""A command-line interface for the 'foamlib' package."""

from __future__ import annotations

import asyncio
import sys
from pathlib import Path

import typer

if sys.version_info >= (3, 9):
from typing import Annotated
else:
from typing_extensions import Annotated

from . import AsyncFoamCase, AsyncSlurmFoamCase, __version__
from ._util import async_to_sync

app = typer.Typer(help=__doc__)


@app.command()
@async_to_sync
async def run(
cases: Annotated[
list[Path] | None,
typer.Argument(help="Case directories", show_default="."),
] = None,
slurm: Annotated[
bool | None,
typer.Option(
help="Use Slurm for running cases.", show_default="use Slurm if available"
),
] = None,
max_cpus: Annotated[
int,
typer.Option(
help="Maximum number of concurrent processes (for non-Slurm runs).",
),
] = AsyncFoamCase.max_cpus,
) -> None:
"""Run one or more OpenFOAM cases."""
if cases is None:
cases = [Path.cwd()]

AsyncFoamCase.max_cpus = max_cpus

if slurm is None:
await asyncio.gather(
*(AsyncSlurmFoamCase(case).run(fallback=True) for case in cases)
)
elif slurm:
await asyncio.gather(*(AsyncSlurmFoamCase(case).run() for case in cases))
else:
await asyncio.gather(*(AsyncFoamCase(case).run() for case in cases))


@app.command()
@async_to_sync
async def clean(
cases: Annotated[
list[Path] | None,
typer.Argument(help="Case directories", show_default="."),
],
) -> None:
"""Clean one or more OpenFOAM cases."""
if cases is None:
cases = [Path.cwd()]

await asyncio.gather(*(AsyncFoamCase(case).clean() for case in cases))


def _version_callback(*, show: bool) -> None:
if show:
typer.echo(f"foamlib {__version__}")
raise typer.Exit


@app.callback()
def common( # noqa: D103
*,
version: Annotated[
bool,
typer.Option(
"--version", help="Show version and exit.", callback=_version_callback
),
] = False,
) -> None:
pass


if __name__ == "__main__":
app()
21 changes: 21 additions & 0 deletions foamlib/_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from __future__ import annotations

import asyncio
import sys
from functools import wraps
from typing import Any, TypeVar

if sys.version_info >= (3, 9):
from collections.abc import Callable, Coroutine
else:
from typing import Callable, Coroutine

R = TypeVar("R")


def async_to_sync(coro: Callable[..., Coroutine[Any, Any, R]]) -> Callable[..., R]:
@wraps(coro)
def wrapper(*args: Any, **kwargs: Any) -> R:
return asyncio.run(coro(*args, **kwargs))

return wrapper
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies = [
"numpy>=1,<3",
"pyparsing>=3.1.2,<4",
"rich>=13,<15",
"typer-slim>=0.13,<0.16",
"typing-extensions>=4,<5; python_version<'3.11'",
]

Expand Down Expand Up @@ -76,6 +77,9 @@ Homepage = "https://github.com/gerlero/foamlib"
Repository = "https://github.com/gerlero/foamlib"
Documentation = "https://foamlib.readthedocs.io"

[project.scripts]
foamlib = "foamlib.__main__:app"

[tool.hatch.version]
path = "foamlib/__init__.py"

Expand Down
Loading