Skip to content
Merged
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
37 changes: 22 additions & 15 deletions src/foamlib/_cases/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def clone(self, dst: os.PathLike[str] | str | None = None) -> object:

@abstractmethod
def _prepare(
self, *, check: bool = True, log: bool = True
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None | Coroutine[None, None, None]:
raise NotImplementedError

Expand All @@ -124,25 +124,25 @@ def run(
parallel: bool | None = None,
cpus: int | None = None,
check: bool = True,
log: bool = True,
log: bool | str | os.PathLike[str] = True,
) -> None | Coroutine[None, None, None]:
raise NotImplementedError

@abstractmethod
def block_mesh(
self, *, check: bool = True, log: bool = True
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None | Coroutine[None, None, None]:
raise NotImplementedError

@abstractmethod
def decompose_par(
self, *, check: bool = True, log: bool = True
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None | Coroutine[None, None, None]:
raise NotImplementedError

@abstractmethod
def reconstruct_par(
self, *, check: bool = True, log: bool = True
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None | Coroutine[None, None, None]:
raise NotImplementedError

Expand Down Expand Up @@ -262,13 +262,20 @@ def __cmd_name(cmd: Sequence[str | os.PathLike[str]] | str) -> str:

@contextmanager
def __output(
self, cmd: Sequence[str | os.PathLike[str]] | str, *, log: bool
self,
cmd: Sequence[str | os.PathLike[str]] | str,
*,
log: bool | str | os.PathLike[str],
) -> Generator[tuple[int | TextIOBase, int | TextIOBase], None, None]:
if log:
with (self.path / f"log.{self.__cmd_name(cmd)}").open("a") as stdout:
yield stdout, STDOUT
else:
if log is False:
yield DEVNULL, DEVNULL
return

if log is True:
log = f"log.{self.__cmd_name(cmd)}"

with (self.path / log).open("a") as stdout:
yield stdout, STDOUT

@contextmanager
def __process_stdout(
Expand Down Expand Up @@ -364,22 +371,22 @@ def _restore_0_dir_calls(
yield self._copytree(self.path / "0.orig", self.path / "0", symlinks=True)

def _block_mesh_calls(
self, *, check: bool, log: bool
self, *, check: bool, log: bool | str | os.PathLike[str]
) -> Generator[object | Coroutine[None, None, object], None, None]:
yield self.run(["blockMesh"], cpus=0, check=check, log=log)

def _decompose_par_calls(
self, *, check: bool, log: bool
self, *, check: bool, log: bool | str | os.PathLike[str]
) -> Generator[object | Coroutine[None, None, object], None, None]:
yield self.run(["decomposePar"], cpus=0, check=check, log=log)

def _reconstruct_par_calls(
self, *, check: bool, log: bool
self, *, check: bool, log: bool | str | os.PathLike[str]
) -> Generator[object | Coroutine[None, None, object], None, None]:
yield self.run(["reconstructPar"], cpus=0, check=check, log=log)

def _prepare_calls(
self, *, check: bool, log: bool
self, *, check: bool, log: bool | str | os.PathLike[str]
) -> Generator[object | Coroutine[None, None, object], None, None]:
if (script_path := self.__prepare_script()) is not None:
yield self.run([script_path], log=log, check=check)
Expand All @@ -394,7 +401,7 @@ def _run_calls(
cpus: int | None = None,
parallel: bool | None,
check: bool,
log: bool,
log: bool | str | os.PathLike[str],
**kwargs: Any,
) -> Generator[object | Coroutine[None, None, object], None, None]:
if cmd is not None:
Expand Down
18 changes: 13 additions & 5 deletions src/foamlib/_cases/async_.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ def __getitem__(
return [AsyncFoamCase.TimeDirectory(r) for r in ret]

@override
async def _prepare(self, *, check: bool = True, log: bool = True) -> None:
async def _prepare(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
for coro in self._prepare_calls(check=check, log=log):
assert isinstance(coro, Awaitable)
await coro
Expand All @@ -218,7 +220,7 @@ async def run(
parallel: bool | None = None,
cpus: int | None = None,
check: bool = True,
log: bool = True,
log: bool | str | os.PathLike[str] = True,
) -> None:
"""
Run this case, or a specified command in the context of this case.
Expand Down Expand Up @@ -265,21 +267,27 @@ async def run(
await coro

@override
async def block_mesh(self, *, check: bool = True, log: bool = True) -> None:
async def block_mesh(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
"""Run blockMesh on this case."""
for coro in self._block_mesh_calls(check=check, log=log):
assert isinstance(coro, Awaitable)
await coro

@override
async def decompose_par(self, *, check: bool = True, log: bool = True) -> None:
async def decompose_par(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
"""Decompose this case for parallel running."""
for coro in self._decompose_par_calls(check=check, log=log):
assert isinstance(coro, Awaitable)
await coro

@override
async def reconstruct_par(self, *, check: bool = True, log: bool = True) -> None:
async def reconstruct_par(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
"""Reconstruct this case after parallel running."""
for coro in self._reconstruct_par_calls(check=check, log=log):
assert isinstance(coro, Awaitable)
Expand Down
2 changes: 1 addition & 1 deletion src/foamlib/_cases/slurm.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ async def run(
parallel: bool | None = None,
cpus: int | None = None,
check: bool = True,
log: bool = True,
log: bool | str | os.PathLike[str] = True,
fallback: bool = False,
) -> None:
"""
Expand Down
18 changes: 13 additions & 5 deletions src/foamlib/_cases/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@ def clean(self, *, check: bool = False) -> None:
pass

@override
def _prepare(self, *, check: bool = True, log: bool = True) -> None:
def _prepare(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
for _ in self._prepare_calls(check=check, log=log):
pass

Expand All @@ -177,7 +179,7 @@ def run(
parallel: bool | None = None,
cpus: int | None = None,
check: bool = True,
log: bool = True,
log: bool | str | os.PathLike[str] = True,
) -> None:
"""
Run this case, or a specified command in the context of this case.
Expand Down Expand Up @@ -223,19 +225,25 @@ def run(
pass

@override
def block_mesh(self, *, check: bool = True, log: bool = True) -> None:
def block_mesh(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
"""Run blockMesh on this case."""
for _ in self._block_mesh_calls(check=check, log=log):
pass

@override
def decompose_par(self, *, check: bool = True, log: bool = True) -> None:
def decompose_par(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
"""Decompose this case for parallel running."""
for _ in self._decompose_par_calls(check=check, log=log):
pass

@override
def reconstruct_par(self, *, check: bool = True, log: bool = True) -> None:
def reconstruct_par(
self, *, check: bool = True, log: bool | str | os.PathLike[str] = True
) -> None:
"""Reconstruct this case after parallel running."""
for _ in self._reconstruct_par_calls(check=check, log=log):
pass
Expand Down
29 changes: 29 additions & 0 deletions tests/test_cases/test_flange.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,32 @@ def test_run_cmd_shell(flange: FoamCase) -> None:

def test_path(flange: FoamCase) -> None:
assert Path(flange) == flange.path


def test_run_cmd_log_false(flange: FoamCase) -> None:
if not flange:
flange.restore_0_dir()

ans_path = (
Path(os.environ["FOAM_TUTORIALS"]) / "resources" / "geometry" / "flange.ans"
)
if not ans_path.exists():
ans_path = Path("flange.ans")

flange.run(["ansysToFoam", ans_path, "-scale", "0.001"], log=False)
assert not (flange.path / "log.ansysToFoam").exists()


def test_run_cmd_custom_log(flange: FoamCase) -> None:
if not flange:
flange.restore_0_dir()

ans_path = (
Path(os.environ["FOAM_TUTORIALS"]) / "resources" / "geometry" / "flange.ans"
)
if not ans_path.exists():
ans_path = Path("flange.ans")

flange.run(["ansysToFoam", ans_path, "-scale", "0.001"], log="custom_ansys.log")
assert not (flange.path / "log.ansysToFoam").exists()
assert (flange.path / "custom_ansys.log").exists()
47 changes: 47 additions & 0 deletions tests/test_cases/test_flange_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,50 @@ async def test_run_cmd_shell(flange: AsyncFoamCase) -> None:

def test_path(flange: AsyncFoamCase) -> None:
assert Path(flange) == flange.path


@pytest.mark.asyncio
async def test_run_cmd_log_false(flange: AsyncFoamCase) -> None:
if not flange:
await flange.restore_0_dir()

ans_path = (
Path(os.environ["FOAM_TUTORIALS"]) / "resources" / "geometry" / "flange.ans"
)
if not ans_path.exists():
ans_path = Path("flange.ans")

if isinstance(flange, AsyncSlurmFoamCase):
await flange.run(
["ansysToFoam", ans_path, "-scale", "0.001"], log=False, fallback=True
)
else:
await flange.run(["ansysToFoam", ans_path, "-scale", "0.001"], log=False)

assert not (flange.path / "log.ansysToFoam").exists()


@pytest.mark.asyncio
async def test_run_cmd_custom_log(flange: AsyncFoamCase) -> None:
if not flange:
await flange.restore_0_dir()

ans_path = (
Path(os.environ["FOAM_TUTORIALS"]) / "resources" / "geometry" / "flange.ans"
)
if not ans_path.exists():
ans_path = Path("flange.ans")

if isinstance(flange, AsyncSlurmFoamCase):
await flange.run(
["ansysToFoam", ans_path, "-scale", "0.001"],
log="custom_ansys.log",
fallback=True,
)
else:
await flange.run(
["ansysToFoam", ans_path, "-scale", "0.001"], log="custom_ansys.log"
)

assert not (flange.path / "log.ansysToFoam").exists()
assert (flange.path / "custom_ansys.log").exists()
Loading