foamlib 0.5.0__py3-none-any.whl → 0.5.1__py3-none-any.whl

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.
foamlib/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.5.0"
3
+ __version__ = "0.5.1"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
foamlib/_cases/_async.py CHANGED
@@ -5,16 +5,18 @@ import sys
5
5
  import tempfile
6
6
  from contextlib import asynccontextmanager
7
7
  from pathlib import Path
8
- from typing import (
9
- Callable,
10
- Optional,
11
- Union,
12
- )
8
+ from typing import Callable, Optional, TypeVar, Union
13
9
 
14
10
  if sys.version_info >= (3, 9):
15
- from collections.abc import AsyncGenerator, Collection, Sequence
11
+ from collections.abc import (
12
+ AsyncGenerator,
13
+ Awaitable,
14
+ Collection,
15
+ Iterable,
16
+ Sequence,
17
+ )
16
18
  else:
17
- from typing import AsyncGenerator, Collection, Sequence
19
+ from typing import AsyncGenerator, Awaitable, Collection, Iterable, Sequence
18
20
 
19
21
  if sys.version_info >= (3, 11):
20
22
  from typing import Self
@@ -27,6 +29,9 @@ from ._recipes import _FoamCaseRecipes
27
29
  from ._subprocess import run_async
28
30
  from ._util import awaitableasynccontextmanager
29
31
 
32
+ X = TypeVar("X")
33
+ Y = TypeVar("Y")
34
+
30
35
 
31
36
  class AsyncFoamCase(_FoamCaseRecipes):
32
37
  """
@@ -231,3 +236,10 @@ class AsyncFoamCase(_FoamCaseRecipes):
231
236
  await self._rmtree(dst.parent)
232
237
  else:
233
238
  await self._rmtree(dst)
239
+
240
+ @staticmethod
241
+ def map(coro: Callable[[X], Awaitable[Y]], iterable: Iterable[X]) -> Iterable[Y]:
242
+ """Run an async function on each element of an iterable concurrently."""
243
+ return asyncio.get_event_loop().run_until_complete(
244
+ asyncio.gather(*(coro(arg) for arg in iterable))
245
+ )
foamlib/_cases/_sync.py CHANGED
@@ -36,6 +36,10 @@ class FoamCase(_FoamCaseRecipes):
36
36
  :param path: The path to the case directory.
37
37
  """
38
38
 
39
+ def __init__(self, path: Union["os.PathLike[str]", str] = Path()):
40
+ super().__init__(path)
41
+ self._tmp: Optional[bool] = None
42
+
39
43
  @staticmethod
40
44
  def _rmtree(
41
45
  path: Union["os.PathLike[str]", str], *, ignore_errors: bool = False
@@ -55,6 +59,10 @@ class FoamCase(_FoamCaseRecipes):
55
59
  shutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
56
60
 
57
61
  def __enter__(self) -> "FoamCase":
62
+ if self._tmp is None:
63
+ raise RuntimeError(
64
+ "Cannot use a non-copied/cloned case as a context manager"
65
+ )
58
66
  return self
59
67
 
60
68
  def __exit__(
@@ -63,7 +71,15 @@ class FoamCase(_FoamCaseRecipes):
63
71
  exc_val: Optional[BaseException],
64
72
  exc_tb: Optional[TracebackType],
65
73
  ) -> None:
66
- self._rmtree(self.path)
74
+ if self._tmp is not None:
75
+ if self._tmp:
76
+ self._rmtree(self.path.parent)
77
+ else:
78
+ self._rmtree(self.path)
79
+ else:
80
+ raise RuntimeError(
81
+ "Cannot use a non-copied/cloned case as a context manager"
82
+ )
67
83
 
68
84
  def clean(
69
85
  self,
@@ -160,11 +176,17 @@ class FoamCase(_FoamCaseRecipes):
160
176
  """
161
177
  if dst is None:
162
178
  dst = Path(tempfile.mkdtemp(), self.name)
179
+ tmp = True
180
+ else:
181
+ tmp = False
163
182
 
164
183
  for name, args, kwargs in self._copy_cmds(dst):
165
184
  getattr(self, name)(*args, **kwargs)
166
185
 
167
- return type(self)(dst)
186
+ ret = type(self)(dst)
187
+ ret._tmp = tmp
188
+
189
+ return ret
168
190
 
169
191
  def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> "Self":
170
192
  """
@@ -176,8 +198,14 @@ class FoamCase(_FoamCaseRecipes):
176
198
  """
177
199
  if dst is None:
178
200
  dst = Path(tempfile.mkdtemp(), self.name)
201
+ tmp = True
202
+ else:
203
+ tmp = False
179
204
 
180
205
  for name, args, kwargs in self._clone_cmds(dst):
181
206
  getattr(self, name)(*args, **kwargs)
182
207
 
183
- return type(self)(dst)
208
+ ret = type(self)(dst)
209
+ ret._tmp = tmp
210
+
211
+ return ret
foamlib/_cases/_util.py CHANGED
@@ -1,15 +1,33 @@
1
+ import functools
2
+ import sys
1
3
  from types import TracebackType
2
- from typing import Any, AsyncContextManager, Callable, Optional, Type
4
+ from typing import (
5
+ Any,
6
+ AsyncContextManager,
7
+ Callable,
8
+ Generic,
9
+ Optional,
10
+ Type,
11
+ TypeVar,
12
+ )
3
13
 
14
+ if sys.version_info >= (3, 9):
15
+ from collections.abc import Generator
16
+ else:
17
+ from typing import Generator
4
18
 
5
- class _AwaitableAsyncContextManager:
6
- def __init__(self, cm: "AsyncContextManager[Any]"):
19
+
20
+ R = TypeVar("R")
21
+
22
+
23
+ class _AwaitableAsyncContextManager(Generic[R]):
24
+ def __init__(self, cm: "AsyncContextManager[R]"):
7
25
  self._cm = cm
8
26
 
9
- def __await__(self) -> Any:
27
+ def __await__(self) -> Generator[Any, Any, R]:
10
28
  return self._cm.__aenter__().__await__()
11
29
 
12
- async def __aenter__(self) -> Any:
30
+ async def __aenter__(self) -> R:
13
31
  return await self._cm.__aenter__()
14
32
 
15
33
  async def __aexit__(
@@ -17,11 +35,15 @@ class _AwaitableAsyncContextManager:
17
35
  exc_type: Optional[Type[BaseException]],
18
36
  exc_val: Optional[BaseException],
19
37
  exc_tb: Optional[TracebackType],
20
- ) -> Any:
38
+ ) -> Optional[bool]:
21
39
  return await self._cm.__aexit__(exc_type, exc_val, exc_tb)
22
40
 
23
41
 
24
42
  def awaitableasynccontextmanager(
25
- cm: Callable[..., "AsyncContextManager[Any]"],
26
- ) -> Callable[..., _AwaitableAsyncContextManager]:
27
- return lambda *args, **kwargs: _AwaitableAsyncContextManager(cm(*args, **kwargs))
43
+ cm: Callable[..., "AsyncContextManager[R]"],
44
+ ) -> Callable[..., _AwaitableAsyncContextManager[R]]:
45
+ @functools.wraps(cm)
46
+ def f(*args: Any, **kwargs: Any) -> _AwaitableAsyncContextManager[R]:
47
+ return _AwaitableAsyncContextManager(cm(*args, **kwargs))
48
+
49
+ return f
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.5.0
3
+ Version: 0.5.1
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
@@ -155,6 +155,25 @@ U = FoamFieldFile(Path(my_pitz) / "0/U")
155
155
  print(U.internal_field)
156
156
  ```
157
157
 
158
+ ### Run an optimization loop in parallel
159
+
160
+ ```python
161
+ import os
162
+ from pathlib import Path
163
+ from foamlib import AsyncFoamCase
164
+ from scipy.optimize import differential_evolution
165
+
166
+ base = AsyncFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
167
+
168
+ async def cost(x):
169
+ async with base.clone() as clone:
170
+ clone[0]["U"].boundary_field["inlet"].value = [x[0], 0, 0]
171
+ await clone.run()
172
+ return abs(clone[-1]["U"].internal_field[0][0])
173
+
174
+ result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
175
+ ```
176
+
158
177
  ## Documentation
159
178
 
160
179
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -1,12 +1,12 @@
1
- foamlib/__init__.py,sha256=VR4c4hlAQ8I180EqNsDls6vyYKbnOScayXFOr2fKAEk,392
1
+ foamlib/__init__.py,sha256=bRzkPtbKy1RniCL9eYinI6Yc8g8utDoXrPV6hFimMZY,392
2
2
  foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  foamlib/_cases/__init__.py,sha256=C0mpRu7c-X-4uVMKmVrZhwIyhBNyvUoCv0o-BQ72RC0,236
4
- foamlib/_cases/_async.py,sha256=QVZaYJCOaUgt0_xI_cYsZAJcdfpdZoYnh5dojTjMX08,7816
4
+ foamlib/_cases/_async.py,sha256=um1nrAjGcT8Vp1hz8XvC_87xVBjfeU3MrpnuzAHN5GY,8251
5
5
  foamlib/_cases/_base.py,sha256=1CUkkK4afBxDgP79dmho97WJdj-GLgYhnrCSf_52Eao,6604
6
6
  foamlib/_cases/_recipes.py,sha256=UTFnVuTvEf-9wn-Fr30a9wOOmyOxvWeDhbqBhdtbhzA,9692
7
7
  foamlib/_cases/_subprocess.py,sha256=CfUy_LrqLnMLR9FHINqInCd3soN6eYvonwZ30epiLu8,2234
8
- foamlib/_cases/_sync.py,sha256=JQAkKXwgdhR_drGy7gadun1XPGjNddKR7Y07HK-TpoA,5964
9
- foamlib/_cases/_util.py,sha256=4jlv8pe2Ro1-ZtpY5DtR2B1Vpbj84SlqH_HzwGYPoH4,861
8
+ foamlib/_cases/_sync.py,sha256=R32Ea4BTOPq-x9OIndObcYZACF8e0RnzB4TywNvDxbA,6733
9
+ foamlib/_cases/_util.py,sha256=BpPW_91bFLzV3b4r50u-2pGMR3tfQQfY2xqToOHCUBk,1204
10
10
  foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
11
11
  foamlib/_files/_base.py,sha256=zaFDjLE6jB7WtGWk8hfKusjLtlGu6CZV16AHJpRUibs,1929
12
12
  foamlib/_files/_files.py,sha256=6Fdrc0lksFK99i1a6wEsBi-BtYgSQnUc5s10h2LQ9ew,16091
@@ -14,8 +14,8 @@ foamlib/_files/_io.py,sha256=f_tYI7AqaFsQ8mtK__fEoIUqpYb3YmrI8X5D8updmNM,2084
14
14
  foamlib/_files/_parsing.py,sha256=8V2CKZ45mKE3f9fP8lAfexIdhPGrq7elIZkpBkkGB6Q,8773
15
15
  foamlib/_files/_serialization.py,sha256=pb8_cIVgRhGS_ZV2p3x8p5_lK1SS6xzQHscAYYuOgFY,3407
16
16
  foamlib/_files/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
17
- foamlib-0.5.0.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
18
- foamlib-0.5.0.dist-info/METADATA,sha256=pecBt-ku69uh-HjSN02M40tCqhT22L-sZ9Fcf6AezwM,5759
19
- foamlib-0.5.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
20
- foamlib-0.5.0.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
21
- foamlib-0.5.0.dist-info/RECORD,,
17
+ foamlib-0.5.1.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
18
+ foamlib-0.5.1.dist-info/METADATA,sha256=4Fq_okfGu-zY0IGrcUCD5IuuJxAESwNtr0EgXD0NzF8,6338
19
+ foamlib-0.5.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
20
+ foamlib-0.5.1.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
21
+ foamlib-0.5.1.dist-info/RECORD,,