foamlib 0.9.2__tar.gz → 0.9.3__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.9.2 → foamlib-0.9.3}/PKG-INFO +1 -1
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/__init__.py +1 -1
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_parsing.py +3 -1
- foamlib-0.9.3/foamlib/_files/_types.py +252 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_files.py +5 -6
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_basic.py +10 -8
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_loads.py +12 -8
- foamlib-0.9.3/tests/test_files/test_types.py +62 -0
- foamlib-0.9.2/foamlib/_files/_types.py +0 -136
- {foamlib-0.9.2 → foamlib-0.9.3}/.devcontainer.json +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.dockerignore +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.git-blame-ignore-revs +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.github/dependabot.yml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/ci.yml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/docker.yml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/dockerhub-description.yml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/pypi-publish.yml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.gitignore +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/.readthedocs.yaml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/CONTRIBUTING.md +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/Dockerfile +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/LICENSE.txt +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/README.md +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/benchmark.png +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/benchmark.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/requirements.txt +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/ruff.toml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/Makefile +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/cases.rst +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/conf.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/files.rst +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/index.rst +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/make.bat +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/docs/ruff.toml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/__init__.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_async.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_base.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_run.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_slurm.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_subprocess.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_sync.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_util.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/__init__.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_files.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_io.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_serialization.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/py.typed +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/logo.png +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/pyproject.toml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/__init__.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/ruff.toml +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/__init__.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_cavity.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_cavity_async.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_flange.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_flange_async.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_example.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/__init__.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_dumps.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/__init__.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_advanced.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_decompose_par.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_fields.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_fv_schemes.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_intermediate.py +0 -0
- {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_poly_mesh.py +0 -0
@@ -363,7 +363,9 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
|
363
363
|
_DIRECTIVE = Word("#", _IDENTBODYCHARS)
|
364
364
|
_TOKEN = dbl_quoted_string | _DIRECTIVE | _IDENTIFIER
|
365
365
|
_DATA = Forward()
|
366
|
-
_KEYWORD_ENTRY = _keyword_entry_of(
|
366
|
+
_KEYWORD_ENTRY = _keyword_entry_of(
|
367
|
+
_TOKEN | _list_of(_IDENTIFIER), Opt(_DATA, default="")
|
368
|
+
)
|
367
369
|
_DICT = _dict_of(_TOKEN, _DATA)
|
368
370
|
_DATA_ENTRY = Forward()
|
369
371
|
_LIST_ENTRY = _DICT | _KEYWORD_ENTRY | _DATA_ENTRY
|
@@ -0,0 +1,252 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import sys
|
4
|
+
from typing import Any, Dict, NamedTuple, Optional, Union
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
|
8
|
+
if sys.version_info >= (3, 9):
|
9
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
10
|
+
else:
|
11
|
+
from typing import Mapping, MutableMapping, Sequence
|
12
|
+
|
13
|
+
if sys.version_info >= (3, 10):
|
14
|
+
from typing import TypeGuard
|
15
|
+
else:
|
16
|
+
from typing_extensions import TypeGuard
|
17
|
+
|
18
|
+
|
19
|
+
class DimensionSet(NamedTuple):
|
20
|
+
mass: float = 0
|
21
|
+
length: float = 0
|
22
|
+
time: float = 0
|
23
|
+
temperature: float = 0
|
24
|
+
moles: float = 0
|
25
|
+
current: float = 0
|
26
|
+
luminous_intensity: float = 0
|
27
|
+
|
28
|
+
def __repr__(self) -> str:
|
29
|
+
return f"{type(self).__name__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
|
30
|
+
|
31
|
+
def __add__(self, other: DimensionSet) -> DimensionSet: # type: ignore[override]
|
32
|
+
if not isinstance(other, DimensionSet):
|
33
|
+
return NotImplemented
|
34
|
+
|
35
|
+
if self != other:
|
36
|
+
msg = f"Cannot add DimensionSet with different dimensions: {self} + {other}"
|
37
|
+
raise ValueError(msg)
|
38
|
+
|
39
|
+
return self
|
40
|
+
|
41
|
+
def __sub__(self, other: DimensionSet) -> DimensionSet:
|
42
|
+
if not isinstance(other, DimensionSet):
|
43
|
+
return NotImplemented
|
44
|
+
|
45
|
+
if self != other:
|
46
|
+
msg = f"Cannot subtract DimensionSet with different dimensions: {self} - {other}"
|
47
|
+
raise ValueError(msg)
|
48
|
+
|
49
|
+
return self
|
50
|
+
|
51
|
+
def __mul__(self, other: DimensionSet) -> DimensionSet: # type: ignore[override]
|
52
|
+
if not isinstance(other, DimensionSet):
|
53
|
+
return NotImplemented
|
54
|
+
|
55
|
+
return DimensionSet(*(a + b for a, b in zip(self, other)))
|
56
|
+
|
57
|
+
def __truediv__(self, other: DimensionSet) -> DimensionSet:
|
58
|
+
if not isinstance(other, DimensionSet):
|
59
|
+
return NotImplemented
|
60
|
+
|
61
|
+
return DimensionSet(*(a - b for a, b in zip(self, other)))
|
62
|
+
|
63
|
+
def __pow__(self, exponent: float) -> DimensionSet:
|
64
|
+
if not isinstance(exponent, (int, float)):
|
65
|
+
return NotImplemented
|
66
|
+
|
67
|
+
return DimensionSet(*(a * exponent for a in self))
|
68
|
+
|
69
|
+
def __bool__(self) -> bool:
|
70
|
+
return any(v != 0 for v in self)
|
71
|
+
|
72
|
+
|
73
|
+
Tensor = Union[
|
74
|
+
float,
|
75
|
+
"np.ndarray[tuple[int], np.dtype[np.float64]]",
|
76
|
+
]
|
77
|
+
|
78
|
+
TensorLike = Union[
|
79
|
+
Sequence[float],
|
80
|
+
"np.ndarray[tuple[()], np.dtype[np.float64]]",
|
81
|
+
Tensor,
|
82
|
+
]
|
83
|
+
|
84
|
+
|
85
|
+
class Dimensioned:
|
86
|
+
def __init__(
|
87
|
+
self,
|
88
|
+
value: TensorLike,
|
89
|
+
dimensions: DimensionSet | Sequence[float],
|
90
|
+
name: str | None = None,
|
91
|
+
) -> None:
|
92
|
+
if is_sequence(value):
|
93
|
+
self.value: Tensor = np.array(value, dtype=float) # type: ignore [assignment]
|
94
|
+
else:
|
95
|
+
assert isinstance(value, (int, float, np.ndarray))
|
96
|
+
self.value = float(value)
|
97
|
+
|
98
|
+
if not isinstance(dimensions, DimensionSet):
|
99
|
+
self.dimensions = DimensionSet(*dimensions)
|
100
|
+
else:
|
101
|
+
self.dimensions = dimensions
|
102
|
+
|
103
|
+
self.name = name
|
104
|
+
|
105
|
+
def __repr__(self) -> str:
|
106
|
+
if self.name is not None:
|
107
|
+
return (
|
108
|
+
f"{type(self).__name__}({self.value}, {self.dimensions}, {self.name})"
|
109
|
+
)
|
110
|
+
return f"{type(self).__name__}({self.value}, {self.dimensions})"
|
111
|
+
|
112
|
+
def __add__(self, other: Dimensioned | Tensor) -> Dimensioned:
|
113
|
+
if not isinstance(other, Dimensioned):
|
114
|
+
other = Dimensioned(other, DimensionSet())
|
115
|
+
|
116
|
+
return Dimensioned(
|
117
|
+
self.value + other.value, # type: ignore [arg-type]
|
118
|
+
self.dimensions + other.dimensions,
|
119
|
+
f"{self.name} + {other.name}"
|
120
|
+
if self.name is not None and other.name is not None
|
121
|
+
else None,
|
122
|
+
)
|
123
|
+
|
124
|
+
def __sub__(self, other: Dimensioned | Tensor) -> Dimensioned:
|
125
|
+
if not isinstance(other, Dimensioned):
|
126
|
+
other = Dimensioned(other, DimensionSet())
|
127
|
+
|
128
|
+
return Dimensioned(
|
129
|
+
self.value - other.value, # type: ignore [arg-type]
|
130
|
+
self.dimensions - other.dimensions,
|
131
|
+
f"{self.name} - {other.name}"
|
132
|
+
if self.name is not None and other.name is not None
|
133
|
+
else None,
|
134
|
+
)
|
135
|
+
|
136
|
+
def __mul__(self, other: Dimensioned | Tensor) -> Dimensioned:
|
137
|
+
if not isinstance(other, Dimensioned):
|
138
|
+
other = Dimensioned(other, DimensionSet())
|
139
|
+
|
140
|
+
return Dimensioned(
|
141
|
+
self.value * other.value, # type: ignore [arg-type]
|
142
|
+
self.dimensions * other.dimensions,
|
143
|
+
f"{self.name} * {other.name}"
|
144
|
+
if self.name is not None and other.name is not None
|
145
|
+
else None,
|
146
|
+
)
|
147
|
+
|
148
|
+
def __truediv__(self, other: Dimensioned | Tensor) -> Dimensioned:
|
149
|
+
if not isinstance(other, Dimensioned):
|
150
|
+
other = Dimensioned(other, DimensionSet())
|
151
|
+
|
152
|
+
return Dimensioned(
|
153
|
+
self.value / other.value, # type: ignore [arg-type]
|
154
|
+
self.dimensions / other.dimensions,
|
155
|
+
f"{self.name} / {other.name}"
|
156
|
+
if self.name is not None and other.name is not None
|
157
|
+
else None,
|
158
|
+
)
|
159
|
+
|
160
|
+
def __pow__(self, exponent: float) -> Dimensioned:
|
161
|
+
if not isinstance(exponent, (int, float)):
|
162
|
+
return NotImplemented
|
163
|
+
|
164
|
+
return Dimensioned(
|
165
|
+
self.value**exponent, # type: ignore [arg-type]
|
166
|
+
self.dimensions**exponent,
|
167
|
+
f"{self.name} ** {exponent}" if self.name is not None else None,
|
168
|
+
)
|
169
|
+
|
170
|
+
def __float__(self) -> float:
|
171
|
+
if self.dimensions:
|
172
|
+
msg = f"Cannot convert non-dimensionless Dimensioned object to float: {self.dimensions}"
|
173
|
+
raise ValueError(msg)
|
174
|
+
return float(self.value)
|
175
|
+
|
176
|
+
def __int__(self) -> int:
|
177
|
+
if self.dimensions:
|
178
|
+
msg = f"Cannot convert non-dimensionless Dimensioned object to int: {self.dimensions}"
|
179
|
+
raise ValueError(msg)
|
180
|
+
return int(self.value)
|
181
|
+
|
182
|
+
def __array__(
|
183
|
+
self, dtype: Any = None, *, copy: Any = None
|
184
|
+
) -> np.ndarray[tuple[()] | tuple[int], np.dtype[np.float64]]:
|
185
|
+
if self.dimensions:
|
186
|
+
msg = f"Cannot convert non-dimensionless Dimensioned object to array: {self.dimensions}"
|
187
|
+
raise ValueError(msg)
|
188
|
+
return np.array(self.value, dtype=dtype, copy=copy)
|
189
|
+
|
190
|
+
|
191
|
+
Field = Union[
|
192
|
+
float,
|
193
|
+
"np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]",
|
194
|
+
]
|
195
|
+
|
196
|
+
FieldLike = Union[
|
197
|
+
TensorLike,
|
198
|
+
Sequence[TensorLike],
|
199
|
+
Sequence[Sequence[TensorLike]],
|
200
|
+
Field,
|
201
|
+
]
|
202
|
+
|
203
|
+
|
204
|
+
Data = Union[
|
205
|
+
str,
|
206
|
+
int,
|
207
|
+
float,
|
208
|
+
bool,
|
209
|
+
Dimensioned,
|
210
|
+
DimensionSet,
|
211
|
+
Sequence["Entry"],
|
212
|
+
Field,
|
213
|
+
]
|
214
|
+
|
215
|
+
Entry = Union[
|
216
|
+
Data,
|
217
|
+
Mapping[str, "Entry"],
|
218
|
+
]
|
219
|
+
"""
|
220
|
+
A value that can be stored in an OpenFOAM file.
|
221
|
+
"""
|
222
|
+
|
223
|
+
DataLike = Union[
|
224
|
+
FieldLike,
|
225
|
+
Sequence["EntryLike"],
|
226
|
+
Data,
|
227
|
+
]
|
228
|
+
|
229
|
+
EntryLike = Union[
|
230
|
+
DataLike,
|
231
|
+
Mapping[str, "EntryLike"],
|
232
|
+
]
|
233
|
+
|
234
|
+
|
235
|
+
def is_sequence(
|
236
|
+
value: EntryLike,
|
237
|
+
) -> TypeGuard[
|
238
|
+
Sequence[EntryLike]
|
239
|
+
| np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]
|
240
|
+
]:
|
241
|
+
return (isinstance(value, Sequence) and not isinstance(value, str)) or (
|
242
|
+
isinstance(value, np.ndarray) and value.ndim > 0
|
243
|
+
)
|
244
|
+
|
245
|
+
|
246
|
+
MutableEntry = Union[
|
247
|
+
Data,
|
248
|
+
MutableMapping[str, "MutableEntry"],
|
249
|
+
]
|
250
|
+
|
251
|
+
Dict_ = Dict[str, Union["Entry", "Dict_"]]
|
252
|
+
File = Dict[Optional[str], Union["Entry", "Dict_"]]
|
@@ -78,13 +78,12 @@ def test_write_read(tmp_path: Path) -> None:
|
|
78
78
|
assert sd["nestedList"] == [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
79
79
|
|
80
80
|
sd["g"] = FoamFile.Dimensioned(
|
81
|
-
name="g", dimensions=[
|
82
|
-
)
|
83
|
-
assert sd["g"] == FoamFile.Dimensioned(
|
84
|
-
name="g",
|
85
|
-
dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2),
|
86
|
-
value=[0, 0, -9.81],
|
81
|
+
name="g", dimensions=[0, 1, -2, 0, 0, 0, 0], value=[0, 0, -9.81]
|
87
82
|
)
|
83
|
+
assert isinstance(sd["g"], FoamFile.Dimensioned)
|
84
|
+
assert sd["g"].name == "g"
|
85
|
+
assert sd["g"].dimensions == FoamFile.DimensionSet(length=1, time=-2)
|
86
|
+
assert np.array_equal(sd["g"].value, [0, 0, -9.81])
|
88
87
|
|
89
88
|
sd["n"] = 1
|
90
89
|
sd["y"] = 2
|
@@ -95,14 +95,16 @@ def test_parse_value() -> None:
|
|
95
95
|
assert Parsed(b"[1 1 -2 0 0 0 0]")[()] == FoamFile.DimensionSet(
|
96
96
|
mass=1, length=1, time=-2
|
97
97
|
)
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
)
|
98
|
+
dimensioned = Parsed(b"g [1 1 -2 0 0 0 0] (0 0 -9.81)")[()]
|
99
|
+
assert isinstance(dimensioned, FoamFile.Dimensioned)
|
100
|
+
assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
101
|
+
assert np.array_equal(dimensioned.value, [0, 0, -9.81])
|
102
|
+
assert dimensioned.name == "g"
|
103
|
+
dimensioned = Parsed(b"[1 1 -2 0 0 0 0] 9.81")[()]
|
104
|
+
assert isinstance(dimensioned, FoamFile.Dimensioned)
|
105
|
+
assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
106
|
+
assert dimensioned.value == 9.81
|
107
|
+
assert dimensioned.name is None
|
106
108
|
tpl = Parsed(b"hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)")[()]
|
107
109
|
assert isinstance(tpl, tuple)
|
108
110
|
assert len(tpl) == 5
|
@@ -18,6 +18,7 @@ def test_loads() -> None:
|
|
18
18
|
assert FoamFile.loads("word word") == ("word", "word")
|
19
19
|
assert FoamFile.loads('"a string"') == '"a string"'
|
20
20
|
assert FoamFile.loads("(word word)") == ["word", "word"]
|
21
|
+
assert FoamFile.loads("keyword value;") == {"keyword": "value"}
|
21
22
|
assert FoamFile.loads("uniform 1") == 1
|
22
23
|
assert FoamFile.loads("uniform 1.0") == 1.0
|
23
24
|
assert FoamFile.loads("uniform 1.0e-3") == 1.0e-3
|
@@ -57,13 +58,16 @@ def test_loads() -> None:
|
|
57
58
|
assert FoamFile.loads("[1 1 -2 0 0 0 0]") == FoamFile.DimensionSet(
|
58
59
|
mass=1, length=1, time=-2
|
59
60
|
)
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
)
|
61
|
+
dimensioned = FoamFile.loads("g [1 1 -2 0 0 0 0] (0 0 -9.81)")
|
62
|
+
assert isinstance(dimensioned, FoamFile.Dimensioned)
|
63
|
+
assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
64
|
+
assert np.array_equal(dimensioned.value, [0, 0, -9.81])
|
65
|
+
assert dimensioned.name == "g"
|
66
|
+
dimensioned = FoamFile.loads("[1 1 -2 0 0 0 0] 9.81")
|
67
|
+
assert isinstance(dimensioned, FoamFile.Dimensioned)
|
68
|
+
assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
69
|
+
assert dimensioned.value == 9.81
|
70
|
+
assert dimensioned.name is None
|
68
71
|
assert FoamFile.loads("a {b c; d e;}") == {"a": {"b": "c", "d": "e"}}
|
69
72
|
assert FoamFile.loads("(a b; c d;)") == [("a", "b"), ("c", "d")]
|
73
|
+
assert FoamFile.loads("keyword;") == {"keyword": ""}
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import numpy as np
|
2
|
+
import pytest
|
3
|
+
from foamlib import FoamFile
|
4
|
+
|
5
|
+
|
6
|
+
def test_dimension_set() -> None:
|
7
|
+
dims = FoamFile.DimensionSet(length=1, time=-2)
|
8
|
+
assert dims.length == 1
|
9
|
+
assert dims.time == -2
|
10
|
+
assert dims.mass == 0
|
11
|
+
|
12
|
+
assert dims + dims == dims
|
13
|
+
assert dims - dims == dims
|
14
|
+
assert dims * dims == FoamFile.DimensionSet(length=2, time=-4)
|
15
|
+
assert dims / dims == FoamFile.DimensionSet()
|
16
|
+
assert dims**2 == FoamFile.DimensionSet(length=2, time=-4)
|
17
|
+
|
18
|
+
assert dims
|
19
|
+
assert not FoamFile.DimensionSet()
|
20
|
+
|
21
|
+
|
22
|
+
def test_dimensioned() -> None:
|
23
|
+
dimensioned = FoamFile.Dimensioned(
|
24
|
+
9.81, FoamFile.DimensionSet(length=1, time=-2), "g"
|
25
|
+
)
|
26
|
+
assert dimensioned.value == 9.81
|
27
|
+
assert dimensioned.dimensions == FoamFile.DimensionSet(length=1, time=-2)
|
28
|
+
assert dimensioned.name == "g"
|
29
|
+
|
30
|
+
result = dimensioned + dimensioned
|
31
|
+
assert result.value == 9.81 * 2
|
32
|
+
assert result.dimensions == FoamFile.DimensionSet(length=1, time=-2)
|
33
|
+
assert result.name == "g + g"
|
34
|
+
|
35
|
+
result = dimensioned - dimensioned
|
36
|
+
assert result.value == 0.0
|
37
|
+
assert result.dimensions == FoamFile.DimensionSet(length=1, time=-2)
|
38
|
+
assert result.name == "g - g"
|
39
|
+
|
40
|
+
result = dimensioned * dimensioned
|
41
|
+
assert result.value == 9.81**2
|
42
|
+
assert result.dimensions == FoamFile.DimensionSet(length=2, time=-4)
|
43
|
+
assert result.name == "g * g"
|
44
|
+
|
45
|
+
result = dimensioned / dimensioned
|
46
|
+
assert result.value == 1.0
|
47
|
+
assert result.dimensions == FoamFile.DimensionSet()
|
48
|
+
assert result.name == "g / g"
|
49
|
+
|
50
|
+
result = dimensioned**2
|
51
|
+
assert result.value == 9.81**2
|
52
|
+
assert result.dimensions == FoamFile.DimensionSet(length=2, time=-4)
|
53
|
+
assert result.name == "g ** 2"
|
54
|
+
|
55
|
+
with pytest.raises(ValueError, match="dimension"):
|
56
|
+
dimensioned + 1
|
57
|
+
|
58
|
+
with pytest.raises(ValueError, match="dimension"):
|
59
|
+
float(dimensioned)
|
60
|
+
|
61
|
+
with pytest.raises(ValueError, match="dimension"):
|
62
|
+
np.array(dimensioned)
|
@@ -1,136 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import sys
|
4
|
-
from typing import Dict, NamedTuple, Optional, Union
|
5
|
-
|
6
|
-
import numpy as np
|
7
|
-
|
8
|
-
if sys.version_info >= (3, 9):
|
9
|
-
from collections.abc import Mapping, MutableMapping, Sequence
|
10
|
-
else:
|
11
|
-
from typing import Mapping, MutableMapping, Sequence
|
12
|
-
|
13
|
-
if sys.version_info >= (3, 10):
|
14
|
-
from typing import TypeGuard
|
15
|
-
else:
|
16
|
-
from typing_extensions import TypeGuard
|
17
|
-
|
18
|
-
|
19
|
-
class DimensionSet(NamedTuple):
|
20
|
-
mass: float = 0
|
21
|
-
length: float = 0
|
22
|
-
time: float = 0
|
23
|
-
temperature: float = 0
|
24
|
-
moles: float = 0
|
25
|
-
current: float = 0
|
26
|
-
luminous_intensity: float = 0
|
27
|
-
|
28
|
-
def __repr__(self) -> str:
|
29
|
-
return f"{type(self).__name__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
|
30
|
-
|
31
|
-
|
32
|
-
Tensor = Union[
|
33
|
-
float,
|
34
|
-
"np.ndarray[tuple[int], np.dtype[np.float64]]",
|
35
|
-
]
|
36
|
-
|
37
|
-
TensorLike = Union[
|
38
|
-
Sequence[float],
|
39
|
-
"np.ndarray[tuple[()], np.dtype[np.float64]]",
|
40
|
-
Tensor,
|
41
|
-
]
|
42
|
-
|
43
|
-
|
44
|
-
class Dimensioned:
|
45
|
-
def __init__(
|
46
|
-
self,
|
47
|
-
value: TensorLike,
|
48
|
-
dimensions: DimensionSet | Sequence[float],
|
49
|
-
name: str | None = None,
|
50
|
-
) -> None:
|
51
|
-
if is_sequence(value):
|
52
|
-
self.value: Tensor = np.array(value, dtype=float) # type: ignore [assignment]
|
53
|
-
else:
|
54
|
-
assert isinstance(value, (int, float, np.ndarray))
|
55
|
-
self.value = float(value)
|
56
|
-
|
57
|
-
if not isinstance(dimensions, DimensionSet):
|
58
|
-
self.dimensions = DimensionSet(*dimensions)
|
59
|
-
else:
|
60
|
-
self.dimensions = dimensions
|
61
|
-
|
62
|
-
self.name = name
|
63
|
-
|
64
|
-
def __eq__(self, other: object) -> bool:
|
65
|
-
if not isinstance(other, Dimensioned):
|
66
|
-
return NotImplemented
|
67
|
-
|
68
|
-
return (
|
69
|
-
self.dimensions == other.dimensions
|
70
|
-
and np.array_equal(self.value, other.value)
|
71
|
-
and self.name == other.name
|
72
|
-
)
|
73
|
-
|
74
|
-
|
75
|
-
Field = Union[
|
76
|
-
float,
|
77
|
-
"np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]",
|
78
|
-
]
|
79
|
-
|
80
|
-
FieldLike = Union[
|
81
|
-
TensorLike,
|
82
|
-
Sequence[TensorLike],
|
83
|
-
Sequence[Sequence[TensorLike]],
|
84
|
-
Field,
|
85
|
-
]
|
86
|
-
|
87
|
-
|
88
|
-
Data = Union[
|
89
|
-
str,
|
90
|
-
int,
|
91
|
-
float,
|
92
|
-
bool,
|
93
|
-
Dimensioned,
|
94
|
-
DimensionSet,
|
95
|
-
Sequence["Entry"],
|
96
|
-
Field,
|
97
|
-
]
|
98
|
-
|
99
|
-
Entry = Union[
|
100
|
-
Data,
|
101
|
-
Mapping[str, "Entry"],
|
102
|
-
]
|
103
|
-
"""
|
104
|
-
A value that can be stored in an OpenFOAM file.
|
105
|
-
"""
|
106
|
-
|
107
|
-
DataLike = Union[
|
108
|
-
FieldLike,
|
109
|
-
Sequence["EntryLike"],
|
110
|
-
Data,
|
111
|
-
]
|
112
|
-
|
113
|
-
EntryLike = Union[
|
114
|
-
DataLike,
|
115
|
-
Mapping[str, "EntryLike"],
|
116
|
-
]
|
117
|
-
|
118
|
-
|
119
|
-
def is_sequence(
|
120
|
-
value: EntryLike,
|
121
|
-
) -> TypeGuard[
|
122
|
-
Sequence[EntryLike]
|
123
|
-
| np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]
|
124
|
-
]:
|
125
|
-
return (isinstance(value, Sequence) and not isinstance(value, str)) or (
|
126
|
-
isinstance(value, np.ndarray) and value.ndim > 0
|
127
|
-
)
|
128
|
-
|
129
|
-
|
130
|
-
MutableEntry = Union[
|
131
|
-
Data,
|
132
|
-
MutableMapping[str, "MutableEntry"],
|
133
|
-
]
|
134
|
-
|
135
|
-
Dict_ = Dict[str, Union["Entry", "Dict_"]]
|
136
|
-
File = Dict[Optional[str], Union["Entry", "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
|
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
|
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
|
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
|