foamlib 0.6.15__tar.gz → 0.7.1__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.
- {foamlib-0.6.15 → foamlib-0.7.1}/PKG-INFO +12 -6
- {foamlib-0.6.15 → foamlib-0.7.1}/README.md +11 -5
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/__init__.py +2 -3
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_files/__init__.py +0 -2
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_files/_files.py +34 -27
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_files/_parsing.py +95 -54
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_files/_serialization.py +22 -56
- foamlib-0.7.1/foamlib/_files/_types.py +78 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_files/_util.py +3 -3
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib.egg-info/PKG-INFO +12 -6
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib.egg-info/SOURCES.txt +1 -1
- foamlib-0.6.15/foamlib/_files/_base.py +0 -76
- {foamlib-0.6.15 → foamlib-0.7.1}/LICENSE.txt +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/__init__.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_async.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_base.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_run.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_slurm.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_subprocess.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_sync.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_cases/_util.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/_files/_io.py +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib/py.typed +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib.egg-info/dependency_links.txt +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib.egg-info/requires.txt +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/foamlib.egg-info/top_level.txt +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/pyproject.toml +0 -0
- {foamlib-0.6.15 → foamlib-0.7.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.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
|
@@ -69,13 +69,19 @@ Requires-Dist: foamlib[docs]; extra == "dev"
|
|
69
69
|
[](https://github.com/gerlero/foamlib/actions/workflows/docker.yml)
|
70
70
|
[](https://hub.docker.com/r/microfluidica/foamlib/)
|
71
71
|
|
72
|
-
|
72
|
+
**foamlib** provides a simple, modern, ergonomic and fast Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
|
73
|
+
|
74
|
+
<p align="center">
|
75
|
+
<img alt="benchmark" src="https://github.com/gerlero/foamlib/raw/main/benchmark.png" height="250">
|
76
|
+
<br>
|
77
|
+
<i>Parsing a </i>volVectorField<i> with 200k cells.</i>
|
78
|
+
</p>
|
73
79
|
|
74
|
-
|
80
|
+
## 👋 Basics
|
75
81
|
|
76
|
-
|
82
|
+
**foamlib** offers the following Python classes:
|
77
83
|
|
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).
|
84
|
+
* [`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 and in-place editor. Supports ASCII and binary field formats (with or without compression).
|
79
85
|
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
|
80
86
|
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
81
87
|
* [`AsyncSlurmFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncSlurmFoamCase): subclass of `AsyncFoamCase` used for running cases on a Slurm cluster.
|
@@ -185,7 +191,7 @@ async def cost(x):
|
|
185
191
|
await clone.run(fallback=True) # Run locally if Slurm is not available
|
186
192
|
return abs(clone[-1]["U"].internal_field[0][0])
|
187
193
|
|
188
|
-
result = differential_evolution(cost, bounds=[(-1, 1)], workers=
|
194
|
+
result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncSlurmFoamCase.map, polish=False)
|
189
195
|
```
|
190
196
|
|
191
197
|
### 📄 Use it to create a `run` (or `clean`) script
|
@@ -14,13 +14,19 @@
|
|
14
14
|
[](https://github.com/gerlero/foamlib/actions/workflows/docker.yml)
|
15
15
|
[](https://hub.docker.com/r/microfluidica/foamlib/)
|
16
16
|
|
17
|
-
|
17
|
+
**foamlib** provides a simple, modern, ergonomic and fast Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
|
18
|
+
|
19
|
+
<p align="center">
|
20
|
+
<img alt="benchmark" src="https://github.com/gerlero/foamlib/raw/main/benchmark.png" height="250">
|
21
|
+
<br>
|
22
|
+
<i>Parsing a </i>volVectorField<i> with 200k cells.</i>
|
23
|
+
</p>
|
18
24
|
|
19
|
-
|
25
|
+
## 👋 Basics
|
20
26
|
|
21
|
-
|
27
|
+
**foamlib** offers the following Python classes:
|
22
28
|
|
23
|
-
* [`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).
|
29
|
+
* [`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 and in-place editor. Supports ASCII and binary field formats (with or without compression).
|
24
30
|
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
|
25
31
|
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
26
32
|
* [`AsyncSlurmFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncSlurmFoamCase): subclass of `AsyncFoamCase` used for running cases on a Slurm cluster.
|
@@ -130,7 +136,7 @@ async def cost(x):
|
|
130
136
|
await clone.run(fallback=True) # Run locally if Slurm is not available
|
131
137
|
return abs(clone[-1]["U"].internal_field[0][0])
|
132
138
|
|
133
|
-
result = differential_evolution(cost, bounds=[(-1, 1)], workers=
|
139
|
+
result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncSlurmFoamCase.map, polish=False)
|
134
140
|
```
|
135
141
|
|
136
142
|
### 📄 Use it to create a `run` (or `clean`) script
|
@@ -1,6 +1,6 @@
|
|
1
1
|
"""A Python interface for interacting with OpenFOAM."""
|
2
2
|
|
3
|
-
__version__ = "0.
|
3
|
+
__version__ = "0.7.1"
|
4
4
|
|
5
5
|
from ._cases import (
|
6
6
|
AsyncFoamCase,
|
@@ -10,7 +10,7 @@ from ._cases import (
|
|
10
10
|
FoamCaseBase,
|
11
11
|
FoamCaseRunBase,
|
12
12
|
)
|
13
|
-
from ._files import FoamFieldFile, FoamFile
|
13
|
+
from ._files import FoamFieldFile, FoamFile
|
14
14
|
|
15
15
|
__all__ = [
|
16
16
|
"AsyncFoamCase",
|
@@ -21,5 +21,4 @@ __all__ = [
|
|
21
21
|
"FoamCaseBase",
|
22
22
|
"FoamCaseRunBase",
|
23
23
|
"FoamFieldFile",
|
24
|
-
"FoamFileBase",
|
25
24
|
]
|
@@ -15,17 +15,25 @@ if sys.version_info >= (3, 9):
|
|
15
15
|
else:
|
16
16
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
17
17
|
|
18
|
-
from ._base import FoamFileBase
|
19
18
|
from ._io import FoamFileIO
|
20
19
|
from ._serialization import Kind, dumps, normalize
|
20
|
+
from ._types import (
|
21
|
+
Data,
|
22
|
+
DataEntry,
|
23
|
+
Dict_,
|
24
|
+
Dimensioned,
|
25
|
+
DimensionSet,
|
26
|
+
Field,
|
27
|
+
File,
|
28
|
+
MutableData,
|
29
|
+
)
|
21
30
|
from ._util import is_sequence
|
22
31
|
|
23
32
|
|
24
33
|
class FoamFile(
|
25
|
-
FoamFileBase,
|
26
34
|
MutableMapping[
|
27
35
|
Optional[Union[str, Tuple[str, ...]]],
|
28
|
-
|
36
|
+
MutableData,
|
29
37
|
],
|
30
38
|
FoamFileIO,
|
31
39
|
):
|
@@ -37,8 +45,11 @@ class FoamFile(
|
|
37
45
|
Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
|
38
46
|
"""
|
39
47
|
|
48
|
+
Dimensioned = Dimensioned
|
49
|
+
DimensionSet = DimensionSet
|
50
|
+
|
40
51
|
class SubDict(
|
41
|
-
MutableMapping[str,
|
52
|
+
MutableMapping[str, MutableData],
|
42
53
|
):
|
43
54
|
"""An OpenFOAM dictionary within a file as a mutable mapping."""
|
44
55
|
|
@@ -46,15 +57,13 @@ class FoamFile(
|
|
46
57
|
self._file = _file
|
47
58
|
self._keywords = _keywords
|
48
59
|
|
49
|
-
def __getitem__(
|
50
|
-
self, keyword: str
|
51
|
-
) -> FoamFileBase._DataEntry | FoamFile.SubDict:
|
60
|
+
def __getitem__(self, keyword: str) -> DataEntry | FoamFile.SubDict:
|
52
61
|
return self._file[(*self._keywords, keyword)]
|
53
62
|
|
54
63
|
def __setitem__(
|
55
64
|
self,
|
56
65
|
keyword: str,
|
57
|
-
data:
|
66
|
+
data: Data,
|
58
67
|
) -> None:
|
59
68
|
self._file[(*self._keywords, keyword)] = data
|
60
69
|
|
@@ -83,7 +92,7 @@ class FoamFile(
|
|
83
92
|
def __repr__(self) -> str:
|
84
93
|
return f"{type(self).__qualname__}('{self._file}', {self._keywords})"
|
85
94
|
|
86
|
-
def as_dict(self) ->
|
95
|
+
def as_dict(self) -> Dict_:
|
87
96
|
"""Return a nested dict representation of the dictionary."""
|
88
97
|
ret = self._file.as_dict(include_header=True)
|
89
98
|
|
@@ -91,9 +100,9 @@ class FoamFile(
|
|
91
100
|
assert isinstance(ret, dict)
|
92
101
|
v = ret[k]
|
93
102
|
assert isinstance(v, dict)
|
94
|
-
ret = cast(
|
103
|
+
ret = cast(File, v)
|
95
104
|
|
96
|
-
return cast(
|
105
|
+
return cast(Dict_, ret)
|
97
106
|
|
98
107
|
@property
|
99
108
|
def version(self) -> float:
|
@@ -165,7 +174,7 @@ class FoamFile(
|
|
165
174
|
|
166
175
|
def __getitem__(
|
167
176
|
self, keywords: str | tuple[str, ...] | None
|
168
|
-
) ->
|
177
|
+
) -> DataEntry | FoamFile.SubDict:
|
169
178
|
if not keywords:
|
170
179
|
keywords = ()
|
171
180
|
elif not isinstance(keywords, tuple):
|
@@ -181,9 +190,7 @@ class FoamFile(
|
|
181
190
|
return FoamFile.SubDict(self, keywords)
|
182
191
|
return deepcopy(value)
|
183
192
|
|
184
|
-
def __setitem__(
|
185
|
-
self, keywords: str | tuple[str, ...] | None, data: FoamFileBase.Data
|
186
|
-
) -> None:
|
193
|
+
def __setitem__(self, keywords: str | tuple[str, ...] | None, data: Data) -> None:
|
187
194
|
if not keywords:
|
188
195
|
keywords = ()
|
189
196
|
elif not isinstance(keywords, tuple):
|
@@ -368,7 +375,7 @@ class FoamFile(
|
|
368
375
|
def __fspath__(self) -> str:
|
369
376
|
return str(self.path)
|
370
377
|
|
371
|
-
def as_dict(self, *, include_header: bool = False) ->
|
378
|
+
def as_dict(self, *, include_header: bool = False) -> File:
|
372
379
|
"""
|
373
380
|
Return a nested dict representation of the file.
|
374
381
|
|
@@ -411,17 +418,17 @@ class FoamFieldFile(FoamFile):
|
|
411
418
|
@property
|
412
419
|
def value(
|
413
420
|
self,
|
414
|
-
) ->
|
421
|
+
) -> Field:
|
415
422
|
"""Alias of `self["value"]`."""
|
416
423
|
return cast(
|
417
|
-
|
424
|
+
Field,
|
418
425
|
self["value"],
|
419
426
|
)
|
420
427
|
|
421
428
|
@value.setter
|
422
429
|
def value(
|
423
430
|
self,
|
424
|
-
value:
|
431
|
+
value: Field,
|
425
432
|
) -> None:
|
426
433
|
self["value"] = value
|
427
434
|
|
@@ -431,7 +438,7 @@ class FoamFieldFile(FoamFile):
|
|
431
438
|
|
432
439
|
def __getitem__(
|
433
440
|
self, keywords: str | tuple[str, ...] | None
|
434
|
-
) ->
|
441
|
+
) -> DataEntry | FoamFile.SubDict:
|
435
442
|
if not keywords:
|
436
443
|
keywords = ()
|
437
444
|
elif not isinstance(keywords, tuple):
|
@@ -446,29 +453,29 @@ class FoamFieldFile(FoamFile):
|
|
446
453
|
return ret
|
447
454
|
|
448
455
|
@property
|
449
|
-
def dimensions(self) ->
|
456
|
+
def dimensions(self) -> DimensionSet | Sequence[float]:
|
450
457
|
"""Alias of `self["dimensions"]`."""
|
451
458
|
ret = self["dimensions"]
|
452
|
-
if not isinstance(ret,
|
459
|
+
if not isinstance(ret, DimensionSet):
|
453
460
|
msg = "dimensions is not a DimensionSet"
|
454
461
|
raise TypeError(msg)
|
455
462
|
return ret
|
456
463
|
|
457
464
|
@dimensions.setter
|
458
|
-
def dimensions(self, value:
|
465
|
+
def dimensions(self, value: DimensionSet | Sequence[float]) -> None:
|
459
466
|
self["dimensions"] = value
|
460
467
|
|
461
468
|
@property
|
462
469
|
def internal_field(
|
463
470
|
self,
|
464
|
-
) ->
|
471
|
+
) -> Field:
|
465
472
|
"""Alias of `self["internalField"]`."""
|
466
|
-
return cast(
|
473
|
+
return cast(Field, self["internalField"])
|
467
474
|
|
468
475
|
@internal_field.setter
|
469
476
|
def internal_field(
|
470
477
|
self,
|
471
|
-
value:
|
478
|
+
value: Field,
|
472
479
|
) -> None:
|
473
480
|
self["internalField"] = value
|
474
481
|
|
@@ -483,5 +490,5 @@ class FoamFieldFile(FoamFile):
|
|
483
490
|
return ret
|
484
491
|
|
485
492
|
@boundary_field.setter
|
486
|
-
def boundary_field(self, value: Mapping[str,
|
493
|
+
def boundary_field(self, value: Mapping[str, Dict_]) -> None:
|
487
494
|
self["boundaryField"] = value
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import array
|
4
|
+
import re
|
4
5
|
import sys
|
5
6
|
from typing import Tuple, Union, cast
|
6
7
|
|
@@ -28,15 +29,15 @@ from pyparsing import (
|
|
28
29
|
ParserElement,
|
29
30
|
ParseResults,
|
30
31
|
QuotedString,
|
32
|
+
Regex,
|
31
33
|
Word,
|
32
34
|
common,
|
33
35
|
counted_array,
|
34
|
-
cpp_style_comment,
|
35
36
|
identchars,
|
36
37
|
printables,
|
37
38
|
)
|
38
39
|
|
39
|
-
from .
|
40
|
+
from ._types import DataEntry, Dimensioned, DimensionSet, File
|
40
41
|
|
41
42
|
|
42
43
|
def _list_of(entry: ParserElement) -> ParserElement:
|
@@ -56,6 +57,59 @@ def _list_of(entry: ParserElement) -> ParserElement:
|
|
56
57
|
)
|
57
58
|
|
58
59
|
|
60
|
+
def _counted_tensor_list(*, size: int, ignore: Regex) -> ParserElement:
|
61
|
+
float_pattern = r"[+-]?((\d+\.?\d*(e[+-]?\d+)?)|nan|inf(inity)?)"
|
62
|
+
ignore_pattern = rf"(?:{ignore.re.pattern}|\s)+"
|
63
|
+
|
64
|
+
if size == 1:
|
65
|
+
tensor_pattern = float_pattern
|
66
|
+
tensor = common.ieee_float
|
67
|
+
else:
|
68
|
+
tensor_pattern = rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{size - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
|
69
|
+
tensor = (
|
70
|
+
Literal("(").suppress()
|
71
|
+
+ Group(common.ieee_float[size], aslist=True)
|
72
|
+
+ Literal(")").suppress()
|
73
|
+
)
|
74
|
+
|
75
|
+
list_ = Forward()
|
76
|
+
|
77
|
+
def count_parse_action(tks: ParseResults) -> None:
|
78
|
+
nonlocal list_
|
79
|
+
length = tks[0]
|
80
|
+
assert isinstance(length, int)
|
81
|
+
|
82
|
+
list_ <<= Regex(
|
83
|
+
rf"\((?:{ignore_pattern})?(?:{tensor_pattern}{ignore_pattern}){{{length - 1}}}{tensor_pattern}(?:{ignore_pattern})?\)",
|
84
|
+
re.IGNORECASE,
|
85
|
+
)
|
86
|
+
|
87
|
+
count = common.integer.add_parse_action(count_parse_action)
|
88
|
+
|
89
|
+
def list_parse_action(
|
90
|
+
tks: ParseResults,
|
91
|
+
) -> list[list[float]] | list[list[list[float]]]:
|
92
|
+
values = (
|
93
|
+
re.sub(ignore.re, " ", tks[0]).replace("(", " ").replace(")", " ").split()
|
94
|
+
)
|
95
|
+
|
96
|
+
if size == 1:
|
97
|
+
return [[float(v) for v in values]]
|
98
|
+
|
99
|
+
return [
|
100
|
+
[
|
101
|
+
[float(v) for v in values[i : i + size]]
|
102
|
+
for i in range(0, len(values), size)
|
103
|
+
]
|
104
|
+
]
|
105
|
+
|
106
|
+
list_.add_parse_action(list_parse_action)
|
107
|
+
|
108
|
+
return (count.suppress() + list_) | (
|
109
|
+
common.integer + Literal("{").suppress() + tensor + Literal("}").suppress()
|
110
|
+
).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
|
111
|
+
|
112
|
+
|
59
113
|
def _keyword_entry_of(
|
60
114
|
keyword: ParserElement,
|
61
115
|
data_entries: ParserElement,
|
@@ -96,6 +150,9 @@ def _unpack_binary_field(
|
|
96
150
|
return [values]
|
97
151
|
|
98
152
|
|
153
|
+
# https://github.com/pyparsing/pyparsing/pull/584
|
154
|
+
_COMMENT = Regex(r"(?:/\*(?:[^*]|\*(?!/))*\*/)|(?://(?:\\\n|[^\n])*)")
|
155
|
+
|
99
156
|
_IDENTCHARS = identchars + "$"
|
100
157
|
_IDENTBODYCHARS = (
|
101
158
|
printables.replace(";", "")
|
@@ -120,11 +177,11 @@ _SWITCH = (
|
|
120
177
|
).set_parse_action(lambda: False)
|
121
178
|
_DIMENSIONS = (
|
122
179
|
Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
|
123
|
-
).set_parse_action(lambda tks:
|
180
|
+
).set_parse_action(lambda tks: DimensionSet(*tks))
|
124
181
|
_TENSOR = common.ieee_float | (
|
125
182
|
Literal("(").suppress()
|
126
183
|
+ Group(
|
127
|
-
common.ieee_float[
|
184
|
+
common.ieee_float[3] | common.ieee_float[6] | common.ieee_float[9], aslist=True
|
128
185
|
)
|
129
186
|
+ Literal(")").suppress()
|
130
187
|
)
|
@@ -133,7 +190,7 @@ _IDENTIFIER = Combine(
|
|
133
190
|
+ Opt(Literal("(") + Word(_IDENTBODYCHARS, exclude_chars="()") + Literal(")"))
|
134
191
|
)
|
135
192
|
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
136
|
-
lambda tks:
|
193
|
+
lambda tks: Dimensioned(*reversed(tks.as_list()))
|
137
194
|
)
|
138
195
|
_FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
139
196
|
Keyword("nonuniform", _IDENTBODYCHARS).suppress()
|
@@ -145,7 +202,7 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
|
145
202
|
Literal("scalar").suppress()
|
146
203
|
+ Literal(">").suppress()
|
147
204
|
+ (
|
148
|
-
|
205
|
+
_counted_tensor_list(size=1, ignore=_COMMENT)
|
149
206
|
| (
|
150
207
|
(
|
151
208
|
(
|
@@ -169,11 +226,7 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
|
169
226
|
Literal("vector").suppress()
|
170
227
|
+ Literal(">").suppress()
|
171
228
|
+ (
|
172
|
-
|
173
|
-
Literal("(").suppress()
|
174
|
-
+ Group(common.ieee_float[3], aslist=True)
|
175
|
-
+ Literal(")").suppress()
|
176
|
-
)
|
229
|
+
_counted_tensor_list(size=3, ignore=_COMMENT)
|
177
230
|
| (
|
178
231
|
(
|
179
232
|
(
|
@@ -194,14 +247,10 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
|
194
247
|
)
|
195
248
|
)
|
196
249
|
| (
|
197
|
-
Literal("
|
250
|
+
Literal("symmTensor").suppress()
|
198
251
|
+ Literal(">").suppress()
|
199
252
|
+ (
|
200
|
-
|
201
|
-
Literal("(").suppress()
|
202
|
-
+ Group(common.ieee_float[6], aslist=True)
|
203
|
-
+ Literal(")").suppress()
|
204
|
-
)
|
253
|
+
_counted_tensor_list(size=6, ignore=_COMMENT)
|
205
254
|
| (
|
206
255
|
(
|
207
256
|
(
|
@@ -225,11 +274,7 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
|
225
274
|
Literal("tensor").suppress()
|
226
275
|
+ Literal(">").suppress()
|
227
276
|
+ (
|
228
|
-
|
229
|
-
Literal("(").suppress()
|
230
|
-
+ Group(common.ieee_float[9], aslist=True)
|
231
|
-
+ Literal(")").suppress()
|
232
|
-
)
|
277
|
+
_counted_tensor_list(size=9, ignore=_COMMENT)
|
233
278
|
| (
|
234
279
|
(
|
235
280
|
(
|
@@ -253,46 +298,45 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
|
253
298
|
)
|
254
299
|
)
|
255
300
|
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
256
|
-
|
257
|
-
|
258
|
-
|
301
|
+
DATA = Forward()
|
302
|
+
KEYWORD = (
|
303
|
+
_TOKEN
|
304
|
+
| _list_of(_IDENTIFIER)
|
305
|
+
.set_parse_action(lambda tks: "(" + " ".join(tks[0]) + ")")
|
306
|
+
.ignore(_COMMENT)
|
307
|
+
.parse_with_tabs()
|
259
308
|
)
|
260
|
-
_KEYWORD_ENTRY = Dict(Group(_keyword_entry_of(
|
309
|
+
_KEYWORD_ENTRY = Dict(Group(_keyword_entry_of(KEYWORD, DATA)), asdict=True)
|
261
310
|
_DATA_ENTRY = Forward()
|
262
311
|
_LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
|
263
312
|
_LIST = _list_of(_LIST_ENTRY)
|
264
313
|
_NUMBER = common.signed_integer ^ common.ieee_float
|
265
314
|
_DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH | _TOKEN
|
266
315
|
|
267
|
-
|
268
|
-
|
316
|
+
DATA <<= (
|
317
|
+
_DATA_ENTRY[1, ...]
|
318
|
+
.set_parse_action(lambda tks: tuple(tks) if len(tks) > 1 else [tks[0]])
|
319
|
+
.ignore(_COMMENT)
|
320
|
+
.parse_with_tabs()
|
269
321
|
)
|
270
322
|
|
271
323
|
_FILE = (
|
272
324
|
Dict(
|
273
|
-
Group(_keyword_entry_of(
|
274
|
-
+ Opt(
|
275
|
-
|
276
|
-
Located(
|
277
|
-
_DATA_ENTRY[1, ...].set_parse_action(
|
278
|
-
lambda tks: [None, tuple(tks) if len(tks) > 1 else tks[0]]
|
279
|
-
)
|
280
|
-
)
|
281
|
-
)
|
282
|
-
)
|
283
|
-
+ Group(_keyword_entry_of(_KEYWORD, Opt(_DATA, default=""), located=True))[...]
|
325
|
+
Group(_keyword_entry_of(KEYWORD, Opt(DATA, default=""), located=True))[...]
|
326
|
+
+ Opt(Group(Located(DATA.copy().add_parse_action(lambda tks: ["", tks[0]]))))
|
327
|
+
+ Group(_keyword_entry_of(KEYWORD, Opt(DATA, default=""), located=True))[...]
|
284
328
|
)
|
285
|
-
.ignore(
|
329
|
+
.ignore(_COMMENT)
|
286
330
|
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
|
287
331
|
.parse_with_tabs()
|
288
332
|
)
|
289
333
|
|
290
334
|
|
291
|
-
class Parsed(Mapping[Tuple[str, ...], Union[
|
335
|
+
class Parsed(Mapping[Tuple[str, ...], Union[DataEntry, EllipsisType]]):
|
292
336
|
def __init__(self, contents: bytes) -> None:
|
293
337
|
self._parsed: MutableMapping[
|
294
338
|
tuple[str, ...],
|
295
|
-
tuple[int,
|
339
|
+
tuple[int, DataEntry | EllipsisType, int],
|
296
340
|
] = {}
|
297
341
|
for parse_result in _FILE.parse_string(
|
298
342
|
contents.decode("latin-1"), parse_all=True
|
@@ -305,12 +349,10 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase._DataEntry, EllipsisTyp
|
|
305
349
|
@staticmethod
|
306
350
|
def _flatten_result(
|
307
351
|
parse_result: ParseResults, *, _keywords: tuple[str, ...] = ()
|
308
|
-
) -> Mapping[
|
309
|
-
tuple[str, ...], tuple[int, FoamFileBase._DataEntry | EllipsisType, int]
|
310
|
-
]:
|
352
|
+
) -> Mapping[tuple[str, ...], tuple[int, DataEntry | EllipsisType, int]]:
|
311
353
|
ret: MutableMapping[
|
312
354
|
tuple[str, ...],
|
313
|
-
tuple[int,
|
355
|
+
tuple[int, DataEntry | EllipsisType, int],
|
314
356
|
] = {}
|
315
357
|
start = parse_result.locn_start
|
316
358
|
assert isinstance(start, int)
|
@@ -319,7 +361,8 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase._DataEntry, EllipsisTyp
|
|
319
361
|
end = parse_result.locn_end
|
320
362
|
assert isinstance(end, int)
|
321
363
|
keyword, *data = item
|
322
|
-
|
364
|
+
assert isinstance(keyword, str)
|
365
|
+
if not keyword:
|
323
366
|
assert not _keywords
|
324
367
|
assert len(data) == 1
|
325
368
|
assert not isinstance(data[0], ParseResults)
|
@@ -336,16 +379,14 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase._DataEntry, EllipsisTyp
|
|
336
379
|
ret[(*_keywords, keyword)] = (start, d, end)
|
337
380
|
return ret
|
338
381
|
|
339
|
-
def __getitem__(
|
340
|
-
self, keywords: tuple[str, ...]
|
341
|
-
) -> FoamFileBase._DataEntry | EllipsisType:
|
382
|
+
def __getitem__(self, keywords: tuple[str, ...]) -> DataEntry | EllipsisType:
|
342
383
|
_, data, _ = self._parsed[keywords]
|
343
384
|
return data
|
344
385
|
|
345
386
|
def put(
|
346
387
|
self,
|
347
388
|
keywords: tuple[str, ...],
|
348
|
-
data:
|
389
|
+
data: DataEntry | EllipsisType,
|
349
390
|
content: bytes,
|
350
391
|
) -> None:
|
351
392
|
start, end = self.entry_location(keywords, missing_ok=True)
|
@@ -413,14 +454,14 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase._DataEntry, EllipsisTyp
|
|
413
454
|
|
414
455
|
return start, end
|
415
456
|
|
416
|
-
def as_dict(self) ->
|
417
|
-
ret:
|
457
|
+
def as_dict(self) -> File:
|
458
|
+
ret: File = {}
|
418
459
|
for keywords, (_, data, _) in self._parsed.items():
|
419
460
|
r = ret
|
420
461
|
for k in keywords[:-1]:
|
421
462
|
v = r[k]
|
422
463
|
assert isinstance(v, dict)
|
423
|
-
r = cast(
|
464
|
+
r = cast(File, v)
|
424
465
|
|
425
466
|
assert isinstance(r, dict)
|
426
467
|
if keywords:
|
@@ -1,9 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import array
|
4
|
-
import contextlib
|
5
4
|
import itertools
|
6
|
-
import re
|
7
5
|
import sys
|
8
6
|
from enum import Enum, auto
|
9
7
|
from typing import cast, overload
|
@@ -13,7 +11,8 @@ if sys.version_info >= (3, 9):
|
|
13
11
|
else:
|
14
12
|
from typing import Mapping, Sequence
|
15
13
|
|
16
|
-
from .
|
14
|
+
from ._parsing import DATA, KEYWORD
|
15
|
+
from ._types import Data, DataEntry, Dimensioned, DimensionSet
|
17
16
|
from ._util import is_sequence
|
18
17
|
|
19
18
|
try:
|
@@ -34,24 +33,15 @@ class Kind(Enum):
|
|
34
33
|
DIMENSIONS = auto()
|
35
34
|
|
36
35
|
|
37
|
-
_TOKENS = re.compile(r'(?:[^\s"]|"(?:[^"])*")+')
|
38
|
-
|
39
|
-
|
40
36
|
@overload
|
41
|
-
def normalize(
|
42
|
-
data: FoamFileBase._DataEntry, *, kind: Kind = Kind.DEFAULT
|
43
|
-
) -> FoamFileBase._DataEntry: ...
|
37
|
+
def normalize(data: DataEntry, *, kind: Kind = Kind.DEFAULT) -> DataEntry: ...
|
44
38
|
|
45
39
|
|
46
40
|
@overload
|
47
|
-
def normalize(
|
48
|
-
data: FoamFileBase.Data, *, kind: Kind = Kind.DEFAULT
|
49
|
-
) -> FoamFileBase.Data: ...
|
41
|
+
def normalize(data: Data, *, kind: Kind = Kind.DEFAULT) -> Data: ...
|
50
42
|
|
51
43
|
|
52
|
-
def normalize(
|
53
|
-
data: FoamFileBase.Data, *, kind: Kind = Kind.DEFAULT
|
54
|
-
) -> FoamFileBase.Data:
|
44
|
+
def normalize(data: Data, *, kind: Kind = Kind.DEFAULT) -> Data:
|
55
45
|
if numpy and isinstance(data, np.ndarray):
|
56
46
|
ret = data.tolist()
|
57
47
|
assert isinstance(ret, list)
|
@@ -67,54 +57,30 @@ def normalize(
|
|
67
57
|
and all(isinstance(d, (int, float)) for d in data)
|
68
58
|
):
|
69
59
|
data = cast(Sequence[float], data)
|
70
|
-
return
|
60
|
+
return DimensionSet(*data)
|
71
61
|
|
72
62
|
if is_sequence(data) and (kind == Kind.SINGLE_ENTRY or not isinstance(data, tuple)):
|
73
|
-
|
74
|
-
|
75
|
-
if isinstance(data, str):
|
76
|
-
data = data.strip()
|
77
|
-
|
78
|
-
if data.startswith("(") and data.endswith(")"):
|
79
|
-
data = data[1:-1].split()
|
80
|
-
if kind == Kind.KEYWORD:
|
81
|
-
return "(" + " ".join(data) + ")"
|
82
|
-
return [normalize(d, kind=Kind.SINGLE_ENTRY) for d in data]
|
83
|
-
|
84
|
-
if data.startswith("[") and data.endswith("]"):
|
85
|
-
data = data[1:-1].split()
|
86
|
-
return normalize(data, kind=Kind.DIMENSIONS)
|
87
|
-
|
88
|
-
with contextlib.suppress(ValueError):
|
89
|
-
return int(data)
|
90
|
-
|
91
|
-
with contextlib.suppress(ValueError):
|
92
|
-
return float(data)
|
63
|
+
if len(data) == 1 and isinstance(data[0], Mapping) and len(data[0]) > 1:
|
64
|
+
return [normalize({k: v}) for k, v in data[0].items()]
|
93
65
|
|
94
|
-
|
95
|
-
if data in ("yes", "true", "on", "y", "t"):
|
96
|
-
return True
|
97
|
-
if data in ("no", "false", "off", "n", "f"):
|
98
|
-
return False
|
99
|
-
|
100
|
-
tokens: list[str] = re.findall(_TOKENS, data)
|
66
|
+
return [normalize(d, kind=Kind.SINGLE_ENTRY) for d in data]
|
101
67
|
|
102
|
-
|
103
|
-
|
68
|
+
if isinstance(data, Dimensioned):
|
69
|
+
value = normalize(data.value, kind=Kind.SINGLE_ENTRY)
|
70
|
+
assert isinstance(value, (int, float, list))
|
71
|
+
return Dimensioned(value, data.dimensions, data.name)
|
104
72
|
|
73
|
+
if isinstance(data, str):
|
105
74
|
if kind == Kind.KEYWORD:
|
106
|
-
|
107
|
-
|
108
|
-
|
75
|
+
data = KEYWORD.parse_string(data, parse_all=True)[0]
|
76
|
+
assert isinstance(data, str)
|
77
|
+
return data
|
109
78
|
|
110
|
-
|
111
|
-
value = normalize(data.value, kind=Kind.SINGLE_ENTRY)
|
112
|
-
assert isinstance(value, (int, float, list))
|
113
|
-
return FoamFileBase.Dimensioned(value, data.dimensions, data.name)
|
79
|
+
return cast(DataEntry, DATA.parse_string(data, parse_all=True)[0])
|
114
80
|
|
115
81
|
if isinstance(
|
116
82
|
data,
|
117
|
-
(int, float, bool, tuple,
|
83
|
+
(int, float, bool, tuple, DimensionSet),
|
118
84
|
):
|
119
85
|
return data
|
120
86
|
|
@@ -123,7 +89,7 @@ def normalize(
|
|
123
89
|
|
124
90
|
|
125
91
|
def dumps(
|
126
|
-
data:
|
92
|
+
data: Data,
|
127
93
|
*,
|
128
94
|
kind: Kind = Kind.DEFAULT,
|
129
95
|
) -> bytes:
|
@@ -144,7 +110,7 @@ def dumps(
|
|
144
110
|
|
145
111
|
return b" ".join(entries)
|
146
112
|
|
147
|
-
if isinstance(data,
|
113
|
+
if isinstance(data, DimensionSet):
|
148
114
|
return b"[" + b" ".join(dumps(v) for v in data) + b"]"
|
149
115
|
|
150
116
|
if kind in (
|
@@ -201,7 +167,7 @@ def dumps(
|
|
201
167
|
|
202
168
|
return b"nonuniform List<" + tensor_kind + b"> " + dumps(len(data)) + contents
|
203
169
|
|
204
|
-
if isinstance(data,
|
170
|
+
if isinstance(data, Dimensioned):
|
205
171
|
if data.name is not None:
|
206
172
|
return (
|
207
173
|
dumps(data.name)
|
@@ -0,0 +1,78 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import sys
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import TYPE_CHECKING, Dict, NamedTuple, Optional, Tuple, Union
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
import numpy as np
|
9
|
+
|
10
|
+
if sys.version_info >= (3, 9):
|
11
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
12
|
+
else:
|
13
|
+
from typing import Mapping, MutableMapping, Sequence
|
14
|
+
|
15
|
+
|
16
|
+
class DimensionSet(NamedTuple):
|
17
|
+
mass: float = 0
|
18
|
+
length: float = 0
|
19
|
+
time: float = 0
|
20
|
+
temperature: float = 0
|
21
|
+
moles: float = 0
|
22
|
+
current: float = 0
|
23
|
+
luminous_intensity: float = 0
|
24
|
+
|
25
|
+
def __repr__(self) -> str:
|
26
|
+
return f"{type(self).__name__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
|
27
|
+
|
28
|
+
|
29
|
+
Tensor = Union[
|
30
|
+
float,
|
31
|
+
Sequence[float],
|
32
|
+
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
33
|
+
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
34
|
+
]
|
35
|
+
|
36
|
+
|
37
|
+
@dataclass
|
38
|
+
class Dimensioned:
|
39
|
+
value: Tensor = 0
|
40
|
+
dimensions: DimensionSet | Sequence[float] = ()
|
41
|
+
name: str | None = None
|
42
|
+
|
43
|
+
def __post_init__(self) -> None:
|
44
|
+
if not isinstance(self.dimensions, DimensionSet):
|
45
|
+
self.dimensions = DimensionSet(*self.dimensions)
|
46
|
+
|
47
|
+
|
48
|
+
Field = Union[
|
49
|
+
Tensor, Sequence[Tensor], "np.ndarray[Tuple[int, int], np.dtype[np.generic]]"
|
50
|
+
]
|
51
|
+
|
52
|
+
DataEntry = Union[
|
53
|
+
str,
|
54
|
+
int,
|
55
|
+
float,
|
56
|
+
bool,
|
57
|
+
Dimensioned,
|
58
|
+
DimensionSet,
|
59
|
+
Sequence["Data"],
|
60
|
+
Tensor,
|
61
|
+
Field,
|
62
|
+
]
|
63
|
+
|
64
|
+
Data = Union[
|
65
|
+
DataEntry,
|
66
|
+
Mapping[str, "Data"],
|
67
|
+
]
|
68
|
+
"""
|
69
|
+
A value that can be stored in an OpenFOAM file.
|
70
|
+
"""
|
71
|
+
|
72
|
+
MutableData = Union[
|
73
|
+
DataEntry,
|
74
|
+
MutableMapping[str, "MutableData"],
|
75
|
+
]
|
76
|
+
|
77
|
+
Dict_ = Dict[str, Union["Data", "Dict_"]]
|
78
|
+
File = Dict[Optional[str], Union["Data", "Dict_"]]
|
@@ -14,10 +14,10 @@ else:
|
|
14
14
|
from typing_extensions import TypeGuard
|
15
15
|
|
16
16
|
if TYPE_CHECKING:
|
17
|
-
from .
|
17
|
+
from ._types import Data
|
18
18
|
|
19
19
|
|
20
20
|
def is_sequence(
|
21
|
-
value:
|
22
|
-
) -> TypeGuard[Sequence[
|
21
|
+
value: Data,
|
22
|
+
) -> TypeGuard[Sequence[Data]]:
|
23
23
|
return isinstance(value, Sequence) and not isinstance(value, str)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.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
|
@@ -69,13 +69,19 @@ Requires-Dist: foamlib[docs]; extra == "dev"
|
|
69
69
|
[](https://github.com/gerlero/foamlib/actions/workflows/docker.yml)
|
70
70
|
[](https://hub.docker.com/r/microfluidica/foamlib/)
|
71
71
|
|
72
|
-
|
72
|
+
**foamlib** provides a simple, modern, ergonomic and fast Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
|
73
|
+
|
74
|
+
<p align="center">
|
75
|
+
<img alt="benchmark" src="https://github.com/gerlero/foamlib/raw/main/benchmark.png" height="250">
|
76
|
+
<br>
|
77
|
+
<i>Parsing a </i>volVectorField<i> with 200k cells.</i>
|
78
|
+
</p>
|
73
79
|
|
74
|
-
|
80
|
+
## 👋 Basics
|
75
81
|
|
76
|
-
|
82
|
+
**foamlib** offers the following Python classes:
|
77
83
|
|
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).
|
84
|
+
* [`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 and in-place editor. Supports ASCII and binary field formats (with or without compression).
|
79
85
|
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for configuring, running, and accessing the results of OpenFOAM cases.
|
80
86
|
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
81
87
|
* [`AsyncSlurmFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncSlurmFoamCase): subclass of `AsyncFoamCase` used for running cases on a Slurm cluster.
|
@@ -185,7 +191,7 @@ async def cost(x):
|
|
185
191
|
await clone.run(fallback=True) # Run locally if Slurm is not available
|
186
192
|
return abs(clone[-1]["U"].internal_field[0][0])
|
187
193
|
|
188
|
-
result = differential_evolution(cost, bounds=[(-1, 1)], workers=
|
194
|
+
result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncSlurmFoamCase.map, polish=False)
|
189
195
|
```
|
190
196
|
|
191
197
|
### 📄 Use it to create a `run` (or `clean`) script
|
@@ -17,9 +17,9 @@ foamlib/_cases/_subprocess.py
|
|
17
17
|
foamlib/_cases/_sync.py
|
18
18
|
foamlib/_cases/_util.py
|
19
19
|
foamlib/_files/__init__.py
|
20
|
-
foamlib/_files/_base.py
|
21
20
|
foamlib/_files/_files.py
|
22
21
|
foamlib/_files/_io.py
|
23
22
|
foamlib/_files/_parsing.py
|
24
23
|
foamlib/_files/_serialization.py
|
24
|
+
foamlib/_files/_types.py
|
25
25
|
foamlib/_files/_util.py
|
@@ -1,76 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import sys
|
4
|
-
from dataclasses import dataclass
|
5
|
-
from typing import TYPE_CHECKING, Dict, NamedTuple, Optional, Tuple, Union
|
6
|
-
|
7
|
-
if TYPE_CHECKING:
|
8
|
-
import numpy as np
|
9
|
-
|
10
|
-
if sys.version_info >= (3, 9):
|
11
|
-
from collections.abc import Mapping, MutableMapping, Sequence
|
12
|
-
else:
|
13
|
-
from typing import Mapping, MutableMapping, Sequence
|
14
|
-
|
15
|
-
|
16
|
-
class FoamFileBase:
|
17
|
-
class DimensionSet(NamedTuple):
|
18
|
-
mass: float = 0
|
19
|
-
length: float = 0
|
20
|
-
time: float = 0
|
21
|
-
temperature: float = 0
|
22
|
-
moles: float = 0
|
23
|
-
current: float = 0
|
24
|
-
luminous_intensity: float = 0
|
25
|
-
|
26
|
-
def __repr__(self) -> str:
|
27
|
-
return f"{type(self).__qualname__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
|
28
|
-
|
29
|
-
_Tensor = Union[
|
30
|
-
float,
|
31
|
-
Sequence[float],
|
32
|
-
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
33
|
-
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
34
|
-
]
|
35
|
-
|
36
|
-
@dataclass
|
37
|
-
class Dimensioned:
|
38
|
-
value: FoamFileBase._Tensor = 0
|
39
|
-
dimensions: FoamFileBase.DimensionSet | Sequence[float] = ()
|
40
|
-
name: str | None = None
|
41
|
-
|
42
|
-
def __post_init__(self) -> None:
|
43
|
-
if not isinstance(self.dimensions, FoamFileBase.DimensionSet):
|
44
|
-
self.dimensions = FoamFileBase.DimensionSet(*self.dimensions)
|
45
|
-
|
46
|
-
_Field = Union[
|
47
|
-
_Tensor, Sequence[_Tensor], "np.ndarray[Tuple[int, int], np.dtype[np.generic]]"
|
48
|
-
]
|
49
|
-
|
50
|
-
_DataEntry = Union[
|
51
|
-
str,
|
52
|
-
int,
|
53
|
-
float,
|
54
|
-
bool,
|
55
|
-
Dimensioned,
|
56
|
-
DimensionSet,
|
57
|
-
Sequence["Data"],
|
58
|
-
_Tensor,
|
59
|
-
_Field,
|
60
|
-
]
|
61
|
-
|
62
|
-
Data = Union[
|
63
|
-
_DataEntry,
|
64
|
-
Mapping[str, "Data"],
|
65
|
-
]
|
66
|
-
"""
|
67
|
-
A value that can be stored in an OpenFOAM file.
|
68
|
-
"""
|
69
|
-
|
70
|
-
_MutableData = Union[
|
71
|
-
_DataEntry,
|
72
|
-
MutableMapping[str, "_MutableData"],
|
73
|
-
]
|
74
|
-
|
75
|
-
_Dict = Dict[str, Union["Data", "_Dict"]]
|
76
|
-
_File = Dict[Optional[str], Union["Data", "_Dict"]]
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|