foamlib 0.6.6__py3-none-any.whl → 0.6.8__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.6.6"
3
+ __version__ = "0.6.8"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
foamlib/_cases/_async.py CHANGED
@@ -1,9 +1,8 @@
1
1
  import asyncio
2
2
  import multiprocessing
3
- import os
4
3
  import sys
5
4
  from contextlib import asynccontextmanager
6
- from typing import Any, Callable, Optional, TypeVar, Union, overload
5
+ from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, overload
7
6
 
8
7
  if sys.version_info >= (3, 9):
9
8
  from collections.abc import (
@@ -29,6 +28,9 @@ from ._run import FoamCaseRunBase
29
28
  from ._subprocess import run_async
30
29
  from ._util import ValuedGenerator, awaitableasynccontextmanager
31
30
 
31
+ if TYPE_CHECKING:
32
+ import os
33
+
32
34
  X = TypeVar("X")
33
35
  Y = TypeVar("Y")
34
36
 
@@ -101,7 +103,7 @@ class AsyncFoamCase(FoamCaseRunBase):
101
103
  async def _rmtree(
102
104
  path: Union["os.PathLike[str]", str], ignore_errors: bool = False
103
105
  ) -> None:
104
- await aioshutil.rmtree(path, ignore_errors=ignore_errors) # type: ignore [call-arg]
106
+ await aioshutil.rmtree(path, ignore_errors=ignore_errors)
105
107
 
106
108
  @staticmethod
107
109
  async def _copytree(
@@ -138,8 +140,11 @@ class AsyncFoamCase(FoamCaseRunBase):
138
140
  ret = super().__getitem__(index)
139
141
  if isinstance(ret, FoamCaseBase.TimeDirectory):
140
142
  return AsyncFoamCase.TimeDirectory(ret)
141
- else:
142
- return [AsyncFoamCase.TimeDirectory(r) for r in ret]
143
+ return [AsyncFoamCase.TimeDirectory(r) for r in ret]
144
+
145
+ async def _prepare(self, *, check: bool = True, log: bool = True) -> None:
146
+ for coro in self._prepare_calls(check=check, log=log):
147
+ await coro
143
148
 
144
149
  async def run(
145
150
  self,
@@ -188,7 +193,7 @@ class AsyncFoamCase(FoamCaseRunBase):
188
193
  @asynccontextmanager
189
194
  async def copy(
190
195
  self, dst: Optional[Union["os.PathLike[str]", str]] = None
191
- ) -> "AsyncGenerator[Self]":
196
+ ) -> AsyncGenerator[Self, None]:
192
197
  """
193
198
  Make a copy of this case.
194
199
 
@@ -209,7 +214,7 @@ class AsyncFoamCase(FoamCaseRunBase):
209
214
  @asynccontextmanager
210
215
  async def clone(
211
216
  self, dst: Optional[Union["os.PathLike[str]", str]] = None
212
- ) -> "AsyncGenerator[Self]":
217
+ ) -> AsyncGenerator[Self, None]:
213
218
  """
214
219
  Clone this case (make a clean copy).
215
220
 
foamlib/_cases/_base.py CHANGED
@@ -1,8 +1,8 @@
1
- import os
2
1
  import shutil
3
2
  import sys
4
3
  from pathlib import Path
5
4
  from typing import (
5
+ TYPE_CHECKING,
6
6
  Optional,
7
7
  Union,
8
8
  overload,
@@ -23,6 +23,9 @@ else:
23
23
 
24
24
  from .._files import FoamFieldFile, FoamFile
25
25
 
26
+ if TYPE_CHECKING:
27
+ import os
28
+
26
29
 
27
30
  class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
28
31
  def __init__(self, path: Union["os.PathLike[str]", str] = Path()):
@@ -57,18 +60,16 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
57
60
  def __getitem__(self, key: str) -> FoamFieldFile:
58
61
  if (self.path / f"{key}.gz").is_file() and not (self.path / key).is_file():
59
62
  return FoamFieldFile(self.path / f"{key}.gz")
60
- else:
61
- return FoamFieldFile(self.path / key)
63
+ return FoamFieldFile(self.path / key)
62
64
 
63
65
  def __contains__(self, obj: object) -> bool:
64
66
  if isinstance(obj, FoamFieldFile):
65
67
  return obj.path.parent == self.path and obj.path.is_file()
66
- elif isinstance(obj, str):
68
+ if isinstance(obj, str):
67
69
  return (self.path / obj).is_file() or (
68
70
  self.path / f"{obj}.gz"
69
71
  ).is_file()
70
- else:
71
- return False
72
+ return False
72
73
 
73
74
  def __iter__(self) -> Iterator[FoamFieldFile]:
74
75
  for p in self.path.iterdir():
@@ -124,7 +125,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
124
125
  ) -> Union["FoamCaseBase.TimeDirectory", Sequence["FoamCaseBase.TimeDirectory"]]:
125
126
  if isinstance(index, str):
126
127
  return FoamCaseBase.TimeDirectory(self.path / index)
127
- elif isinstance(index, float):
128
+ if isinstance(index, float):
128
129
  for time in self._times:
129
130
  if time.time == index:
130
131
  return time
foamlib/_cases/_run.py CHANGED
@@ -115,6 +115,12 @@ class FoamCaseRunBase(FoamCaseBase):
115
115
  def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Any:
116
116
  raise NotImplementedError
117
117
 
118
+ @abstractmethod
119
+ def _prepare(
120
+ self, *, check: bool = True, log: bool = True
121
+ ) -> Union[None, Coroutine[None, None, None]]:
122
+ raise NotImplementedError
123
+
118
124
  @abstractmethod
119
125
  def run(
120
126
  self,
@@ -208,6 +214,18 @@ class FoamCaseRunBase(FoamCaseBase):
208
214
 
209
215
  return script
210
216
 
217
+ def __prepare_script(self) -> Optional[Path]:
218
+ """Return the path to the Allrun.pre script, or None if no prepare script is found."""
219
+ script = self.path / "Allrun.pre"
220
+
221
+ if not script.is_file():
222
+ return None
223
+
224
+ if sys.argv and Path(sys.argv[0]).absolute() == script.absolute():
225
+ return None
226
+
227
+ return script
228
+
211
229
  def __run_script(self, *, parallel: Optional[bool]) -> Optional[Path]:
212
230
  """Return the path to the (All)run script, or None if no run script is found."""
213
231
  run = self.path / "run"
@@ -256,8 +274,7 @@ class FoamCaseRunBase(FoamCaseBase):
256
274
  env["DYLD_LIBRARY_PATH"] = env["FOAM_LD_LIBRARY_PATH"]
257
275
 
258
276
  return env
259
- else:
260
- return None
277
+ return None
261
278
 
262
279
  @contextmanager
263
280
  def __output(
@@ -267,10 +284,7 @@ class FoamCaseRunBase(FoamCaseBase):
267
284
  if isinstance(cmd, str):
268
285
  name = shlex.split(cmd)[0]
269
286
  else:
270
- if isinstance(cmd[0], os.PathLike):
271
- name = Path(cmd[0]).name
272
- else:
273
- name = cmd[0]
287
+ name = Path(cmd[0]).name if isinstance(cmd[0], os.PathLike) else cmd[0]
274
288
 
275
289
  with (self.path / f"log.{name}").open("ab") as stdout:
276
290
  yield stdout, STDOUT
@@ -341,6 +355,15 @@ class FoamCaseRunBase(FoamCaseBase):
341
355
  ) -> Generator[Any, None, None]:
342
356
  yield self.run(["reconstructPar"], cpus=0, check=check, log=log)
343
357
 
358
+ def _prepare_calls(self, *, check: bool, log: bool) -> Generator[Any, None, None]:
359
+ script_path = self.__prepare_script()
360
+
361
+ if script_path is not None:
362
+ yield self.run([script_path], log=log, check=check)
363
+
364
+ elif (self.path / "system" / "blockMeshDict").is_file():
365
+ yield self.block_mesh(check=check, log=log)
366
+
344
367
  def _run_calls(
345
368
  self,
346
369
  cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
@@ -406,12 +429,11 @@ class FoamCaseRunBase(FoamCaseBase):
406
429
  )
407
430
 
408
431
  else:
432
+ yield self._prepare(check=check, log=log)
433
+
409
434
  if not self and (self.path / "0.orig").is_dir():
410
435
  yield self.restore_0_dir()
411
436
 
412
- if (self.path / "system" / "blockMeshDict").is_file():
413
- yield self.block_mesh(check=check)
414
-
415
437
  if parallel is None:
416
438
  parallel = (
417
439
  (cpus is not None and cpus > 1)
foamlib/_cases/_slurm.py CHANGED
@@ -1,7 +1,6 @@
1
- import os
2
1
  import shutil
3
2
  import sys
4
- from typing import Any, Optional, Union
3
+ from typing import TYPE_CHECKING, Any, Optional, Union
5
4
 
6
5
  if sys.version_info >= (3, 9):
7
6
  from collections.abc import Sequence
@@ -11,6 +10,9 @@ else:
11
10
  from ._async import AsyncFoamCase
12
11
  from ._subprocess import run_async
13
12
 
13
+ if TYPE_CHECKING:
14
+ import os
15
+
14
16
 
15
17
  class AsyncSlurmFoamCase(AsyncFoamCase):
16
18
  """An asynchronous OpenFOAM case that launches jobs on a Slurm cluster."""
@@ -1,9 +1,11 @@
1
1
  import asyncio
2
- import os
3
2
  import subprocess
4
3
  import sys
5
4
  from io import BytesIO
6
- from typing import IO, Optional, Union
5
+ from typing import IO, TYPE_CHECKING, Optional, Union
6
+
7
+ if TYPE_CHECKING:
8
+ import os
7
9
 
8
10
  if sys.version_info >= (3, 9):
9
11
  from collections.abc import Mapping, Sequence
@@ -18,7 +20,7 @@ class CalledProcessError(subprocess.CalledProcessError):
18
20
  if self.stderr:
19
21
  if isinstance(self.stderr, bytes):
20
22
  return super().__str__() + "\n" + self.stderr.decode()
21
- elif isinstance(self.stderr, str):
23
+ if isinstance(self.stderr, str):
22
24
  return super().__str__() + "\n" + self.stderr
23
25
  return super().__str__()
24
26
 
foamlib/_cases/_sync.py CHANGED
@@ -1,8 +1,7 @@
1
- import os
2
1
  import shutil
3
2
  import sys
4
3
  from types import TracebackType
5
- from typing import Any, Callable, Optional, Type, Union, overload
4
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Type, Union, overload
6
5
 
7
6
  if sys.version_info >= (3, 9):
8
7
  from collections.abc import Collection, Sequence
@@ -20,6 +19,9 @@ from ._run import FoamCaseRunBase
20
19
  from ._subprocess import run_sync
21
20
  from ._util import ValuedGenerator
22
21
 
22
+ if TYPE_CHECKING:
23
+ import os
24
+
23
25
 
24
26
  class FoamCase(FoamCaseRunBase):
25
27
  """
@@ -44,8 +46,6 @@ class FoamCase(FoamCaseRunBase):
44
46
  for _ in calls:
45
47
  pass
46
48
 
47
- print(calls.value)
48
-
49
49
  return calls.value
50
50
 
51
51
  @staticmethod
@@ -89,8 +89,7 @@ class FoamCase(FoamCaseRunBase):
89
89
  ret = super().__getitem__(index)
90
90
  if isinstance(ret, FoamCaseBase.TimeDirectory):
91
91
  return FoamCase.TimeDirectory(ret)
92
- else:
93
- return [FoamCase.TimeDirectory(r) for r in ret]
92
+ return [FoamCase.TimeDirectory(r) for r in ret]
94
93
 
95
94
  def __enter__(self) -> Self:
96
95
  return self
@@ -112,6 +111,10 @@ class FoamCase(FoamCaseRunBase):
112
111
  for _ in self._clean_calls(check=check):
113
112
  pass
114
113
 
114
+ def _prepare(self, *, check: bool = True, log: bool = True) -> None:
115
+ for _ in self._prepare_calls(check=check, log=log):
116
+ pass
117
+
115
118
  def run(
116
119
  self,
117
120
  cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
foamlib/_files/_base.py CHANGED
@@ -1,17 +1,15 @@
1
1
  import sys
2
2
  from dataclasses import dataclass
3
- from typing import Dict, NamedTuple, Optional, Tuple, Union
3
+ from typing import TYPE_CHECKING, Dict, NamedTuple, Optional, Tuple, Union
4
+
5
+ if TYPE_CHECKING:
6
+ import numpy as np
4
7
 
5
8
  if sys.version_info >= (3, 9):
6
9
  from collections.abc import Mapping, Sequence
7
10
  else:
8
11
  from typing import Mapping, Sequence
9
12
 
10
- try:
11
- import numpy as np
12
- except ModuleNotFoundError:
13
- pass
14
-
15
13
 
16
14
  class FoamFileBase:
17
15
  class DimensionSet(NamedTuple):
foamlib/_files/_files.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import sys
2
- from typing import Any, Optional, Tuple, Union, cast
2
+ from typing import TYPE_CHECKING, Any, Optional, Tuple, Union, cast
3
3
 
4
4
  if sys.version_info >= (3, 8):
5
5
  from typing import Literal
@@ -16,10 +16,8 @@ from ._io import FoamFileIO
16
16
  from ._serialization import Kind, dumpb
17
17
  from ._util import is_sequence
18
18
 
19
- try:
19
+ if TYPE_CHECKING:
20
20
  import numpy as np
21
- except ModuleNotFoundError:
22
- pass
23
21
 
24
22
 
25
23
  class FoamFile(
@@ -86,7 +84,7 @@ class FoamFile(
86
84
 
87
85
  def as_dict(self) -> FoamFileBase._Dict:
88
86
  """Return a nested dict representation of the dictionary."""
89
- ret = self._file.as_dict()
87
+ ret = self._file.as_dict(include_header=True)
90
88
 
91
89
  for k in self._keywords:
92
90
  assert isinstance(ret, dict)
@@ -172,8 +170,7 @@ class FoamFile(
172
170
 
173
171
  if value is ...:
174
172
  return FoamFile.SubDict(self, keywords)
175
- else:
176
- return value
173
+ return value
177
174
 
178
175
  def __setitem__(
179
176
  self, keywords: Optional[Union[str, Tuple[str, ...]]], data: "FoamFile._SetData"
@@ -294,15 +291,10 @@ class FoamFile(
294
291
 
295
292
  def _iter(self, keywords: Tuple[str, ...] = ()) -> Iterator[Optional[str]]:
296
293
  _, parsed = self._read()
297
-
298
- yield from (
299
- k[-1] if k else None
300
- for k in parsed
301
- if k != ("FoamFile",) and k[:-1] == keywords
302
- )
294
+ yield from (k[-1] if k else None for k in parsed if k[:-1] == keywords)
303
295
 
304
296
  def __iter__(self) -> Iterator[Optional[str]]:
305
- return self._iter()
297
+ yield from (k for k in self._iter() if k != "FoamFile")
306
298
 
307
299
  def __contains__(self, keywords: object) -> bool:
308
300
  if not keywords:
@@ -328,11 +320,16 @@ class FoamFile(
328
320
  def __fspath__(self) -> str:
329
321
  return str(self.path)
330
322
 
331
- def as_dict(self) -> FoamFileBase._File:
332
- """Return a nested dict representation of the file."""
323
+ def as_dict(self, *, include_header: bool = False) -> FoamFileBase._File:
324
+ """
325
+ Return a nested dict representation of the file.
326
+
327
+ :param include_header: Whether to include the "FoamFile" header in the output.
328
+ """
333
329
  _, parsed = self._read()
334
330
  d = parsed.as_dict()
335
- del d["FoamFile"]
331
+ if not include_header:
332
+ d.pop("FoamFile", None)
336
333
  return d
337
334
 
338
335
 
foamlib/_files/_io.py CHANGED
@@ -1,10 +1,10 @@
1
1
  import gzip
2
- import os
3
2
  import sys
4
3
  from copy import deepcopy
5
4
  from pathlib import Path
6
5
  from types import TracebackType
7
6
  from typing import (
7
+ TYPE_CHECKING,
8
8
  Optional,
9
9
  Tuple,
10
10
  Type,
@@ -18,6 +18,9 @@ else:
18
18
 
19
19
  from ._parsing import Parsed
20
20
 
21
+ if TYPE_CHECKING:
22
+ import os
23
+
21
24
 
22
25
  class FoamFileIO:
23
26
  def __init__(self, path: Union["os.PathLike[str]", str]) -> None:
@@ -63,8 +66,7 @@ class FoamFileIO:
63
66
  if self.__contents is None:
64
67
  if missing_ok:
65
68
  return b"", Parsed(b"")
66
- else:
67
- raise FileNotFoundError(self.path)
69
+ raise FileNotFoundError(self.path)
68
70
 
69
71
  if self.__parsed is None:
70
72
  parsed = Parsed(self.__contents)
@@ -95,18 +95,35 @@ def _unpack_binary_field(
95
95
  return [all]
96
96
 
97
97
 
98
+ _IDENTCHARS = identchars + "$"
99
+ _IDENTBODYCHARS = (
100
+ printables.replace(";", "")
101
+ .replace("{", "")
102
+ .replace("}", "")
103
+ .replace("[", "")
104
+ .replace("]", "")
105
+ )
106
+
98
107
  _SWITCH = (
99
- Keyword("yes") | Keyword("true") | Keyword("on") | Keyword("y") | Keyword("t")
108
+ Keyword("yes", _IDENTBODYCHARS)
109
+ | Keyword("true", _IDENTBODYCHARS)
110
+ | Keyword("on", _IDENTBODYCHARS)
111
+ | Keyword("y", _IDENTBODYCHARS)
112
+ | Keyword("t", _IDENTBODYCHARS)
100
113
  ).set_parse_action(lambda: True) | (
101
- Keyword("no") | Keyword("false") | Keyword("off") | Keyword("n") | Keyword("f")
114
+ Keyword("no", _IDENTBODYCHARS)
115
+ | Keyword("false", _IDENTBODYCHARS)
116
+ | Keyword("off", _IDENTBODYCHARS)
117
+ | Keyword("n", _IDENTBODYCHARS)
118
+ | Keyword("f", _IDENTBODYCHARS)
102
119
  ).set_parse_action(lambda: False)
103
120
  _DIMENSIONS = (
104
121
  Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
105
122
  ).set_parse_action(lambda tks: FoamFileBase.DimensionSet(*tks))
106
123
  _TENSOR = _list_of(common.number) | common.number
107
124
  _IDENTIFIER = Combine(
108
- Word(identchars + "$", printables, exclude_chars="[{(;)}]")
109
- + Opt(Literal("(") + Word(printables, exclude_chars="[{(;)}]") + Literal(")"))
125
+ Word(_IDENTCHARS, _IDENTBODYCHARS, exclude_chars="()")
126
+ + Opt(Literal("(") + Word(_IDENTBODYCHARS, exclude_chars="()") + Literal(")"))
110
127
  )
111
128
  _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
112
129
  lambda tks: FoamFileBase.Dimensioned(*reversed(tks.as_list()))
@@ -35,7 +35,7 @@ def dumpb(
35
35
  if numpy and isinstance(data, np.ndarray):
36
36
  return dumpb(data.tolist(), kind=kind)
37
37
 
38
- elif isinstance(data, Mapping):
38
+ if isinstance(data, Mapping):
39
39
  entries = []
40
40
  for k, v in data.items():
41
41
  b = dumpb(v, kind=kind)
@@ -48,12 +48,12 @@ def dumpb(
48
48
 
49
49
  return b" ".join(entries)
50
50
 
51
- elif isinstance(data, FoamFileBase.DimensionSet) or (
51
+ if isinstance(data, FoamFileBase.DimensionSet) or (
52
52
  kind == Kind.DIMENSIONS and is_sequence(data) and len(data) == 7
53
53
  ):
54
54
  return b"[" + b" ".join(dumpb(v) for v in data) + b"]"
55
55
 
56
- elif (kind == Kind.FIELD or kind == Kind.BINARY_FIELD) and (
56
+ if (kind == Kind.FIELD or kind == Kind.BINARY_FIELD) and (
57
57
  isinstance(data, (int, float))
58
58
  or is_sequence(data)
59
59
  and data
@@ -62,7 +62,7 @@ def dumpb(
62
62
  ):
63
63
  return b"uniform " + dumpb(data, kind=Kind.SINGLE_ENTRY)
64
64
 
65
- elif (kind == Kind.FIELD or kind == Kind.BINARY_FIELD) and is_sequence(data):
65
+ if (kind == Kind.FIELD or kind == Kind.BINARY_FIELD) and is_sequence(data):
66
66
  if isinstance(data[0], (int, float)):
67
67
  tensor_kind = b"scalar"
68
68
  elif len(data[0]) == 3:
@@ -88,10 +88,10 @@ def dumpb(
88
88
 
89
89
  return b"nonuniform List<" + tensor_kind + b"> " + dumpb(len(data)) + contents
90
90
 
91
- elif kind != Kind.SINGLE_ENTRY and isinstance(data, tuple):
91
+ if kind != Kind.SINGLE_ENTRY and isinstance(data, tuple):
92
92
  return b" ".join(dumpb(v) for v in data)
93
93
 
94
- elif isinstance(data, FoamFileBase.Dimensioned):
94
+ if isinstance(data, FoamFileBase.Dimensioned):
95
95
  if data.name is not None:
96
96
  return (
97
97
  dumpb(data.name)
@@ -100,20 +100,18 @@ def dumpb(
100
100
  + b" "
101
101
  + dumpb(data.value, kind=Kind.SINGLE_ENTRY)
102
102
  )
103
- else:
104
- return (
105
- dumpb(data.dimensions, kind=Kind.DIMENSIONS)
106
- + b" "
107
- + dumpb(data.value, kind=Kind.SINGLE_ENTRY)
108
- )
103
+ return (
104
+ dumpb(data.dimensions, kind=Kind.DIMENSIONS)
105
+ + b" "
106
+ + dumpb(data.value, kind=Kind.SINGLE_ENTRY)
107
+ )
109
108
 
110
- elif is_sequence(data):
109
+ if is_sequence(data):
111
110
  return b"(" + b" ".join(dumpb(v, kind=Kind.SINGLE_ENTRY) for v in data) + b")"
112
111
 
113
- elif data is True:
112
+ if data is True:
114
113
  return b"yes"
115
- elif data is False:
114
+ if data is False:
116
115
  return b"no"
117
116
 
118
- else:
119
- return str(data).encode("latin-1")
117
+ return str(data).encode("latin-1")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.6
3
+ Version: 0.6.8
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
@@ -37,12 +37,13 @@ Requires-Dist: foamlib[typing] ; extra == 'dev'
37
37
  Requires-Dist: foamlib[docs] ; extra == 'dev'
38
38
  Provides-Extra: docs
39
39
  Requires-Dist: foamlib[numpy] ; extra == 'docs'
40
- Requires-Dist: sphinx <9,>=7 ; extra == 'docs'
40
+ Requires-Dist: sphinx <9,>=5 ; extra == 'docs'
41
41
  Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
42
42
  Provides-Extra: lint
43
43
  Requires-Dist: ruff ; extra == 'lint'
44
44
  Provides-Extra: numpy
45
45
  Requires-Dist: numpy <3,>=1 ; extra == 'numpy'
46
+ Requires-Dist: numpy <3,>=1.25.0 ; (python_version >= "3.10") and extra == 'numpy'
46
47
  Provides-Extra: test
47
48
  Requires-Dist: foamlib[numpy] ; extra == 'test'
48
49
  Requires-Dist: pytest <9,>=7 ; extra == 'test'
@@ -52,38 +53,50 @@ Provides-Extra: typing
52
53
  Requires-Dist: foamlib[test] ; extra == 'typing'
53
54
  Requires-Dist: mypy <2,>=1 ; extra == 'typing'
54
55
 
55
- [<img alt="foamlib" src="https://github.com/gerlero/foamlib/raw/main/logo.png" height="50">](https://github.com/gerlero/foamlib)
56
+ [<img alt="foamlib" src="https://github.com/gerlero/foamlib/raw/main/logo.png" height="65">](https://github.com/gerlero/foamlib)
56
57
 
57
58
  [![Documentation](https://img.shields.io/readthedocs/foamlib)](https://foamlib.readthedocs.io/)
58
59
  [![CI](https://github.com/gerlero/foamlib/actions/workflows/ci.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
59
60
  [![Codecov](https://codecov.io/gh/gerlero/foamlib/branch/main/graph/badge.svg)](https://codecov.io/gh/gerlero/foamlib)
60
61
  [![Checked with mypy](http://www.mypy-lang.org/static/mypy_badge.svg)](http://mypy-lang.org/)
61
62
  [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
63
+ [![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)
64
+ [![Publish](https://github.com/gerlero/foamlib/actions/workflows/pypi-publish.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/pypi-publish.yml)
62
65
  [![PyPI](https://img.shields.io/pypi/v/foamlib)](https://pypi.org/project/foamlib/)
66
+ [![Conda Version](https://img.shields.io/conda/vn/conda-forge/foamlib)](https://anaconda.org/conda-forge/foamlib)
63
67
  [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/foamlib)](https://pypi.org/project/foamlib/)
64
68
  ![OpenFOAM](https://img.shields.io/badge/openfoam-.com%20|%20.org-informational)
69
+ [![Docker](https://github.com/gerlero/foamlib/actions/workflows/docker.yml/badge.svg)](https://github.com/gerlero/foamlib/actions/workflows/docker.yml)
65
70
  [![Docker image](https://img.shields.io/badge/docker%20image-microfluidica%2Ffoamlib-0085a0)](https://hub.docker.com/r/microfluidica/foamlib/)
66
71
 
72
+ ## 👋 Basics
73
+
67
74
  **foamlib** provides a simple, modern and ergonomic Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
68
75
 
69
- It offers the following classes:
76
+ It offers the following Python classes:
70
77
 
71
78
  * [`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 ASCII and binary field formats (with or without compression).
72
79
  * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
73
80
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
74
81
  * [`AsyncSlurmFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncSlurmFoamCase): subclass of `AsyncFoamCase` used for running cases on a Slurm cluster.
75
82
 
76
- ## Get started
83
+ ## ☑️ Get started
77
84
 
78
- ### Install
85
+ ### 📦 Install
79
86
 
80
- Install with [pip](https://pypi.org/project/pip/):
87
+ * With [pip](https://pypi.org/project/pip/):
81
88
 
82
- ```bash
83
- pip install foamlib
84
- ```
89
+ ```bash
90
+ pip install foamlib
91
+ ```
92
+
93
+ * With [conda](https://docs.conda.io/en/latest/):
94
+
95
+ ```bash
96
+ conda install -c conda-forge foamlib
97
+ ```
85
98
 
86
- ### Clone a case
99
+ ### 🐑 Clone a case
87
100
 
88
101
  ```python
89
102
  import os
@@ -95,13 +108,13 @@ pitz_tutorial = FoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/si
95
108
  my_pitz = pitz_tutorial.clone("myPitz")
96
109
  ```
97
110
 
98
- ### Run the case
111
+ ### 🏃 Run the case
99
112
 
100
113
  ```python
101
114
  my_pitz.run()
102
115
  ```
103
116
 
104
- ### Access the results
117
+ ### 🔎 Access the results
105
118
 
106
119
  ```python
107
120
  latest_time = my_pitz[-1]
@@ -113,19 +126,19 @@ print(p.internal_field)
113
126
  print(U.internal_field)
114
127
  ```
115
128
 
116
- ### Clean the case
129
+ ### 🧹 Clean the case
117
130
 
118
131
  ```python
119
132
  my_pitz.clean()
120
133
  ```
121
134
 
122
- ### Edit the `controlDict` file
135
+ ### ⚙️ Edit the `controlDict` file
123
136
 
124
137
  ```python
125
138
  my_pitz.control_dict["writeInterval"] = 10
126
139
  ```
127
140
 
128
- ### Make multiple file reads and writes in a single go
141
+ ### 📝 Make multiple file reads and writes in a single go
129
142
 
130
143
  ```python
131
144
  with my_pitz.fv_schemes as f:
@@ -133,7 +146,7 @@ with my_pitz.fv_schemes as f:
133
146
  f["snGradSchemes"]["default"] = "uncorrected"
134
147
  ```
135
148
 
136
- ### Run a case asynchronously
149
+ ### Run a case asynchronously
137
150
 
138
151
  ```python
139
152
  import asyncio
@@ -146,7 +159,7 @@ async def run_case():
146
159
  asyncio.run(run_case())
147
160
  ```
148
161
 
149
- ### Parse a field using the [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile) class directly
162
+ ### 🔢 Parse a field using the [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile) class directly
150
163
 
151
164
  ```python
152
165
  from foamlib import FoamFieldFile
@@ -156,7 +169,7 @@ U = FoamFieldFile(Path(my_pitz) / "0/U")
156
169
  print(U.internal_field)
157
170
  ```
158
171
 
159
- ### Run an optimization loop in parallel
172
+ ### 🔁 Run an optimization loop in parallel
160
173
 
161
174
  ```python
162
175
  import os
@@ -176,7 +189,7 @@ async def cost(x):
176
189
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
177
190
  ```
178
191
 
179
- ### Use it to create a `run` (or `clean`) script
192
+ ### 📄 Use it to create a `run` (or `clean`) script
180
193
 
181
194
  ```python
182
195
  #!/usr/bin/env python3
@@ -188,6 +201,6 @@ case = FoamCase(Path(__file__).parent)
188
201
  case.run()
189
202
  ```
190
203
 
191
- ## Documentation
204
+ ## 📘 Documentation
192
205
 
193
206
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -0,0 +1,22 @@
1
+ foamlib/__init__.py,sha256=8RKFs674VNgR5xxGbE-3Ua2cFDEsR7TfOipMUBximEA,486
2
+ foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ foamlib/_cases/__init__.py,sha256=wTUHcUgU1CBgpu0cUMtksQ5VKG6B8CFu9xc3dWwsQuo,358
4
+ foamlib/_cases/_async.py,sha256=oQ2XnD9sansJLJDuUl3_vHaUM4L9hq2G97IcRjQAOPY,7920
5
+ foamlib/_cases/_base.py,sha256=HV7ifv60pAhgZdC3s5IxvdrDQ2vvlhzPwyB-80QQLe8,6733
6
+ foamlib/_cases/_run.py,sha256=XFDBr5G1hJo0402RcZClLL_rv0d5jVgq2be25FgZcyQ,14725
7
+ foamlib/_cases/_slurm.py,sha256=pUXcFSrwOK5URsoooiUOJg61X0WLLNHyv7bhUxIb7Bs,2346
8
+ foamlib/_cases/_subprocess.py,sha256=tzsBGe44EnQw3GWqRW1ktEZ4sNQ1sSHdJ_89zIfF5L0,3952
9
+ foamlib/_cases/_sync.py,sha256=ENvn8zseRh6YOjAgqXHzmKhKFYXdzGIW5NoMHS_NKPY,6049
10
+ foamlib/_cases/_util.py,sha256=GNndpqw3Jg_S-Hxzl5vwRgD0czcTNb9NYHMhcfBoMBg,1493
11
+ foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
12
+ foamlib/_files/_base.py,sha256=mrZuk90sdIrSGiOeEtCcEHoz6RK4qUc6GFoDSuo7U3w,1920
13
+ foamlib/_files/_files.py,sha256=i7qBKIgJZs-679TAZPhUfnmHpwUjvR_FNyQULA4aidA,15721
14
+ foamlib/_files/_io.py,sha256=ArD9lgMw-Kjj-wl_5qU6eRdW4lZWyGBIEMWLCNV4a0Y,2467
15
+ foamlib/_files/_parsing.py,sha256=SvPAbNb_xJwOBRGii4oLOUvhj9OdJ8auMIzdx77AYwI,9394
16
+ foamlib/_files/_serialization.py,sha256=JaN6-aTHgRv_i5CSQ14UzH4dSVue5CLaAfJ5kA2ldNA,3341
17
+ foamlib/_files/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
18
+ foamlib-0.6.8.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
19
+ foamlib-0.6.8.dist-info/METADATA,sha256=AcdzWRVqzTM8i_gD4C1S21p17gt7XeidFON9mzyzQdw,7741
20
+ foamlib-0.6.8.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
21
+ foamlib-0.6.8.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
22
+ foamlib-0.6.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.1.0)
2
+ Generator: setuptools (75.2.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,22 +0,0 @@
1
- foamlib/__init__.py,sha256=qC9o7n8gVSCw0x-I6eWgOoqp8Fszi4QrL6Ub4ZozWvI,486
2
- foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- foamlib/_cases/__init__.py,sha256=wTUHcUgU1CBgpu0cUMtksQ5VKG6B8CFu9xc3dWwsQuo,358
4
- foamlib/_cases/_async.py,sha256=PYODgIkRLHArMf3wtf34jMx7gBGi-K6hoLEhjYK9bOw,7753
5
- foamlib/_cases/_base.py,sha256=4VWsu22VockyKgU_5tnSMeNrAkMgBKwZo2KGkV65rRQ,6739
6
- foamlib/_cases/_run.py,sha256=SjA-Qux5NhnhcLz7qiph2DcGYpElAAl_PlTGk6kGeuU,13972
7
- foamlib/_cases/_slurm.py,sha256=rhGSFJ-3g72TnAYlhECGfs7P7IkeaHL3ysjXfXLdCCc,2308
8
- foamlib/_cases/_subprocess.py,sha256=Z6GHZ44fIQcOiDO-c7Oqp8pqE8NG7WFP9Mdbf_6WtIU,3916
9
- foamlib/_cases/_sync.py,sha256=5lVMGuesCC--1GX8CL82bXg2o0K7OsZoff2zSOEs5d0,5910
10
- foamlib/_cases/_util.py,sha256=GNndpqw3Jg_S-Hxzl5vwRgD0czcTNb9NYHMhcfBoMBg,1493
11
- foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
12
- foamlib/_files/_base.py,sha256=zaFDjLE6jB7WtGWk8hfKusjLtlGu6CZV16AHJpRUibs,1929
13
- foamlib/_files/_files.py,sha256=1jV52dZqr-g6P0X0lA80OQcr2yMt4O-mAgERcaU4440,15583
14
- foamlib/_files/_io.py,sha256=T8vTEmJDidL03qeX1PUJFND-_vaF0AuC_0lE_zxLz7Q,2447
15
- foamlib/_files/_parsing.py,sha256=DzgJ53QnohRQyLXn2zOs52RIQ7bJ_fS_S2Z7rmRyVK4,9023
16
- foamlib/_files/_serialization.py,sha256=pb8_cIVgRhGS_ZV2p3x8p5_lK1SS6xzQHscAYYuOgFY,3407
17
- foamlib/_files/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
18
- foamlib-0.6.6.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
19
- foamlib-0.6.6.dist-info/METADATA,sha256=qvNj5spqcgV48wAZ14CAUUK6_X5pInV_vIjNJyMOaRY,6866
20
- foamlib-0.6.6.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
21
- foamlib-0.6.6.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
22
- foamlib-0.6.6.dist-info/RECORD,,