foamlib 0.6.3__tar.gz → 0.6.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {foamlib-0.6.3 → foamlib-0.6.4}/PKG-INFO +14 -2
  2. {foamlib-0.6.3 → foamlib-0.6.4}/README.md +13 -1
  3. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/__init__.py +4 -2
  4. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/__init__.py +2 -0
  5. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/_async.py +9 -6
  6. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/_run.py +36 -5
  7. foamlib-0.6.4/foamlib/_cases/_slurm.py +69 -0
  8. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/_sync.py +15 -12
  9. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib.egg-info/PKG-INFO +14 -2
  10. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib.egg-info/SOURCES.txt +1 -0
  11. {foamlib-0.6.3 → foamlib-0.6.4}/LICENSE.txt +0 -0
  12. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/_base.py +0 -0
  13. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/_subprocess.py +0 -0
  14. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_cases/_util.py +0 -0
  15. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/__init__.py +0 -0
  16. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/_base.py +0 -0
  17. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/_files.py +0 -0
  18. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/_io.py +0 -0
  19. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/_parsing.py +0 -0
  20. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/_serialization.py +0 -0
  21. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/_files/_util.py +0 -0
  22. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib/py.typed +0 -0
  23. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib.egg-info/dependency_links.txt +0 -0
  24. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib.egg-info/requires.txt +0 -0
  25. {foamlib-0.6.3 → foamlib-0.6.4}/foamlib.egg-info/top_level.txt +0 -0
  26. {foamlib-0.6.3 → foamlib-0.6.4}/pyproject.toml +0 -0
  27. {foamlib-0.6.3 → foamlib-0.6.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.3
3
+ Version: 0.6.4
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
5
  Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
6
  Project-URL: Homepage, https://github.com/gerlero/foamlib
@@ -69,7 +69,7 @@ Requires-Dist: foamlib[docs]; extra == "dev"
69
69
  It offers the following classes:
70
70
 
71
71
  * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFieldFile)): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s, using `foamlib`'s own parser. Supports both ASCII and binary field formats.
72
- * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
72
+ * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
73
73
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
74
74
 
75
75
  ## Get started
@@ -174,6 +174,18 @@ async def cost(x):
174
174
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
175
175
  ```
176
176
 
177
+ ### Use it to create a `run` (or `clean`) script
178
+
179
+ ```python
180
+ #!/usr/bin/env python3
181
+ from pathlib import Path
182
+ from foamlib import FoamCase
183
+
184
+ case = FoamCase(Path(__file__).parent)
185
+ # Any additional configuration here
186
+ case.run()
187
+ ```
188
+
177
189
  ## Documentation
178
190
 
179
191
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -15,7 +15,7 @@
15
15
  It offers the following classes:
16
16
 
17
17
  * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFieldFile)): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s, using `foamlib`'s own parser. Supports both ASCII and binary field formats.
18
- * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
18
+ * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
19
19
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
20
20
 
21
21
  ## Get started
@@ -120,6 +120,18 @@ async def cost(x):
120
120
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
121
121
  ```
122
122
 
123
+ ### Use it to create a `run` (or `clean`) script
124
+
125
+ ```python
126
+ #!/usr/bin/env python3
127
+ from pathlib import Path
128
+ from foamlib import FoamCase
129
+
130
+ case = FoamCase(Path(__file__).parent)
131
+ # Any additional configuration here
132
+ case.run()
133
+ ```
134
+
123
135
  ## Documentation
124
136
 
125
137
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -1,9 +1,10 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.6.3"
3
+ __version__ = "0.6.4"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
7
+ AsyncSlurmFoamCase,
7
8
  CalledProcessError,
8
9
  FoamCase,
9
10
  FoamCaseBase,
@@ -13,11 +14,12 @@ from ._files import FoamFieldFile, FoamFile, FoamFileBase
13
14
 
14
15
  __all__ = [
15
16
  "AsyncFoamCase",
17
+ "AsyncSlurmFoamCase",
16
18
  "CalledProcessError",
17
19
  "FoamFile",
18
20
  "FoamCase",
21
+ "FoamCaseBase",
19
22
  "FoamCaseRunBase",
20
23
  "FoamFieldFile",
21
- "FoamCaseBase",
22
24
  "FoamFileBase",
23
25
  ]
@@ -1,6 +1,7 @@
1
1
  from ._async import AsyncFoamCase
2
2
  from ._base import FoamCaseBase
3
3
  from ._run import FoamCaseRunBase
4
+ from ._slurm import AsyncSlurmFoamCase
4
5
  from ._subprocess import CalledProcessError
5
6
  from ._sync import FoamCase
6
7
 
@@ -8,6 +9,7 @@ __all__ = [
8
9
  "AsyncFoamCase",
9
10
  "FoamCaseBase",
10
11
  "FoamCaseRunBase",
12
+ "AsyncSlurmFoamCase",
11
13
  "CalledProcessError",
12
14
  "FoamCase",
13
15
  ]
@@ -164,17 +164,20 @@ class AsyncFoamCase(FoamCaseRunBase):
164
164
  ):
165
165
  await coro
166
166
 
167
- async def block_mesh(self, *, check: bool = True) -> None:
167
+ async def block_mesh(self, *, check: bool = True, log: bool = True) -> None:
168
168
  """Run blockMesh on this case."""
169
- await self.run(["blockMesh"], check=check)
169
+ for coro in self._block_mesh_calls(check=check, log=log):
170
+ await coro
170
171
 
171
- async def decompose_par(self, *, check: bool = True) -> None:
172
+ async def decompose_par(self, *, check: bool = True, log: bool = True) -> None:
172
173
  """Decompose this case for parallel running."""
173
- await self.run(["decomposePar"], check=check)
174
+ for coro in self._decompose_par_calls(check=check, log=log):
175
+ await coro
174
176
 
175
- async def reconstruct_par(self, *, check: bool = True) -> None:
177
+ async def reconstruct_par(self, *, check: bool = True, log: bool = True) -> None:
176
178
  """Reconstruct this case after parallel running."""
177
- await self.run(["reconstructPar"], check=check)
179
+ for coro in self._reconstruct_par_calls(check=check, log=log):
180
+ await coro
178
181
 
179
182
  async def restore_0_dir(self) -> None:
180
183
  """Restore the 0 directory from the 0.orig directory."""
@@ -63,7 +63,9 @@ class FoamCaseRunBase(FoamCaseBase):
63
63
 
64
64
  if ret not in self:
65
65
  yield self._case.run(
66
- ["postProcess", "-func", "writeCellCentres", "-time", self.name]
66
+ ["postProcess", "-func", "writeCellCentres", "-time", self.name],
67
+ cpus=0,
68
+ log=False,
67
69
  )
68
70
 
69
71
  return ret
@@ -127,13 +129,19 @@ class FoamCaseRunBase(FoamCaseBase):
127
129
 
128
130
  @abstractmethod
129
131
  def block_mesh(
130
- self, *, check: bool = True
132
+ self, *, check: bool = True, log: bool = True
131
133
  ) -> Union[None, Coroutine[None, None, None]]:
132
134
  raise NotImplementedError
133
135
 
134
136
  @abstractmethod
135
137
  def decompose_par(
136
- self, *, check: bool = True
138
+ self, *, check: bool = True, log: bool = True
139
+ ) -> Union[None, Coroutine[None, None, None]]:
140
+ raise NotImplementedError
141
+
142
+ @abstractmethod
143
+ def reconstruct_par(
144
+ self, *, check: bool = True, log: bool = True
137
145
  ) -> Union[None, Coroutine[None, None, None]]:
138
146
  raise NotImplementedError
139
147
 
@@ -318,6 +326,21 @@ class FoamCaseRunBase(FoamCaseBase):
318
326
  yield self._rmtree(self.path / "0", ignore_errors=True)
319
327
  yield self._copytree(self.path / "0.orig", self.path / "0", symlinks=True)
320
328
 
329
+ def _block_mesh_calls(
330
+ self, *, check: bool, log: bool
331
+ ) -> Generator[Any, None, None]:
332
+ yield self.run(["blockMesh"], cpus=0, check=check, log=log)
333
+
334
+ def _decompose_par_calls(
335
+ self, *, check: bool, log: bool
336
+ ) -> Generator[Any, None, None]:
337
+ yield self.run(["decomposePar"], cpus=0, check=check, log=log)
338
+
339
+ def _reconstruct_par_calls(
340
+ self, *, check: bool, log: bool
341
+ ) -> Generator[Any, None, None]:
342
+ yield self.run(["reconstructPar"], cpus=0, check=check, log=log)
343
+
321
344
  def _run_calls(
322
345
  self,
323
346
  cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
@@ -326,6 +349,7 @@ class FoamCaseRunBase(FoamCaseBase):
326
349
  cpus: Optional[int],
327
350
  check: bool,
328
351
  log: bool,
352
+ **kwargs: Any,
329
353
  ) -> Generator[Any, None, None]:
330
354
  if cmd is not None:
331
355
  if parallel:
@@ -358,6 +382,7 @@ class FoamCaseRunBase(FoamCaseBase):
358
382
  env=self.__env(shell=isinstance(cmd, str)),
359
383
  stdout=stdout,
360
384
  stderr=stderr,
385
+ **kwargs,
361
386
  )
362
387
 
363
388
  else:
@@ -376,7 +401,9 @@ class FoamCaseRunBase(FoamCaseBase):
376
401
  if cpus is None:
377
402
  cpus = 1
378
403
 
379
- yield self.run([script_path], parallel=False, cpus=cpus, check=check)
404
+ yield self.run(
405
+ [script_path], parallel=False, cpus=cpus, check=check, **kwargs
406
+ )
380
407
 
381
408
  else:
382
409
  if not self and (self.path / "0.orig").is_dir():
@@ -406,5 +433,9 @@ class FoamCaseRunBase(FoamCaseBase):
406
433
  cpus = 1
407
434
 
408
435
  yield self.run(
409
- [self.application], parallel=parallel, cpus=cpus, check=check
436
+ [self.application],
437
+ parallel=parallel,
438
+ cpus=cpus,
439
+ check=check,
440
+ **kwargs,
410
441
  )
@@ -0,0 +1,69 @@
1
+ import os
2
+ import shutil
3
+ import sys
4
+ from typing import Any, Optional, Union
5
+
6
+ if sys.version_info >= (3, 9):
7
+ from collections.abc import Sequence
8
+ else:
9
+ from typing import Sequence
10
+
11
+ from ._async import AsyncFoamCase
12
+ from ._subprocess import run_async
13
+
14
+
15
+ class AsyncSlurmFoamCase(AsyncFoamCase):
16
+ """An asynchronous OpenFOAM case that launches jobs on a Slurm cluster."""
17
+
18
+ @staticmethod
19
+ async def _run(
20
+ cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
21
+ *,
22
+ cpus: int,
23
+ fallback: bool = False,
24
+ **kwargs: Any,
25
+ ) -> None:
26
+ if fallback and shutil.which("salloc") is None:
27
+ await AsyncFoamCase._run(cmd, cpus=cpus, **kwargs)
28
+ return
29
+
30
+ if cpus >= 1:
31
+ if isinstance(cmd, str):
32
+ cmd = ["/bin/sh", "-c", cmd]
33
+
34
+ if cpus == 1:
35
+ cmd = ["srun", *cmd]
36
+
37
+ cmd = ["salloc", "-n", str(cpus), "--job-name", "foamlib", *cmd]
38
+
39
+ await run_async(cmd, **kwargs)
40
+
41
+ async def run(
42
+ self,
43
+ cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
44
+ *,
45
+ parallel: Optional[bool] = None,
46
+ cpus: Optional[int] = None,
47
+ check: bool = True,
48
+ log: bool = True,
49
+ fallback: bool = False,
50
+ ) -> None:
51
+ """
52
+ Run this case, or a specified command in the context of this case.
53
+
54
+ :param cmd: The command to run. If None, run the case. If a sequence, the first element is the command and the rest are arguments. If a string, `cmd` is executed in a shell.
55
+ :param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
56
+ :param cpus: The number of CPUs to use. If None, autodetect according to the case. If 0, run locally.
57
+ :param check: If True, raise a CalledProcessError if any command returns a non-zero exit code.
58
+ :param log: If True, log the command output to a file.
59
+ :param fallback: If True, fall back to running the command locally if Slurm is not available.
60
+ """
61
+ for coro in self._run_calls(
62
+ cmd=cmd,
63
+ parallel=parallel,
64
+ cpus=cpus,
65
+ check=check,
66
+ log=log,
67
+ fallback=fallback,
68
+ ):
69
+ await coro
@@ -139,17 +139,20 @@ class FoamCase(FoamCaseRunBase):
139
139
  ):
140
140
  pass
141
141
 
142
- def block_mesh(self, *, check: bool = True) -> None:
142
+ def block_mesh(self, *, check: bool = True, log: bool = True) -> None:
143
143
  """Run blockMesh on this case."""
144
- self.run(["blockMesh"], check=check)
144
+ for _ in self._block_mesh_calls(check=check, log=log):
145
+ pass
145
146
 
146
- def decompose_par(self, *, check: bool = True) -> None:
147
+ def decompose_par(self, *, check: bool = True, log: bool = True) -> None:
147
148
  """Decompose this case for parallel running."""
148
- self.run(["decomposePar"], check=check)
149
+ for _ in self._decompose_par_calls(check=check, log=log):
150
+ pass
149
151
 
150
- def reconstruct_par(self, *, check: bool = True) -> None:
152
+ def reconstruct_par(self, *, check: bool = True, log: bool = True) -> None:
151
153
  """Reconstruct this case after parallel running."""
152
- self.run(["reconstructPar"], check=check)
154
+ for _ in self._reconstruct_par_calls(check=check, log=log):
155
+ pass
153
156
 
154
157
  def restore_0_dir(self) -> None:
155
158
  """Restore the 0 directory from the 0.orig directory."""
@@ -164,12 +167,12 @@ class FoamCase(FoamCaseRunBase):
164
167
 
165
168
  :param dst: The destination path. If None, clone to `$FOAM_RUN/foamlib`.
166
169
  """
167
- cmds = ValuedGenerator(self._copy_calls(dst))
170
+ calls = ValuedGenerator(self._copy_calls(dst))
168
171
 
169
- for _ in cmds:
172
+ for _ in calls:
170
173
  pass
171
174
 
172
- return cmds.value
175
+ return calls.value
173
176
 
174
177
  def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Self:
175
178
  """
@@ -179,9 +182,9 @@ class FoamCase(FoamCaseRunBase):
179
182
 
180
183
  :param dst: The destination path. If None, clone to `$FOAM_RUN/foamlib`.
181
184
  """
182
- cmds = ValuedGenerator(self._clone_calls(dst))
185
+ calls = ValuedGenerator(self._clone_calls(dst))
183
186
 
184
- for _ in cmds:
187
+ for _ in calls:
185
188
  pass
186
189
 
187
- return cmds.value
190
+ return calls.value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.3
3
+ Version: 0.6.4
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
5
  Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
6
  Project-URL: Homepage, https://github.com/gerlero/foamlib
@@ -69,7 +69,7 @@ Requires-Dist: foamlib[docs]; extra == "dev"
69
69
  It offers the following classes:
70
70
 
71
71
  * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFieldFile)): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s, using `foamlib`'s own parser. Supports both ASCII and binary field formats.
72
- * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
72
+ * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
73
73
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
74
74
 
75
75
  ## Get started
@@ -174,6 +174,18 @@ async def cost(x):
174
174
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
175
175
  ```
176
176
 
177
+ ### Use it to create a `run` (or `clean`) script
178
+
179
+ ```python
180
+ #!/usr/bin/env python3
181
+ from pathlib import Path
182
+ from foamlib import FoamCase
183
+
184
+ case = FoamCase(Path(__file__).parent)
185
+ # Any additional configuration here
186
+ case.run()
187
+ ```
188
+
177
189
  ## Documentation
178
190
 
179
191
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -12,6 +12,7 @@ foamlib/_cases/__init__.py
12
12
  foamlib/_cases/_async.py
13
13
  foamlib/_cases/_base.py
14
14
  foamlib/_cases/_run.py
15
+ foamlib/_cases/_slurm.py
15
16
  foamlib/_cases/_subprocess.py
16
17
  foamlib/_cases/_sync.py
17
18
  foamlib/_cases/_util.py
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes