foamlib 0.2.4__py3-none-any.whl → 0.2.6__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 +5 -3
- foamlib/_cases.py +46 -85
- foamlib/_dictionaries/__init__.py +1 -1
- foamlib/_dictionaries/_base.py +8 -4
- foamlib/_dictionaries/_files.py +21 -32
- foamlib/_dictionaries/_parsing.py +17 -17
- foamlib/_dictionaries/_serialization.py +14 -23
- foamlib/{_subprocesses.py → _util.py} +48 -14
- {foamlib-0.2.4.dist-info → foamlib-0.2.6.dist-info}/METADATA +9 -10
- foamlib-0.2.6.dist-info/RECORD +14 -0
- foamlib-0.2.4.dist-info/RECORD +0 -14
- {foamlib-0.2.4.dist-info → foamlib-0.2.6.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.2.4.dist-info → foamlib-0.2.6.dist-info}/WHEEL +0 -0
- {foamlib-0.2.4.dist-info → foamlib-0.2.6.dist-info}/top_level.txt +0 -0
foamlib/__init__.py
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
|
1
|
+
"""A Python interface for interacting with OpenFOAM."""
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
__version__ = "0.2.6"
|
4
|
+
|
5
|
+
from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
|
6
|
+
from ._dictionaries import FoamDictionaryBase, FoamFieldFile, FoamFile
|
5
7
|
|
6
8
|
__all__ = [
|
7
9
|
"FoamCase",
|
foamlib/_cases.py
CHANGED
@@ -1,27 +1,34 @@
|
|
1
|
-
import os
|
2
1
|
import asyncio
|
3
2
|
import multiprocessing
|
3
|
+
import os
|
4
4
|
import shutil
|
5
|
-
|
6
|
-
from pathlib import Path
|
5
|
+
import sys
|
7
6
|
from contextlib import asynccontextmanager
|
7
|
+
from pathlib import Path
|
8
8
|
from typing import (
|
9
9
|
Optional,
|
10
10
|
Union,
|
11
|
-
Collection,
|
12
|
-
Mapping,
|
13
|
-
Set,
|
14
|
-
Sequence,
|
15
|
-
AsyncGenerator,
|
16
|
-
Callable,
|
17
|
-
Iterator,
|
18
11
|
overload,
|
19
12
|
)
|
20
13
|
|
14
|
+
if sys.version_info >= (3, 9):
|
15
|
+
from collections.abc import (
|
16
|
+
AsyncGenerator,
|
17
|
+
Callable,
|
18
|
+
Collection,
|
19
|
+
Iterator,
|
20
|
+
Mapping,
|
21
|
+
Sequence,
|
22
|
+
Set,
|
23
|
+
)
|
24
|
+
else:
|
25
|
+
from typing import AbstractSet as Set
|
26
|
+
from typing import AsyncGenerator, Callable, Collection, Iterator, Mapping, Sequence
|
27
|
+
|
21
28
|
import aioshutil
|
22
29
|
|
23
|
-
from .
|
24
|
-
from .
|
30
|
+
from ._dictionaries import FoamFieldFile, FoamFile
|
31
|
+
from ._util import CalledProcessError, is_sequence, run_process, run_process_async
|
25
32
|
|
26
33
|
|
27
34
|
class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
@@ -46,16 +53,12 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
46
53
|
|
47
54
|
@property
|
48
55
|
def time(self) -> float:
|
49
|
-
"""
|
50
|
-
The time that corresponds to this directory.
|
51
|
-
"""
|
56
|
+
"""The time that corresponds to this directory."""
|
52
57
|
return float(self.path.name)
|
53
58
|
|
54
59
|
@property
|
55
60
|
def name(self) -> str:
|
56
|
-
"""
|
57
|
-
The name of this time directory.
|
58
|
-
"""
|
61
|
+
"""The name of this time directory."""
|
59
62
|
return self.path.name
|
60
63
|
|
61
64
|
def __getitem__(self, key: str) -> FoamFieldFile:
|
@@ -132,7 +135,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
132
135
|
has_decompose_par_dict = (self.path / "system" / "decomposeParDict").is_file()
|
133
136
|
has_block_mesh_dict = (self.path / "system" / "blockMeshDict").is_file()
|
134
137
|
|
135
|
-
paths
|
138
|
+
paths = set()
|
136
139
|
|
137
140
|
for p in self.path.iterdir():
|
138
141
|
if p.is_dir():
|
@@ -164,9 +167,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
164
167
|
return ignore
|
165
168
|
|
166
169
|
def _clean_script(self) -> Optional[Path]:
|
167
|
-
"""
|
168
|
-
Return the path to the (All)clean script, or None if no clean script is found.
|
169
|
-
"""
|
170
|
+
"""Return the path to the (All)clean script, or None if no clean script is found."""
|
170
171
|
clean = self.path / "clean"
|
171
172
|
all_clean = self.path / "Allclean"
|
172
173
|
|
@@ -178,9 +179,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
178
179
|
return None
|
179
180
|
|
180
181
|
def _run_script(self, *, parallel: Optional[bool]) -> Optional[Path]:
|
181
|
-
"""
|
182
|
-
Return the path to the (All)run script, or None if no run script is found.
|
183
|
-
"""
|
182
|
+
"""Return the path to the (All)run script, or None if no run script is found."""
|
184
183
|
run = self.path / "run"
|
185
184
|
run_parallel = self.path / "run-parallel"
|
186
185
|
all_run = self.path / "Allrun"
|
@@ -205,9 +204,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
205
204
|
return None
|
206
205
|
|
207
206
|
def _env(self) -> Mapping[str, str]:
|
208
|
-
"""
|
209
|
-
Return the environment variables for this case.
|
210
|
-
"""
|
207
|
+
"""Return the environment variables for this case."""
|
211
208
|
env = os.environ.copy()
|
212
209
|
env["PWD"] = str(self.path)
|
213
210
|
return env
|
@@ -215,7 +212,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
215
212
|
def _parallel_cmd(
|
216
213
|
self, cmd: Union[Sequence[Union[str, Path]], str, Path]
|
217
214
|
) -> Union[Sequence[Union[str, Path]], str]:
|
218
|
-
if
|
215
|
+
if not is_sequence(cmd):
|
219
216
|
return f"mpiexec -np {self._nprocessors} {cmd} -parallel"
|
220
217
|
else:
|
221
218
|
return [
|
@@ -229,22 +226,16 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
229
226
|
|
230
227
|
@property
|
231
228
|
def name(self) -> str:
|
232
|
-
"""
|
233
|
-
The name of the case.
|
234
|
-
"""
|
229
|
+
"""The name of the case."""
|
235
230
|
return self.path.name
|
236
231
|
|
237
232
|
def file(self, path: Union[Path, str]) -> FoamFile:
|
238
|
-
"""
|
239
|
-
Return a FoamFile object for the given path in the case.
|
240
|
-
"""
|
233
|
+
"""Return a FoamFile object for the given path in the case."""
|
241
234
|
return FoamFile(self.path / path)
|
242
235
|
|
243
236
|
@property
|
244
237
|
def _nsubdomains(self) -> Optional[int]:
|
245
|
-
"""
|
246
|
-
Return the number of subdomains as set in the decomposeParDict, or None if no decomposeParDict is found.
|
247
|
-
"""
|
238
|
+
"""Return the number of subdomains as set in the decomposeParDict, or None if no decomposeParDict is found."""
|
248
239
|
try:
|
249
240
|
nsubdomains = self.decompose_par_dict["numberOfSubdomains"]
|
250
241
|
if not isinstance(nsubdomains, int):
|
@@ -257,16 +248,12 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
257
248
|
|
258
249
|
@property
|
259
250
|
def _nprocessors(self) -> int:
|
260
|
-
"""
|
261
|
-
Return the number of processor directories in the case.
|
262
|
-
"""
|
251
|
+
"""Return the number of processor directories in the case."""
|
263
252
|
return len(list(self.path.glob("processor*")))
|
264
253
|
|
265
254
|
@property
|
266
255
|
def application(self) -> str:
|
267
|
-
"""
|
268
|
-
The application name as set in the controlDict.
|
269
|
-
"""
|
256
|
+
"""The application name as set in the controlDict."""
|
270
257
|
application = self.control_dict["application"]
|
271
258
|
if not isinstance(application, str):
|
272
259
|
raise TypeError(f"application in {self.control_dict} is not a string")
|
@@ -274,51 +261,37 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
274
261
|
|
275
262
|
@property
|
276
263
|
def control_dict(self) -> FoamFile:
|
277
|
-
"""
|
278
|
-
The controlDict file.
|
279
|
-
"""
|
264
|
+
"""The controlDict file."""
|
280
265
|
return self.file("system/controlDict")
|
281
266
|
|
282
267
|
@property
|
283
268
|
def fv_schemes(self) -> FoamFile:
|
284
|
-
"""
|
285
|
-
The fvSchemes file.
|
286
|
-
"""
|
269
|
+
"""The fvSchemes file."""
|
287
270
|
return self.file("system/fvSchemes")
|
288
271
|
|
289
272
|
@property
|
290
273
|
def fv_solution(self) -> FoamFile:
|
291
|
-
"""
|
292
|
-
The fvSolution file.
|
293
|
-
"""
|
274
|
+
"""The fvSolution file."""
|
294
275
|
return self.file("system/fvSolution")
|
295
276
|
|
296
277
|
@property
|
297
278
|
def decompose_par_dict(self) -> FoamFile:
|
298
|
-
"""
|
299
|
-
The decomposeParDict file.
|
300
|
-
"""
|
279
|
+
"""The decomposeParDict file."""
|
301
280
|
return self.file("system/decomposeParDict")
|
302
281
|
|
303
282
|
@property
|
304
283
|
def block_mesh_dict(self) -> FoamFile:
|
305
|
-
"""
|
306
|
-
The blockMeshDict file.
|
307
|
-
"""
|
284
|
+
"""The blockMeshDict file."""
|
308
285
|
return self.file("system/blockMeshDict")
|
309
286
|
|
310
287
|
@property
|
311
288
|
def transport_properties(self) -> FoamFile:
|
312
|
-
"""
|
313
|
-
The transportProperties file.
|
314
|
-
"""
|
289
|
+
"""The transportProperties file."""
|
315
290
|
return self.file("constant/transportProperties")
|
316
291
|
|
317
292
|
@property
|
318
293
|
def turbulence_properties(self) -> FoamFile:
|
319
|
-
"""
|
320
|
-
The turbulenceProperties file.
|
321
|
-
"""
|
294
|
+
"""The turbulenceProperties file."""
|
322
295
|
return self.file("constant/turbulenceProperties")
|
323
296
|
|
324
297
|
def __fspath__(self) -> str:
|
@@ -391,7 +364,7 @@ class FoamCase(FoamCaseBase):
|
|
391
364
|
)
|
392
365
|
except CalledProcessError as e:
|
393
366
|
raise RuntimeError(
|
394
|
-
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr
|
367
|
+
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
|
395
368
|
) from None
|
396
369
|
|
397
370
|
else:
|
@@ -424,21 +397,15 @@ class FoamCase(FoamCaseBase):
|
|
424
397
|
)
|
425
398
|
|
426
399
|
def block_mesh(self, *, check: bool = True) -> None:
|
427
|
-
"""
|
428
|
-
Run blockMesh on this case.
|
429
|
-
"""
|
400
|
+
"""Run blockMesh on this case."""
|
430
401
|
self.run(["blockMesh"], check=check)
|
431
402
|
|
432
403
|
def decompose_par(self, *, check: bool = True) -> None:
|
433
|
-
"""
|
434
|
-
Decompose this case for parallel running.
|
435
|
-
"""
|
404
|
+
"""Decompose this case for parallel running."""
|
436
405
|
self.run(["decomposePar"], check=check)
|
437
406
|
|
438
407
|
def reconstruct_par(self, *, check: bool = True) -> None:
|
439
|
-
"""
|
440
|
-
Reconstruct this case after parallel running.
|
441
|
-
"""
|
408
|
+
"""Reconstruct this case after parallel running."""
|
442
409
|
self.run(["reconstructPar"], check=check)
|
443
410
|
|
444
411
|
def copy(self, dest: Union[Path, str]) -> "FoamCase":
|
@@ -566,7 +533,7 @@ class AsyncFoamCase(FoamCaseBase):
|
|
566
533
|
)
|
567
534
|
except CalledProcessError as e:
|
568
535
|
raise RuntimeError(
|
569
|
-
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr
|
536
|
+
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
|
570
537
|
) from None
|
571
538
|
|
572
539
|
else:
|
@@ -616,21 +583,15 @@ class AsyncFoamCase(FoamCaseBase):
|
|
616
583
|
)
|
617
584
|
|
618
585
|
async def block_mesh(self, *, check: bool = True) -> None:
|
619
|
-
"""
|
620
|
-
Run blockMesh on this case.
|
621
|
-
"""
|
586
|
+
"""Run blockMesh on this case."""
|
622
587
|
await self.run(["blockMesh"], check=check)
|
623
588
|
|
624
589
|
async def decompose_par(self, *, check: bool = True) -> None:
|
625
|
-
"""
|
626
|
-
Decompose this case for parallel running.
|
627
|
-
"""
|
590
|
+
"""Decompose this case for parallel running."""
|
628
591
|
await self.run(["decomposePar"], check=check)
|
629
592
|
|
630
593
|
async def reconstruct_par(self, *, check: bool = True) -> None:
|
631
|
-
"""
|
632
|
-
Reconstruct this case after parallel running.
|
633
|
-
"""
|
594
|
+
"""Reconstruct this case after parallel running."""
|
634
595
|
await self.run(["reconstructPar"], check=check)
|
635
596
|
|
636
597
|
async def copy(self, dest: Union[Path, str]) -> "AsyncFoamCase":
|
foamlib/_dictionaries/_base.py
CHANGED
@@ -1,6 +1,12 @@
|
|
1
|
+
import sys
|
1
2
|
from abc import abstractmethod
|
2
3
|
from dataclasses import dataclass
|
3
|
-
from typing import Dict, NamedTuple, Optional,
|
4
|
+
from typing import Dict, NamedTuple, Optional, Union
|
5
|
+
|
6
|
+
if sys.version_info >= (3, 9):
|
7
|
+
from collections.abc import Sequence
|
8
|
+
else:
|
9
|
+
from typing import Sequence
|
4
10
|
|
5
11
|
|
6
12
|
class FoamDictionaryBase:
|
@@ -37,7 +43,5 @@ class FoamDictionaryBase:
|
|
37
43
|
|
38
44
|
@abstractmethod
|
39
45
|
def as_dict(self) -> _Dict:
|
40
|
-
"""
|
41
|
-
Return a nested dict representation of the dictionary.
|
42
|
-
"""
|
46
|
+
"""Return a nested dict representation of the dictionary."""
|
43
47
|
raise NotImplementedError
|
foamlib/_dictionaries/_files.py
CHANGED
@@ -1,21 +1,22 @@
|
|
1
|
+
import sys
|
1
2
|
from pathlib import Path
|
2
3
|
from typing import (
|
3
4
|
Any,
|
4
|
-
Iterator,
|
5
|
-
Mapping,
|
6
|
-
MutableMapping,
|
7
5
|
Optional,
|
8
|
-
Sequence,
|
9
6
|
Tuple,
|
10
7
|
Union,
|
11
8
|
cast,
|
12
9
|
)
|
13
10
|
|
14
|
-
|
11
|
+
if sys.version_info >= (3, 9):
|
12
|
+
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
13
|
+
else:
|
14
|
+
from typing import Iterator, Mapping, MutableMapping, Sequence
|
15
15
|
|
16
|
-
|
17
|
-
from
|
18
|
-
|
16
|
+
if sys.version_info >= (3, 11):
|
17
|
+
from typing import Self
|
18
|
+
else:
|
19
|
+
from typing_extensions import Self
|
19
20
|
|
20
21
|
try:
|
21
22
|
import numpy as np
|
@@ -23,6 +24,10 @@ try:
|
|
23
24
|
except ModuleNotFoundError:
|
24
25
|
pass
|
25
26
|
|
27
|
+
from ._base import FoamDictionaryBase
|
28
|
+
from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
|
29
|
+
from ._serialization import serialize_entry
|
30
|
+
|
26
31
|
|
27
32
|
class _FoamFileBase:
|
28
33
|
def __init__(self, path: Union[str, Path]) -> None:
|
@@ -93,9 +98,7 @@ class FoamFile(
|
|
93
98
|
FoamDictionaryBase,
|
94
99
|
MutableMapping[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
|
95
100
|
):
|
96
|
-
"""
|
97
|
-
An OpenFOAM dictionary within a file as a mutable mapping.
|
98
|
-
"""
|
101
|
+
"""An OpenFOAM dictionary within a file as a mutable mapping."""
|
99
102
|
|
100
103
|
def __init__(self, _file: "FoamFile", _keywords: Sequence[str]) -> None:
|
101
104
|
self._file = _file
|
@@ -148,9 +151,7 @@ class FoamFile(
|
|
148
151
|
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
149
152
|
|
150
153
|
def as_dict(self) -> FoamDictionaryBase._Dict:
|
151
|
-
"""
|
152
|
-
Return a nested dict representation of the dictionary.
|
153
|
-
"""
|
154
|
+
"""Return a nested dict representation of the dictionary."""
|
154
155
|
ret = self._file.as_dict()
|
155
156
|
|
156
157
|
for k in self._keywords:
|
@@ -258,9 +259,7 @@ class FoamFile(
|
|
258
259
|
return f"{type(self).__name__}({self.path})"
|
259
260
|
|
260
261
|
def as_dict(self) -> FoamDictionaryBase._Dict:
|
261
|
-
"""
|
262
|
-
Return a nested dict representation of the file.
|
263
|
-
"""
|
262
|
+
"""Return a nested dict representation of the file."""
|
264
263
|
_, parsed = self._read()
|
265
264
|
return as_dict(parsed)
|
266
265
|
|
@@ -283,9 +282,7 @@ class FoamFieldFile(FoamFile):
|
|
283
282
|
|
284
283
|
@property
|
285
284
|
def type(self) -> str:
|
286
|
-
"""
|
287
|
-
Alias of `self["type"]`.
|
288
|
-
"""
|
285
|
+
"""Alias of `self["type"]`."""
|
289
286
|
ret = self["type"]
|
290
287
|
if not isinstance(ret, str):
|
291
288
|
raise TypeError("type is not a string")
|
@@ -304,9 +301,7 @@ class FoamFieldFile(FoamFile):
|
|
304
301
|
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
305
302
|
"NDArray[np.generic]",
|
306
303
|
]:
|
307
|
-
"""
|
308
|
-
Alias of `self["value"]`.
|
309
|
-
"""
|
304
|
+
"""Alias of `self["value"]`."""
|
310
305
|
ret = self["value"]
|
311
306
|
if not isinstance(ret, (int, float, Sequence)):
|
312
307
|
raise TypeError("value is not a field")
|
@@ -355,9 +350,7 @@ class FoamFieldFile(FoamFile):
|
|
355
350
|
|
356
351
|
@property
|
357
352
|
def dimensions(self) -> FoamFile.DimensionSet:
|
358
|
-
"""
|
359
|
-
Alias of `self["dimensions"]`.
|
360
|
-
"""
|
353
|
+
"""Alias of `self["dimensions"]`."""
|
361
354
|
ret = self["dimensions"]
|
362
355
|
if not isinstance(ret, FoamFile.DimensionSet):
|
363
356
|
raise TypeError("dimensions is not a DimensionSet")
|
@@ -378,9 +371,7 @@ class FoamFieldFile(FoamFile):
|
|
378
371
|
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
379
372
|
"NDArray[np.generic]",
|
380
373
|
]:
|
381
|
-
"""
|
382
|
-
Alias of `self["internalField"]`.
|
383
|
-
"""
|
374
|
+
"""Alias of `self["internalField"]`."""
|
384
375
|
ret = self["internalField"]
|
385
376
|
if not isinstance(ret, (int, float, Sequence)):
|
386
377
|
raise TypeError("internalField is not a field")
|
@@ -400,9 +391,7 @@ class FoamFieldFile(FoamFile):
|
|
400
391
|
|
401
392
|
@property
|
402
393
|
def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
|
403
|
-
"""
|
404
|
-
Alias of `self["boundaryField"]`.
|
405
|
-
"""
|
394
|
+
"""Alias of `self["boundaryField"]`."""
|
406
395
|
ret = self["boundaryField"]
|
407
396
|
if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
|
408
397
|
assert not isinstance(ret, FoamFile.Dictionary)
|
@@ -1,4 +1,10 @@
|
|
1
|
-
|
1
|
+
import sys
|
2
|
+
from typing import Optional, Tuple
|
3
|
+
|
4
|
+
if sys.version_info >= (3, 9):
|
5
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
6
|
+
else:
|
7
|
+
from typing import Mapping, MutableMapping, Sequence
|
2
8
|
|
3
9
|
from pyparsing import (
|
4
10
|
Dict,
|
@@ -9,8 +15,8 @@ from pyparsing import (
|
|
9
15
|
Literal,
|
10
16
|
Located,
|
11
17
|
Opt,
|
12
|
-
ParseResults,
|
13
18
|
ParserElement,
|
19
|
+
ParseResults,
|
14
20
|
QuotedString,
|
15
21
|
Word,
|
16
22
|
c_style_comment,
|
@@ -22,8 +28,11 @@ from pyparsing import (
|
|
22
28
|
|
23
29
|
from ._base import FoamDictionaryBase
|
24
30
|
|
25
|
-
|
26
|
-
|
31
|
+
_SWITCH = (
|
32
|
+
Keyword("yes") | Keyword("true") | Keyword("on") | Keyword("y") | Keyword("t")
|
33
|
+
).set_parse_action(lambda: True) | (
|
34
|
+
Keyword("no") | Keyword("false") | Keyword("off") | Keyword("n") | Keyword("f")
|
35
|
+
).set_parse_action(lambda: False)
|
27
36
|
_DIMENSIONS = (
|
28
37
|
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
29
38
|
).set_parse_action(lambda tks: FoamDictionaryBase.DimensionSet(*tks))
|
@@ -58,9 +67,7 @@ _FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
|
|
58
67
|
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
59
68
|
_ITEM = Forward()
|
60
69
|
_LIST = _list_of(_ITEM)
|
61
|
-
_ITEM <<=
|
62
|
-
_FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _YES | _NO | _TOKEN
|
63
|
-
)
|
70
|
+
_ITEM <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
|
64
71
|
_TOKENS = (
|
65
72
|
QuotedString('"', unquote_results=False) | Word(printables.replace(";", ""))
|
66
73
|
)[2, ...].set_parse_action(lambda tks: " ".join(tks))
|
@@ -124,9 +131,7 @@ def get_value(
|
|
124
131
|
parsed: Parsed,
|
125
132
|
keywords: Tuple[str, ...],
|
126
133
|
) -> Optional[FoamDictionaryBase.Value]:
|
127
|
-
"""
|
128
|
-
Value of an entry.
|
129
|
-
"""
|
134
|
+
"""Value of an entry."""
|
130
135
|
_, value, _ = parsed[keywords]
|
131
136
|
return value
|
132
137
|
|
@@ -137,9 +142,7 @@ def get_entry_locn(
|
|
137
142
|
*,
|
138
143
|
missing_ok: bool = False,
|
139
144
|
) -> Tuple[int, int]:
|
140
|
-
"""
|
141
|
-
Location of an entry or where it should be inserted.
|
142
|
-
"""
|
145
|
+
"""Location of an entry or where it should be inserted."""
|
143
146
|
try:
|
144
147
|
start, _, end = parsed[keywords]
|
145
148
|
except KeyError:
|
@@ -158,12 +161,9 @@ def get_entry_locn(
|
|
158
161
|
|
159
162
|
|
160
163
|
def as_dict(parsed: Parsed) -> FoamDictionaryBase._Dict:
|
161
|
-
"""
|
162
|
-
Return a nested dict representation of the file.
|
163
|
-
"""
|
164
|
+
"""Return a nested dict representation of the file."""
|
164
165
|
ret: FoamDictionaryBase._Dict = {}
|
165
166
|
for keywords, (_, value, _) in parsed.items():
|
166
|
-
|
167
167
|
r = ret
|
168
168
|
for k in keywords[:-1]:
|
169
169
|
assert isinstance(r, dict)
|
@@ -1,26 +1,17 @@
|
|
1
|
+
import sys
|
1
2
|
from contextlib import suppress
|
2
|
-
from typing import Any
|
3
|
+
from typing import Any
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
try:
|
7
|
-
import numpy as np
|
8
|
-
except ModuleNotFoundError:
|
9
|
-
numpy = False
|
5
|
+
if sys.version_info >= (3, 9):
|
6
|
+
from collections.abc import Mapping
|
10
7
|
else:
|
11
|
-
|
8
|
+
from typing import Mapping
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
return (
|
16
|
-
isinstance(value, Sequence)
|
17
|
-
and not isinstance(value, str)
|
18
|
-
or numpy
|
19
|
-
and isinstance(value, np.ndarray)
|
20
|
-
)
|
10
|
+
from .._util import is_sequence
|
11
|
+
from ._base import FoamDictionaryBase
|
21
12
|
|
22
13
|
|
23
|
-
def
|
14
|
+
def _serialize_switch(value: Any) -> str:
|
24
15
|
if value is True:
|
25
16
|
return "yes"
|
26
17
|
elif value is False:
|
@@ -30,23 +21,23 @@ def _serialize_bool(value: Any) -> str:
|
|
30
21
|
|
31
22
|
|
32
23
|
def _serialize_list(value: Any) -> str:
|
33
|
-
if
|
24
|
+
if is_sequence(value):
|
34
25
|
return f"({' '.join(_serialize_value(v) for v in value)})"
|
35
26
|
else:
|
36
27
|
raise TypeError(f"Not a valid sequence: {type(value)}")
|
37
28
|
|
38
29
|
|
39
30
|
def _serialize_field(value: Any) -> str:
|
40
|
-
if
|
31
|
+
if is_sequence(value):
|
41
32
|
try:
|
42
33
|
s = _serialize_list(value)
|
43
34
|
except TypeError:
|
44
35
|
raise TypeError(f"Not a valid field: {type(value)}") from None
|
45
36
|
else:
|
46
|
-
if len(value) < 10:
|
37
|
+
if not is_sequence(value[0]) and len(value) < 10:
|
47
38
|
return f"uniform {s}"
|
48
39
|
else:
|
49
|
-
if
|
40
|
+
if not is_sequence(value[0]):
|
50
41
|
kind = "scalar"
|
51
42
|
elif len(value[0]) == 3:
|
52
43
|
kind = "vector"
|
@@ -64,7 +55,7 @@ def _serialize_field(value: Any) -> str:
|
|
64
55
|
|
65
56
|
|
66
57
|
def _serialize_dimensions(value: Any) -> str:
|
67
|
-
if
|
58
|
+
if is_sequence(value) and len(value) == 7:
|
68
59
|
return f"[{' '.join(str(v) for v in value)}]"
|
69
60
|
else:
|
70
61
|
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
@@ -98,7 +89,7 @@ def _serialize_value(
|
|
98
89
|
return _serialize_list(value)
|
99
90
|
|
100
91
|
with suppress(TypeError):
|
101
|
-
return
|
92
|
+
return _serialize_switch(value)
|
102
93
|
|
103
94
|
return str(value)
|
104
95
|
|
@@ -1,12 +1,44 @@
|
|
1
1
|
import asyncio
|
2
|
+
import subprocess
|
2
3
|
import sys
|
3
|
-
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import
|
6
|
-
|
7
|
-
|
5
|
+
from typing import Any, Union
|
6
|
+
|
7
|
+
if sys.version_info >= (3, 9):
|
8
|
+
from collections.abc import Mapping, Sequence
|
9
|
+
else:
|
10
|
+
from typing import Mapping, Sequence
|
11
|
+
|
12
|
+
if sys.version_info >= (3, 10):
|
13
|
+
from typing import TypeGuard
|
14
|
+
else:
|
15
|
+
from typing_extensions import TypeGuard
|
16
|
+
|
17
|
+
try:
|
18
|
+
import numpy as np
|
19
|
+
except ModuleNotFoundError:
|
20
|
+
numpy = False
|
21
|
+
else:
|
22
|
+
numpy = True
|
23
|
+
|
24
|
+
|
25
|
+
def is_sequence(
|
26
|
+
value: Any,
|
27
|
+
) -> TypeGuard[Union["Sequence[Any]", "np.ndarray[Any, Any]"]]:
|
28
|
+
return (
|
29
|
+
isinstance(value, Sequence)
|
30
|
+
and not isinstance(value, str)
|
31
|
+
or numpy
|
32
|
+
and isinstance(value, np.ndarray)
|
33
|
+
)
|
34
|
+
|
35
|
+
|
36
|
+
CalledProcessError = subprocess.CalledProcessError
|
8
37
|
|
9
|
-
|
38
|
+
if sys.version_info >= (3, 9):
|
39
|
+
CompletedProcess = subprocess.CompletedProcess[str]
|
40
|
+
else:
|
41
|
+
CompletedProcess = subprocess.CompletedProcess
|
10
42
|
|
11
43
|
|
12
44
|
def run_process(
|
@@ -15,8 +47,8 @@ def run_process(
|
|
15
47
|
check: bool = True,
|
16
48
|
cwd: Union[None, str, Path] = None,
|
17
49
|
env: Union[None, Mapping[str, str]] = None,
|
18
|
-
) ->
|
19
|
-
shell =
|
50
|
+
) -> CompletedProcess:
|
51
|
+
shell = not is_sequence(cmd)
|
20
52
|
|
21
53
|
if sys.version_info < (3, 8):
|
22
54
|
if shell:
|
@@ -28,8 +60,9 @@ def run_process(
|
|
28
60
|
cmd,
|
29
61
|
cwd=cwd,
|
30
62
|
env=env,
|
31
|
-
stdout=subprocess.
|
32
|
-
stderr=subprocess.PIPE,
|
63
|
+
stdout=asyncio.subprocess.DEVNULL,
|
64
|
+
stderr=asyncio.subprocess.PIPE,
|
65
|
+
text=True,
|
33
66
|
shell=shell,
|
34
67
|
check=check,
|
35
68
|
)
|
@@ -43,13 +76,13 @@ async def run_process_async(
|
|
43
76
|
check: bool = True,
|
44
77
|
cwd: Union[None, str, Path] = None,
|
45
78
|
env: Union[None, Mapping[str, str]] = None,
|
46
|
-
) ->
|
47
|
-
if
|
79
|
+
) -> CompletedProcess:
|
80
|
+
if not is_sequence(cmd):
|
48
81
|
proc = await asyncio.create_subprocess_shell(
|
49
82
|
str(cmd),
|
50
83
|
cwd=cwd,
|
51
84
|
env=env,
|
52
|
-
stdout=asyncio.subprocess.
|
85
|
+
stdout=asyncio.subprocess.DEVNULL,
|
53
86
|
stderr=asyncio.subprocess.PIPE,
|
54
87
|
)
|
55
88
|
|
@@ -60,15 +93,16 @@ async def run_process_async(
|
|
60
93
|
*cmd,
|
61
94
|
cwd=cwd,
|
62
95
|
env=env,
|
63
|
-
stdout=asyncio.subprocess.
|
96
|
+
stdout=asyncio.subprocess.DEVNULL,
|
64
97
|
stderr=asyncio.subprocess.PIPE,
|
65
98
|
)
|
66
99
|
|
67
100
|
stdout, stderr = await proc.communicate()
|
68
101
|
|
102
|
+
assert stdout is None
|
69
103
|
assert proc.returncode is not None
|
70
104
|
|
71
|
-
ret =
|
105
|
+
ret = CompletedProcess(cmd, proc.returncode, None, stderr.decode())
|
72
106
|
|
73
107
|
if check:
|
74
108
|
ret.check_returncode()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.6
|
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
|
@@ -27,24 +27,23 @@ Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE.txt
|
28
28
|
Requires-Dist: aioshutil <2,>=1
|
29
29
|
Requires-Dist: pyparsing <4,>=3
|
30
|
-
Requires-Dist: typing-extensions <5,>=4
|
30
|
+
Requires-Dist: typing-extensions <5,>=4 ; python_version < "3.11"
|
31
31
|
Provides-Extra: docs
|
32
32
|
Requires-Dist: sphinx <8,>=7 ; extra == 'docs'
|
33
33
|
Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
|
34
34
|
Requires-Dist: numpy <2,>=1 ; extra == 'docs'
|
35
35
|
Provides-Extra: lint
|
36
|
-
Requires-Dist:
|
37
|
-
Requires-Dist: pytest <9,>=7 ; extra == 'lint'
|
38
|
-
Requires-Dist: pytest-asyncio <0.24,>=0.21 ; extra == 'lint'
|
39
|
-
Requires-Dist: numpy <2,>=1 ; extra == 'lint'
|
40
|
-
Requires-Dist: black ; extra == 'lint'
|
41
|
-
Requires-Dist: flake8 ; extra == 'lint'
|
42
|
-
Requires-Dist: Flake8-pyproject ; extra == 'lint'
|
36
|
+
Requires-Dist: ruff ; extra == 'lint'
|
43
37
|
Provides-Extra: test
|
44
38
|
Requires-Dist: pytest <9,>=7 ; extra == 'test'
|
45
39
|
Requires-Dist: pytest-asyncio <0.24,>=0.21 ; extra == 'test'
|
46
40
|
Requires-Dist: pytest-cov ; extra == 'test'
|
47
41
|
Requires-Dist: numpy <2,>=1 ; extra == 'test'
|
42
|
+
Provides-Extra: typing
|
43
|
+
Requires-Dist: mypy <2,>=1 ; extra == 'typing'
|
44
|
+
Requires-Dist: pytest <9,>=7 ; extra == 'typing'
|
45
|
+
Requires-Dist: pytest-asyncio <0.24,>=0.21 ; extra == 'typing'
|
46
|
+
Requires-Dist: numpy <2,>=1 ; extra == 'typing'
|
48
47
|
|
49
48
|
# foamlib
|
50
49
|
|
@@ -52,7 +51,7 @@ Requires-Dist: numpy <2,>=1 ; extra == 'test'
|
|
52
51
|
[](https://github.com/gerlero/foamlib/actions/workflows/ci.yml)
|
53
52
|
[](https://codecov.io/gh/gerlero/foamlib)
|
54
53
|
[](http://mypy-lang.org/)
|
55
|
-
[](https://github.com/astral-sh/ruff)
|
56
55
|
[](https://pypi.org/project/foamlib/)
|
57
56
|
[](https://pypi.org/project/foamlib/)
|
58
57
|
[](https://hub.docker.com/r/gerlero/foamlib/)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
foamlib/__init__.py,sha256=Y-_qOUsVNMpblI4RzK4j1yA1IsImanCa_v2miOUK_jQ,344
|
2
|
+
foamlib/_cases.py,sha256=SjDTVauOlcrtyvnt5uxWGSu2RYZ0lhKRmvMnuRnRuzI,20973
|
3
|
+
foamlib/_util.py,sha256=PBTpBwt_j1GXASncSDZUR8pH2u_h8UyJXm8GeFKebTY,2552
|
4
|
+
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
foamlib/_dictionaries/__init__.py,sha256=vxGpA7uaEbJwKFGOYdLFR6R9jcUj_HW60YzZBMjNXRo,160
|
6
|
+
foamlib/_dictionaries/_base.py,sha256=RRbuZT_wzbhVyhpP4RIw5_pY3AW62MZJQveLJ2bVD-4,1587
|
7
|
+
foamlib/_dictionaries/_files.py,sha256=gYeimu-8ZXyUaHf5jXp8zLE-Kvnzb6x0TZlbjNTVA0I,12457
|
8
|
+
foamlib/_dictionaries/_parsing.py,sha256=s6t9JJxAjsSp3ypeQNeskgtYWsbb52iBjrVDTUv56jc,5008
|
9
|
+
foamlib/_dictionaries/_serialization.py,sha256=1QJMxK0zXilpQoWR0ZPW2OImF1WRhXfuVDy-iCuELJc,3476
|
10
|
+
foamlib-0.2.6.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
11
|
+
foamlib-0.2.6.dist-info/METADATA,sha256=Y10E-baTUs-CytAKybJIZtvslXkjnKxwPylTdXpEbD8,4650
|
12
|
+
foamlib-0.2.6.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
13
|
+
foamlib-0.2.6.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
14
|
+
foamlib-0.2.6.dist-info/RECORD,,
|
foamlib-0.2.4.dist-info/RECORD
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=BPC3la1UvhtGOS7utfBE5tccqz8mxqzd7Fn0zFC5yz0,287
|
2
|
-
foamlib/_cases.py,sha256=4f3c5BXnsHPhFvgXNjUcGGHyu7I0WZT6zxlvGhb9kMY,21213
|
3
|
-
foamlib/_subprocesses.py,sha256=5vqdQvpN_2v4GgDqxi-s88NGhZ6doFxkh0XY89ZWuHA,1926
|
4
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
foamlib/_dictionaries/__init__.py,sha256=6UWBGe1t7cq-d6WWQrVm0Xpi7Whpkr-mkTWgAM4NwcE,160
|
6
|
-
foamlib/_dictionaries/_base.py,sha256=H8XfiaX1LD6OWwZ9m61SaKgI-_szF1udyEfiLurrCB8,1493
|
7
|
-
foamlib/_dictionaries/_files.py,sha256=1Ve1FG2VVKLyj6yZGe4Vy8EhEOtyORQtJMUK-cbPNt4,12424
|
8
|
-
foamlib/_dictionaries/_parsing.py,sha256=65kwMU6b4WmMngOR5ED8IBvMa59FQqTRmd9o0xPnWJM,4768
|
9
|
-
foamlib/_dictionaries/_serialization.py,sha256=viHpQKggSBfxq9V6veVcCCLNq7wdJ-2g5maecaN2beU,3612
|
10
|
-
foamlib-0.2.4.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
11
|
-
foamlib-0.2.4.dist-info/METADATA,sha256=Z5aFcGYwPssHy9DTrQqZ4Lsw6cJ1QjW9UFiqe0Uuf58,4640
|
12
|
-
foamlib-0.2.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
13
|
-
foamlib-0.2.4.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
14
|
-
foamlib-0.2.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|