foamlib 0.2.2__py3-none-any.whl → 0.2.3__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 +3 -2
- foamlib/_dictionaries/__init__.py +8 -0
- foamlib/_dictionaries/_base.py +43 -0
- foamlib/_dictionaries/_files.py +382 -0
- foamlib/_dictionaries/_parsing.py +177 -0
- foamlib/_dictionaries/_serialization.py +103 -0
- {foamlib-0.2.2.dist-info → foamlib-0.2.3.dist-info}/METADATA +1 -1
- foamlib-0.2.3.dist-info/RECORD +14 -0
- foamlib/_dictionaries.py +0 -491
- foamlib-0.2.2.dist-info/RECORD +0 -10
- {foamlib-0.2.2.dist-info → foamlib-0.2.3.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.2.2.dist-info → foamlib-0.2.3.dist-info}/WHEEL +0 -0
- {foamlib-0.2.2.dist-info → foamlib-0.2.3.dist-info}/top_level.txt +0 -0
foamlib/__init__.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
__version__ = "0.2.
|
1
|
+
__version__ = "0.2.3"
|
2
2
|
|
3
3
|
from ._cases import FoamCase, AsyncFoamCase, FoamCaseBase
|
4
|
-
from ._dictionaries import FoamFile, FoamFieldFile
|
4
|
+
from ._dictionaries import FoamFile, FoamFieldFile, FoamDictionaryBase
|
5
5
|
|
6
6
|
__all__ = [
|
7
7
|
"FoamCase",
|
@@ -9,4 +9,5 @@ __all__ = [
|
|
9
9
|
"FoamCaseBase",
|
10
10
|
"FoamFile",
|
11
11
|
"FoamFieldFile",
|
12
|
+
"FoamDictionaryBase",
|
12
13
|
]
|
@@ -0,0 +1,43 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Dict, NamedTuple, Optional, Sequence, Union
|
4
|
+
|
5
|
+
|
6
|
+
class FoamDictionaryBase:
|
7
|
+
class DimensionSet(NamedTuple):
|
8
|
+
mass: Union[int, float] = 0
|
9
|
+
length: Union[int, float] = 0
|
10
|
+
time: Union[int, float] = 0
|
11
|
+
temperature: Union[int, float] = 0
|
12
|
+
moles: Union[int, float] = 0
|
13
|
+
current: Union[int, float] = 0
|
14
|
+
luminous_intensity: Union[int, float] = 0
|
15
|
+
|
16
|
+
def __repr__(self) -> str:
|
17
|
+
return f"{type(self).__qualname__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class Dimensioned:
|
21
|
+
value: Union[int, float, Sequence[Union[int, float]]] = 0
|
22
|
+
dimensions: Union[
|
23
|
+
"FoamDictionaryBase.DimensionSet", Sequence[Union[int, float]]
|
24
|
+
] = ()
|
25
|
+
name: Optional[str] = None
|
26
|
+
|
27
|
+
def __post_init__(self) -> None:
|
28
|
+
if not isinstance(self.dimensions, FoamDictionaryBase.DimensionSet):
|
29
|
+
self.dimensions = FoamDictionaryBase.DimensionSet(*self.dimensions)
|
30
|
+
|
31
|
+
Value = Union[str, int, float, bool, Dimensioned, DimensionSet, Sequence["Value"]]
|
32
|
+
"""
|
33
|
+
A value that can be stored in an OpenFOAM dictionary.
|
34
|
+
"""
|
35
|
+
|
36
|
+
_Dict = Dict[str, Union["FoamDictionaryBase.Value", "_Dict"]]
|
37
|
+
|
38
|
+
@abstractmethod
|
39
|
+
def as_dict(self) -> _Dict:
|
40
|
+
"""
|
41
|
+
Return a nested dict representation of the dictionary.
|
42
|
+
"""
|
43
|
+
raise NotImplementedError
|
@@ -0,0 +1,382 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import (
|
3
|
+
Any,
|
4
|
+
Iterator,
|
5
|
+
Mapping,
|
6
|
+
MutableMapping,
|
7
|
+
Optional,
|
8
|
+
Sequence,
|
9
|
+
Tuple,
|
10
|
+
Union,
|
11
|
+
cast,
|
12
|
+
)
|
13
|
+
|
14
|
+
from ._base import FoamDictionaryBase
|
15
|
+
from ._parsing import Parsed, as_dict, get_entry_locn, get_value, parse
|
16
|
+
from ._serialization import serialize_value
|
17
|
+
|
18
|
+
try:
|
19
|
+
import numpy as np
|
20
|
+
from numpy.typing import NDArray
|
21
|
+
except ModuleNotFoundError:
|
22
|
+
pass
|
23
|
+
|
24
|
+
|
25
|
+
class FoamFile(
|
26
|
+
FoamDictionaryBase,
|
27
|
+
MutableMapping[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
|
28
|
+
):
|
29
|
+
"""
|
30
|
+
An OpenFOAM dictionary file.
|
31
|
+
|
32
|
+
Use as a mutable mapping (i.e., like a dict) to access and modify entries.
|
33
|
+
|
34
|
+
Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
|
35
|
+
"""
|
36
|
+
|
37
|
+
class Dictionary(
|
38
|
+
FoamDictionaryBase,
|
39
|
+
MutableMapping[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
|
40
|
+
):
|
41
|
+
"""
|
42
|
+
An OpenFOAM dictionary within a file as a mutable mapping.
|
43
|
+
"""
|
44
|
+
|
45
|
+
def __init__(self, _file: "FoamFile", _keywords: Sequence[str]) -> None:
|
46
|
+
self._file = _file
|
47
|
+
self._keywords = _keywords
|
48
|
+
|
49
|
+
def __getitem__(
|
50
|
+
self, keyword: str
|
51
|
+
) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
|
52
|
+
return self._file[(*self._keywords, keyword)]
|
53
|
+
|
54
|
+
def _setitem(
|
55
|
+
self,
|
56
|
+
keyword: str,
|
57
|
+
value: Any,
|
58
|
+
*,
|
59
|
+
assume_field: bool = False,
|
60
|
+
assume_dimensions: bool = False,
|
61
|
+
) -> None:
|
62
|
+
self._file._setitem(
|
63
|
+
(*self._keywords, keyword),
|
64
|
+
value,
|
65
|
+
assume_field=assume_field,
|
66
|
+
assume_dimensions=assume_dimensions,
|
67
|
+
)
|
68
|
+
|
69
|
+
def __setitem__(self, keyword: str, value: Any) -> None:
|
70
|
+
self._setitem(keyword, value)
|
71
|
+
|
72
|
+
def __delitem__(self, keyword: str) -> None:
|
73
|
+
del self._file[(*self._keywords, keyword)]
|
74
|
+
|
75
|
+
def __iter__(self) -> Iterator[str]:
|
76
|
+
return self._file._iter(tuple(self._keywords))
|
77
|
+
|
78
|
+
def __len__(self) -> int:
|
79
|
+
return len(list(iter(self)))
|
80
|
+
|
81
|
+
def __repr__(self) -> str:
|
82
|
+
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
83
|
+
|
84
|
+
def as_dict(self) -> FoamDictionaryBase._Dict:
|
85
|
+
"""
|
86
|
+
Return a nested dict representation of the dictionary.
|
87
|
+
"""
|
88
|
+
ret = self._file.as_dict()
|
89
|
+
|
90
|
+
for k in self._keywords:
|
91
|
+
assert isinstance(ret, dict)
|
92
|
+
v = ret[k]
|
93
|
+
assert isinstance(v, dict)
|
94
|
+
ret = v
|
95
|
+
|
96
|
+
return ret
|
97
|
+
|
98
|
+
def __init__(self, path: Union[str, Path]) -> None:
|
99
|
+
self.path = Path(path).absolute()
|
100
|
+
if self.path.is_dir():
|
101
|
+
raise IsADirectoryError(self.path)
|
102
|
+
elif not self.path.is_file():
|
103
|
+
raise FileNotFoundError(self.path)
|
104
|
+
|
105
|
+
self._contents: Optional[str] = None
|
106
|
+
self._parsed: Optional[Parsed] = None
|
107
|
+
self._defer_io = 0
|
108
|
+
self._dirty = False
|
109
|
+
|
110
|
+
def __enter__(self) -> "FoamFile":
|
111
|
+
if self._defer_io == 0:
|
112
|
+
self._read()
|
113
|
+
self._defer_io += 1
|
114
|
+
return self
|
115
|
+
|
116
|
+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
|
117
|
+
self._defer_io -= 1
|
118
|
+
if self._defer_io == 0 and self._dirty:
|
119
|
+
assert self._contents is not None
|
120
|
+
self._write(self._contents)
|
121
|
+
assert not self._dirty
|
122
|
+
|
123
|
+
def _read(self) -> Tuple[str, Parsed]:
|
124
|
+
if not self._defer_io:
|
125
|
+
contents = self.path.read_text()
|
126
|
+
if contents != self._contents:
|
127
|
+
self._contents = contents
|
128
|
+
self._parsed = None
|
129
|
+
|
130
|
+
assert self._contents is not None
|
131
|
+
|
132
|
+
if self._parsed is None:
|
133
|
+
self._parsed = parse(self._contents)
|
134
|
+
|
135
|
+
return self._contents, self._parsed
|
136
|
+
|
137
|
+
def _write(self, contents: str) -> None:
|
138
|
+
self._contents = contents
|
139
|
+
self._parsed = None
|
140
|
+
if not self._defer_io:
|
141
|
+
self.path.write_text(contents)
|
142
|
+
self._dirty = False
|
143
|
+
else:
|
144
|
+
self._dirty = True
|
145
|
+
|
146
|
+
def __getitem__(
|
147
|
+
self, keywords: Union[str, Tuple[str, ...]]
|
148
|
+
) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
|
149
|
+
if not isinstance(keywords, tuple):
|
150
|
+
keywords = (keywords,)
|
151
|
+
|
152
|
+
_, parsed = self._read()
|
153
|
+
|
154
|
+
value = get_value(parsed, keywords)
|
155
|
+
|
156
|
+
if value is None:
|
157
|
+
return FoamFile.Dictionary(self, keywords)
|
158
|
+
else:
|
159
|
+
return value
|
160
|
+
|
161
|
+
def _setitem(
|
162
|
+
self,
|
163
|
+
keywords: Union[str, Tuple[str, ...]],
|
164
|
+
value: Any,
|
165
|
+
*,
|
166
|
+
assume_field: bool = False,
|
167
|
+
assume_dimensions: bool = False,
|
168
|
+
) -> None:
|
169
|
+
if not isinstance(keywords, tuple):
|
170
|
+
keywords = (keywords,)
|
171
|
+
|
172
|
+
contents, parsed = self._read()
|
173
|
+
|
174
|
+
if isinstance(value, Mapping):
|
175
|
+
with self:
|
176
|
+
if isinstance(value, FoamDictionaryBase):
|
177
|
+
value = value.as_dict()
|
178
|
+
|
179
|
+
start, end = get_entry_locn(parsed, keywords, missing_ok=True)
|
180
|
+
|
181
|
+
self._write(
|
182
|
+
f"{contents[:start]} {keywords[-1]} {{\n}}\n {contents[end:]}"
|
183
|
+
)
|
184
|
+
|
185
|
+
for k, v in value.items():
|
186
|
+
self[(*keywords, k)] = v
|
187
|
+
else:
|
188
|
+
start, end = get_entry_locn(parsed, keywords, missing_ok=True)
|
189
|
+
|
190
|
+
value = serialize_value(
|
191
|
+
value, assume_field=assume_field, assume_dimensions=assume_dimensions
|
192
|
+
)
|
193
|
+
|
194
|
+
self._write(
|
195
|
+
f"{contents[:start]} {keywords[-1]} {value};\n {contents[end:]}"
|
196
|
+
)
|
197
|
+
|
198
|
+
def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
|
199
|
+
self._setitem(keywords, value)
|
200
|
+
|
201
|
+
def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
|
202
|
+
if not isinstance(keywords, tuple):
|
203
|
+
keywords = (keywords,)
|
204
|
+
|
205
|
+
contents, parsed = self._read()
|
206
|
+
|
207
|
+
start, end = get_entry_locn(parsed, keywords)
|
208
|
+
|
209
|
+
self._write(contents[:start] + contents[end:])
|
210
|
+
|
211
|
+
def _iter(self, keywords: Union[str, Tuple[str, ...]] = ()) -> Iterator[str]:
|
212
|
+
if not isinstance(keywords, tuple):
|
213
|
+
keywords = (keywords,)
|
214
|
+
|
215
|
+
contents = self.path.read_text()
|
216
|
+
parsed = parse(contents)
|
217
|
+
|
218
|
+
yield from (k[-1] for k in parsed if k[:-1] == keywords)
|
219
|
+
|
220
|
+
def __iter__(self) -> Iterator[str]:
|
221
|
+
return self._iter()
|
222
|
+
|
223
|
+
def __len__(self) -> int:
|
224
|
+
return len(list(iter(self)))
|
225
|
+
|
226
|
+
def __fspath__(self) -> str:
|
227
|
+
return str(self.path)
|
228
|
+
|
229
|
+
def __repr__(self) -> str:
|
230
|
+
return f"{type(self).__name__}({self.path})"
|
231
|
+
|
232
|
+
def as_dict(self) -> FoamDictionaryBase._Dict:
|
233
|
+
"""
|
234
|
+
Return a nested dict representation of the file.
|
235
|
+
"""
|
236
|
+
_, parsed = self._read()
|
237
|
+
return as_dict(parsed)
|
238
|
+
|
239
|
+
|
240
|
+
class FoamFieldFile(FoamFile):
|
241
|
+
"""An OpenFOAM dictionary file representing a field as a mutable mapping."""
|
242
|
+
|
243
|
+
class BoundariesDictionary(FoamFile.Dictionary):
|
244
|
+
def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundaryDictionary":
|
245
|
+
return cast(FoamFieldFile.BoundaryDictionary, super().__getitem__(keyword))
|
246
|
+
|
247
|
+
class BoundaryDictionary(FoamFile.Dictionary):
|
248
|
+
"""An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
|
249
|
+
|
250
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
251
|
+
if key == "value":
|
252
|
+
self._setitem(key, value, assume_field=True)
|
253
|
+
else:
|
254
|
+
self._setitem(key, value)
|
255
|
+
|
256
|
+
@property
|
257
|
+
def type(self) -> str:
|
258
|
+
"""
|
259
|
+
Alias of `self["type"]`.
|
260
|
+
"""
|
261
|
+
ret = self["type"]
|
262
|
+
if not isinstance(ret, str):
|
263
|
+
raise TypeError("type is not a string")
|
264
|
+
return ret
|
265
|
+
|
266
|
+
@type.setter
|
267
|
+
def type(self, value: str) -> None:
|
268
|
+
self["type"] = value
|
269
|
+
|
270
|
+
@property
|
271
|
+
def value(
|
272
|
+
self,
|
273
|
+
) -> Union[
|
274
|
+
int,
|
275
|
+
float,
|
276
|
+
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
277
|
+
"NDArray[np.generic]",
|
278
|
+
]:
|
279
|
+
"""
|
280
|
+
Alias of `self["value"]`.
|
281
|
+
"""
|
282
|
+
ret = self["value"]
|
283
|
+
if not isinstance(ret, (int, float, Sequence)):
|
284
|
+
raise TypeError("value is not a field")
|
285
|
+
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
286
|
+
|
287
|
+
@value.setter
|
288
|
+
def value(
|
289
|
+
self,
|
290
|
+
value: Union[
|
291
|
+
int,
|
292
|
+
float,
|
293
|
+
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
294
|
+
"NDArray[np.generic]",
|
295
|
+
],
|
296
|
+
) -> None:
|
297
|
+
self["value"] = value
|
298
|
+
|
299
|
+
@value.deleter
|
300
|
+
def value(self) -> None:
|
301
|
+
del self["value"]
|
302
|
+
|
303
|
+
def __getitem__(
|
304
|
+
self, keywords: Union[str, Tuple[str, ...]]
|
305
|
+
) -> Union[FoamFile.Value, FoamFile.Dictionary]:
|
306
|
+
if not isinstance(keywords, tuple):
|
307
|
+
keywords = (keywords,)
|
308
|
+
|
309
|
+
ret = super().__getitem__(keywords)
|
310
|
+
if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.Dictionary):
|
311
|
+
if len(keywords) == 1:
|
312
|
+
ret = FoamFieldFile.BoundariesDictionary(self, keywords)
|
313
|
+
elif len(keywords) == 2:
|
314
|
+
ret = FoamFieldFile.BoundaryDictionary(self, keywords)
|
315
|
+
return ret
|
316
|
+
|
317
|
+
def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
|
318
|
+
if not isinstance(keywords, tuple):
|
319
|
+
keywords = (keywords,)
|
320
|
+
|
321
|
+
if keywords == ("internalField",):
|
322
|
+
self._setitem(keywords, value, assume_field=True)
|
323
|
+
elif keywords == ("dimensions",):
|
324
|
+
self._setitem(keywords, value, assume_dimensions=True)
|
325
|
+
else:
|
326
|
+
self._setitem(keywords, value)
|
327
|
+
|
328
|
+
@property
|
329
|
+
def dimensions(self) -> FoamFile.DimensionSet:
|
330
|
+
"""
|
331
|
+
Alias of `self["dimensions"]`.
|
332
|
+
"""
|
333
|
+
ret = self["dimensions"]
|
334
|
+
if not isinstance(ret, FoamFile.DimensionSet):
|
335
|
+
raise TypeError("dimensions is not a DimensionSet")
|
336
|
+
return ret
|
337
|
+
|
338
|
+
@dimensions.setter
|
339
|
+
def dimensions(
|
340
|
+
self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
|
341
|
+
) -> None:
|
342
|
+
self["dimensions"] = value
|
343
|
+
|
344
|
+
@property
|
345
|
+
def internal_field(
|
346
|
+
self,
|
347
|
+
) -> Union[
|
348
|
+
int,
|
349
|
+
float,
|
350
|
+
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
351
|
+
"NDArray[np.generic]",
|
352
|
+
]:
|
353
|
+
"""
|
354
|
+
Alias of `self["internalField"]`.
|
355
|
+
"""
|
356
|
+
ret = self["internalField"]
|
357
|
+
if not isinstance(ret, (int, float, Sequence)):
|
358
|
+
raise TypeError("internalField is not a field")
|
359
|
+
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
360
|
+
|
361
|
+
@internal_field.setter
|
362
|
+
def internal_field(
|
363
|
+
self,
|
364
|
+
value: Union[
|
365
|
+
int,
|
366
|
+
float,
|
367
|
+
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
368
|
+
"NDArray[np.generic]",
|
369
|
+
],
|
370
|
+
) -> None:
|
371
|
+
self["internalField"] = value
|
372
|
+
|
373
|
+
@property
|
374
|
+
def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
|
375
|
+
"""
|
376
|
+
Alias of `self["boundaryField"]`.
|
377
|
+
"""
|
378
|
+
ret = self["boundaryField"]
|
379
|
+
if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
|
380
|
+
assert not isinstance(ret, FoamFile.Dictionary)
|
381
|
+
raise TypeError("boundaryField is not a dictionary")
|
382
|
+
return ret
|
@@ -0,0 +1,177 @@
|
|
1
|
+
from typing import Mapping, MutableMapping, Optional, Sequence, Tuple
|
2
|
+
|
3
|
+
from pyparsing import (
|
4
|
+
Dict,
|
5
|
+
Forward,
|
6
|
+
Group,
|
7
|
+
Keyword,
|
8
|
+
LineEnd,
|
9
|
+
Literal,
|
10
|
+
Located,
|
11
|
+
Opt,
|
12
|
+
ParseResults,
|
13
|
+
ParserElement,
|
14
|
+
QuotedString,
|
15
|
+
Word,
|
16
|
+
c_style_comment,
|
17
|
+
common,
|
18
|
+
cpp_style_comment,
|
19
|
+
identbodychars,
|
20
|
+
printables,
|
21
|
+
)
|
22
|
+
|
23
|
+
from ._base import FoamDictionaryBase
|
24
|
+
|
25
|
+
_YES = Keyword("yes").set_parse_action(lambda: True)
|
26
|
+
_NO = Keyword("no").set_parse_action(lambda: False)
|
27
|
+
_DIMENSIONS = (
|
28
|
+
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
29
|
+
).set_parse_action(lambda tks: FoamDictionaryBase.DimensionSet(*tks))
|
30
|
+
|
31
|
+
|
32
|
+
def _list_of(elem: ParserElement) -> ParserElement:
|
33
|
+
return Opt(
|
34
|
+
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
35
|
+
).suppress() + (
|
36
|
+
(
|
37
|
+
Opt(common.integer).suppress()
|
38
|
+
+ (
|
39
|
+
Literal("(").suppress()
|
40
|
+
+ Group((elem)[...], aslist=True)
|
41
|
+
+ Literal(")").suppress()
|
42
|
+
)
|
43
|
+
)
|
44
|
+
| (
|
45
|
+
common.integer + Literal("{").suppress() + elem + Literal("}").suppress()
|
46
|
+
).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
|
47
|
+
)
|
48
|
+
|
49
|
+
|
50
|
+
_TENSOR = _list_of(common.number) | common.number
|
51
|
+
_IDENTIFIER = Word(identbodychars + "$", printables.replace(";", ""))
|
52
|
+
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
53
|
+
lambda tks: FoamDictionaryBase.Dimensioned(*reversed(tks.as_list()))
|
54
|
+
)
|
55
|
+
_FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
|
56
|
+
Keyword("nonuniform").suppress() + _list_of(_TENSOR)
|
57
|
+
)
|
58
|
+
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
59
|
+
_ITEM = Forward()
|
60
|
+
_LIST = _list_of(_ITEM)
|
61
|
+
_ITEM <<= (
|
62
|
+
_FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _YES | _NO | _TOKEN
|
63
|
+
)
|
64
|
+
_TOKENS = (
|
65
|
+
QuotedString('"', unquote_results=False) | Word(printables.replace(";", ""))
|
66
|
+
)[2, ...].set_parse_action(lambda tks: " ".join(tks))
|
67
|
+
|
68
|
+
_VALUE = _ITEM ^ _TOKENS
|
69
|
+
|
70
|
+
_ENTRY = Forward()
|
71
|
+
_DICTIONARY = Dict(Group(_ENTRY)[...])
|
72
|
+
_ENTRY <<= Located(
|
73
|
+
_TOKEN
|
74
|
+
+ (
|
75
|
+
(Literal("{").suppress() + _DICTIONARY + Literal("}").suppress())
|
76
|
+
| (Opt(_VALUE, default="") + Literal(";").suppress())
|
77
|
+
)
|
78
|
+
)
|
79
|
+
_FILE = (
|
80
|
+
_DICTIONARY.ignore(c_style_comment)
|
81
|
+
.ignore(cpp_style_comment)
|
82
|
+
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
|
83
|
+
)
|
84
|
+
|
85
|
+
Parsed = Mapping[Sequence[str], Tuple[int, Optional[FoamDictionaryBase.Value], int]]
|
86
|
+
|
87
|
+
|
88
|
+
def _flatten_result(
|
89
|
+
parse_result: ParseResults, *, _keywords: Sequence[str] = ()
|
90
|
+
) -> Parsed:
|
91
|
+
ret: MutableMapping[
|
92
|
+
Sequence[str], Tuple[int, Optional[FoamDictionaryBase.Value], int]
|
93
|
+
] = {}
|
94
|
+
start = parse_result.locn_start
|
95
|
+
assert isinstance(start, int)
|
96
|
+
item = parse_result.value
|
97
|
+
assert isinstance(item, Sequence)
|
98
|
+
end = parse_result.locn_end
|
99
|
+
assert isinstance(end, int)
|
100
|
+
key, *values = item
|
101
|
+
assert isinstance(key, str)
|
102
|
+
ret[(*_keywords, key)] = (start, None, end)
|
103
|
+
for value in values:
|
104
|
+
if isinstance(value, ParseResults):
|
105
|
+
ret.update(_flatten_result(value, _keywords=(*_keywords, key)))
|
106
|
+
else:
|
107
|
+
ret[(*_keywords, key)] = (start, value, end)
|
108
|
+
return ret
|
109
|
+
|
110
|
+
|
111
|
+
def parse(
|
112
|
+
contents: str,
|
113
|
+
) -> Parsed:
|
114
|
+
parse_results = _FILE.parse_string(contents, parse_all=True)
|
115
|
+
ret: MutableMapping[
|
116
|
+
Sequence[str], Tuple[int, Optional[FoamDictionaryBase.Value], int]
|
117
|
+
] = {}
|
118
|
+
for parse_result in parse_results:
|
119
|
+
ret.update(_flatten_result(parse_result))
|
120
|
+
return ret
|
121
|
+
|
122
|
+
|
123
|
+
def get_value(
|
124
|
+
parsed: Parsed,
|
125
|
+
keywords: Tuple[str, ...],
|
126
|
+
) -> Optional[FoamDictionaryBase.Value]:
|
127
|
+
"""
|
128
|
+
Value of an entry.
|
129
|
+
"""
|
130
|
+
_, value, _ = parsed[keywords]
|
131
|
+
return value
|
132
|
+
|
133
|
+
|
134
|
+
def get_entry_locn(
|
135
|
+
parsed: Parsed,
|
136
|
+
keywords: Tuple[str, ...],
|
137
|
+
*,
|
138
|
+
missing_ok: bool = False,
|
139
|
+
) -> Tuple[int, int]:
|
140
|
+
"""
|
141
|
+
Location of an entry or where it should be inserted.
|
142
|
+
"""
|
143
|
+
try:
|
144
|
+
start, _, end = parsed[keywords]
|
145
|
+
except KeyError:
|
146
|
+
if missing_ok:
|
147
|
+
if len(keywords) > 1:
|
148
|
+
_, _, end = parsed[keywords[:-1]]
|
149
|
+
end -= 1
|
150
|
+
else:
|
151
|
+
end = -1
|
152
|
+
|
153
|
+
start = end
|
154
|
+
else:
|
155
|
+
raise
|
156
|
+
|
157
|
+
return start, end
|
158
|
+
|
159
|
+
|
160
|
+
def as_dict(parsed: Parsed) -> FoamDictionaryBase._Dict:
|
161
|
+
"""
|
162
|
+
Return a nested dict representation of the file.
|
163
|
+
"""
|
164
|
+
ret: FoamDictionaryBase._Dict = {}
|
165
|
+
for keywords, (_, value, _) in parsed.items():
|
166
|
+
|
167
|
+
r = ret
|
168
|
+
for k in keywords[:-1]:
|
169
|
+
assert isinstance(r, dict)
|
170
|
+
v = r[k]
|
171
|
+
assert isinstance(v, dict)
|
172
|
+
r = v
|
173
|
+
|
174
|
+
assert isinstance(r, dict)
|
175
|
+
r[keywords[-1]] = {} if value is None else value
|
176
|
+
|
177
|
+
return ret
|
@@ -0,0 +1,103 @@
|
|
1
|
+
from contextlib import suppress
|
2
|
+
from typing import Any, Sequence
|
3
|
+
|
4
|
+
from ._base import FoamDictionaryBase
|
5
|
+
|
6
|
+
try:
|
7
|
+
import numpy as np
|
8
|
+
except ModuleNotFoundError:
|
9
|
+
numpy = False
|
10
|
+
else:
|
11
|
+
numpy = True
|
12
|
+
|
13
|
+
|
14
|
+
def _is_sequence(value: Any) -> bool:
|
15
|
+
return (
|
16
|
+
isinstance(value, Sequence)
|
17
|
+
and not isinstance(value, str)
|
18
|
+
or numpy
|
19
|
+
and isinstance(value, np.ndarray)
|
20
|
+
)
|
21
|
+
|
22
|
+
|
23
|
+
def _serialize_bool(value: Any) -> str:
|
24
|
+
if value is True:
|
25
|
+
return "yes"
|
26
|
+
elif value is False:
|
27
|
+
return "no"
|
28
|
+
else:
|
29
|
+
raise TypeError(f"Not a bool: {type(value)}")
|
30
|
+
|
31
|
+
|
32
|
+
def _serialize_list(value: Any) -> str:
|
33
|
+
if _is_sequence(value):
|
34
|
+
return f"({' '.join(serialize_value(v) for v in value)})"
|
35
|
+
else:
|
36
|
+
raise TypeError(f"Not a valid sequence: {type(value)}")
|
37
|
+
|
38
|
+
|
39
|
+
def _serialize_field(value: Any) -> str:
|
40
|
+
if _is_sequence(value):
|
41
|
+
try:
|
42
|
+
s = _serialize_list(value)
|
43
|
+
except TypeError:
|
44
|
+
raise TypeError(f"Not a valid field: {type(value)}") from None
|
45
|
+
else:
|
46
|
+
if len(value) < 10:
|
47
|
+
return f"uniform {s}"
|
48
|
+
else:
|
49
|
+
if isinstance(value[0], (int, float)):
|
50
|
+
kind = "scalar"
|
51
|
+
elif len(value[0]) == 3:
|
52
|
+
kind = "vector"
|
53
|
+
elif len(value[0]) == 6:
|
54
|
+
kind = "symmTensor"
|
55
|
+
elif len(value[0]) == 9:
|
56
|
+
kind = "tensor"
|
57
|
+
else:
|
58
|
+
raise TypeError(
|
59
|
+
f"Unsupported sequence length for field: {len(value[0])}"
|
60
|
+
)
|
61
|
+
return f"nonuniform List<{kind}> {len(value)}{s}"
|
62
|
+
else:
|
63
|
+
return f"uniform {value}"
|
64
|
+
|
65
|
+
|
66
|
+
def _serialize_dimensions(value: Any) -> str:
|
67
|
+
if _is_sequence(value) and len(value) == 7:
|
68
|
+
return f"[{' '.join(str(v) for v in value)}]"
|
69
|
+
else:
|
70
|
+
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
71
|
+
|
72
|
+
|
73
|
+
def _serialize_dimensioned(value: Any) -> str:
|
74
|
+
if isinstance(value, FoamDictionaryBase.Dimensioned):
|
75
|
+
if value.name is not None:
|
76
|
+
return f"{value.name} {_serialize_dimensions(value.dimensions)} {serialize_value(value.value)}"
|
77
|
+
else:
|
78
|
+
return f"{_serialize_dimensions(value.dimensions)} {serialize_value(value.value)}"
|
79
|
+
else:
|
80
|
+
raise TypeError(f"Not a valid dimensioned value: {type(value)}")
|
81
|
+
|
82
|
+
|
83
|
+
def serialize_value(
|
84
|
+
value: Any, *, assume_field: bool = False, assume_dimensions: bool = False
|
85
|
+
) -> str:
|
86
|
+
if isinstance(value, FoamDictionaryBase.DimensionSet) or assume_dimensions:
|
87
|
+
with suppress(TypeError):
|
88
|
+
return _serialize_dimensions(value)
|
89
|
+
|
90
|
+
if assume_field:
|
91
|
+
with suppress(TypeError):
|
92
|
+
return _serialize_field(value)
|
93
|
+
|
94
|
+
with suppress(TypeError):
|
95
|
+
return _serialize_dimensioned(value)
|
96
|
+
|
97
|
+
with suppress(TypeError):
|
98
|
+
return _serialize_list(value)
|
99
|
+
|
100
|
+
with suppress(TypeError):
|
101
|
+
return _serialize_bool(value)
|
102
|
+
|
103
|
+
return str(value)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
foamlib/__init__.py,sha256=VYMos2oCtIAGQx88R_0tco49MZogwcC6yvwQ2oqmSmU,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=baObt7Ewf_sM-9Y5JGMFCYK1CVdNQg3vYbOfjCgQA64,11582
|
8
|
+
foamlib/_dictionaries/_parsing.py,sha256=65kwMU6b4WmMngOR5ED8IBvMa59FQqTRmd9o0xPnWJM,4768
|
9
|
+
foamlib/_dictionaries/_serialization.py,sha256=P_eP46c-kCXx6rIGXL7hfDuEbS9h7uANZlgXsdcNwl8,3005
|
10
|
+
foamlib-0.2.3.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
11
|
+
foamlib-0.2.3.dist-info/METADATA,sha256=DA7Kq6YIun5m2yisx_F4DSrYP13RZKgXwq0mTTX2y9A,4600
|
12
|
+
foamlib-0.2.3.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
13
|
+
foamlib-0.2.3.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
14
|
+
foamlib-0.2.3.dist-info/RECORD,,
|
foamlib/_dictionaries.py
DELETED
@@ -1,491 +0,0 @@
|
|
1
|
-
from pathlib import Path
|
2
|
-
from dataclasses import dataclass
|
3
|
-
from contextlib import suppress
|
4
|
-
from typing import (
|
5
|
-
Any,
|
6
|
-
Union,
|
7
|
-
Sequence,
|
8
|
-
Iterator,
|
9
|
-
Optional,
|
10
|
-
Mapping,
|
11
|
-
MutableMapping,
|
12
|
-
NamedTuple,
|
13
|
-
cast,
|
14
|
-
)
|
15
|
-
|
16
|
-
from pyparsing import (
|
17
|
-
Dict,
|
18
|
-
Forward,
|
19
|
-
Group,
|
20
|
-
Keyword,
|
21
|
-
LineEnd,
|
22
|
-
Literal,
|
23
|
-
Opt,
|
24
|
-
ParserElement,
|
25
|
-
QuotedString,
|
26
|
-
Word,
|
27
|
-
c_style_comment,
|
28
|
-
common,
|
29
|
-
cpp_style_comment,
|
30
|
-
identbodychars,
|
31
|
-
printables,
|
32
|
-
)
|
33
|
-
|
34
|
-
try:
|
35
|
-
import numpy as np
|
36
|
-
from numpy.typing import NDArray
|
37
|
-
except ModuleNotFoundError:
|
38
|
-
numpy = False
|
39
|
-
else:
|
40
|
-
numpy = True
|
41
|
-
|
42
|
-
from ._subprocesses import run_process, CalledProcessError
|
43
|
-
|
44
|
-
|
45
|
-
class _FoamDictionary(MutableMapping[str, Union["FoamFile.Value", "_FoamDictionary"]]):
|
46
|
-
|
47
|
-
def __init__(self, _file: "FoamFile", _keywords: Sequence[str]) -> None:
|
48
|
-
self._file = _file
|
49
|
-
self._keywords = _keywords
|
50
|
-
|
51
|
-
def _cmd(self, args: Sequence[str], *, key: Optional[str] = None) -> str:
|
52
|
-
keywords = self._keywords
|
53
|
-
|
54
|
-
if key is not None:
|
55
|
-
keywords = [*self._keywords, key]
|
56
|
-
|
57
|
-
if keywords:
|
58
|
-
args = ["-entry", "/".join(keywords), *args]
|
59
|
-
|
60
|
-
try:
|
61
|
-
return (
|
62
|
-
run_process(
|
63
|
-
["foamDictionary", *args, "-precision", "15", self._file.path],
|
64
|
-
)
|
65
|
-
.stdout.decode()
|
66
|
-
.strip()
|
67
|
-
)
|
68
|
-
except CalledProcessError as e:
|
69
|
-
stderr = e.stderr.decode()
|
70
|
-
if "Cannot find entry" in stderr:
|
71
|
-
raise KeyError(key) from None
|
72
|
-
else:
|
73
|
-
raise RuntimeError(
|
74
|
-
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr.decode()}"
|
75
|
-
) from None
|
76
|
-
|
77
|
-
def __getitem__(self, key: str) -> Union["FoamFile.Value", "_FoamDictionary"]:
|
78
|
-
value = _DICTIONARY.parse_file(self._file.path, parse_all=True).as_dict()
|
79
|
-
|
80
|
-
for key in [*self._keywords, key]:
|
81
|
-
value = value[key]
|
82
|
-
|
83
|
-
if isinstance(value, dict):
|
84
|
-
return _FoamDictionary(self._file, [*self._keywords, key])
|
85
|
-
else:
|
86
|
-
return value
|
87
|
-
|
88
|
-
def _setitem(
|
89
|
-
self,
|
90
|
-
key: str,
|
91
|
-
value: Any,
|
92
|
-
*,
|
93
|
-
assume_field: bool = False,
|
94
|
-
assume_dimensions: bool = False,
|
95
|
-
) -> None:
|
96
|
-
if isinstance(value, _FoamDictionary):
|
97
|
-
value = value._cmd(["-value"])
|
98
|
-
elif isinstance(value, Mapping):
|
99
|
-
self._cmd(["-set", "{}"], key=key)
|
100
|
-
subdict = self[key]
|
101
|
-
assert isinstance(subdict, _FoamDictionary)
|
102
|
-
for k, v in value.items():
|
103
|
-
subdict[k] = v
|
104
|
-
return
|
105
|
-
else:
|
106
|
-
value = serialize(
|
107
|
-
value, assume_field=assume_field, assume_dimensions=assume_dimensions
|
108
|
-
)
|
109
|
-
|
110
|
-
if len(value) < 1000:
|
111
|
-
self._cmd(["-set", value], key=key)
|
112
|
-
else:
|
113
|
-
self._cmd(["-set", "_foamlib_value_"], key=key)
|
114
|
-
contents = self._file.path.read_text()
|
115
|
-
contents = contents.replace("_foamlib_value_", value, 1)
|
116
|
-
self._file.path.write_text(contents)
|
117
|
-
|
118
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
119
|
-
self._setitem(key, value)
|
120
|
-
|
121
|
-
def __delitem__(self, key: str) -> None:
|
122
|
-
if key not in self:
|
123
|
-
raise KeyError(key)
|
124
|
-
self._cmd(["-remove"], key=key)
|
125
|
-
|
126
|
-
def __iter__(self) -> Iterator[str]:
|
127
|
-
value = _DICTIONARY.parse_file(self._file.path, parse_all=True).as_dict()
|
128
|
-
|
129
|
-
for key in self._keywords:
|
130
|
-
value = value[key]
|
131
|
-
|
132
|
-
yield from value
|
133
|
-
|
134
|
-
def __len__(self) -> int:
|
135
|
-
return len(list(iter(self)))
|
136
|
-
|
137
|
-
def __repr__(self) -> str:
|
138
|
-
return f"FoamFile.Dictionary({self._file}, {self._keywords})"
|
139
|
-
|
140
|
-
|
141
|
-
class FoamFile(_FoamDictionary):
|
142
|
-
"""An OpenFOAM dictionary file as a mutable mapping."""
|
143
|
-
|
144
|
-
Dictionary = _FoamDictionary
|
145
|
-
|
146
|
-
class DimensionSet(NamedTuple):
|
147
|
-
mass: Union[int, float] = 0
|
148
|
-
length: Union[int, float] = 0
|
149
|
-
time: Union[int, float] = 0
|
150
|
-
temperature: Union[int, float] = 0
|
151
|
-
moles: Union[int, float] = 0
|
152
|
-
current: Union[int, float] = 0
|
153
|
-
luminous_intensity: Union[int, float] = 0
|
154
|
-
|
155
|
-
def __repr__(self) -> str:
|
156
|
-
return f"{type(self).__qualname__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
|
157
|
-
|
158
|
-
@dataclass
|
159
|
-
class Dimensioned:
|
160
|
-
value: Union[int, float, Sequence[Union[int, float]]] = 0
|
161
|
-
dimensions: Union["FoamFile.DimensionSet", Sequence[Union[int, float]]] = ()
|
162
|
-
name: Optional[str] = None
|
163
|
-
|
164
|
-
def __post_init__(self) -> None:
|
165
|
-
if not isinstance(self.dimensions, FoamFile.DimensionSet):
|
166
|
-
self.dimensions = FoamFile.DimensionSet(*self.dimensions)
|
167
|
-
|
168
|
-
Value = Union[str, int, float, bool, Dimensioned, DimensionSet, Sequence["Value"]]
|
169
|
-
"""
|
170
|
-
A value that can be stored in an OpenFOAM dictionary.
|
171
|
-
"""
|
172
|
-
|
173
|
-
def __init__(self, path: Union[str, Path]) -> None:
|
174
|
-
super().__init__(self, [])
|
175
|
-
self.path = Path(path).absolute()
|
176
|
-
if self.path.is_dir():
|
177
|
-
raise IsADirectoryError(self.path)
|
178
|
-
elif not self.path.is_file():
|
179
|
-
raise FileNotFoundError(self.path)
|
180
|
-
|
181
|
-
def __fspath__(self) -> str:
|
182
|
-
return str(self.path)
|
183
|
-
|
184
|
-
def __repr__(self) -> str:
|
185
|
-
return f"{type(self).__name__}({self.path})"
|
186
|
-
|
187
|
-
|
188
|
-
class FoamFieldFile(FoamFile):
|
189
|
-
"""An OpenFOAM dictionary file representing a field as a mutable mapping."""
|
190
|
-
|
191
|
-
class BoundariesDictionary(_FoamDictionary):
|
192
|
-
def __getitem__(
|
193
|
-
self, key: str
|
194
|
-
) -> Union["FoamFile.Value", "FoamFieldFile.BoundaryDictionary"]:
|
195
|
-
ret = super().__getitem__(key)
|
196
|
-
if isinstance(ret, _FoamDictionary):
|
197
|
-
ret = FoamFieldFile.BoundaryDictionary(
|
198
|
-
self._file, [*self._keywords, key]
|
199
|
-
)
|
200
|
-
return ret
|
201
|
-
|
202
|
-
def __repr__(self) -> str:
|
203
|
-
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
204
|
-
|
205
|
-
class BoundaryDictionary(_FoamDictionary):
|
206
|
-
"""An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
|
207
|
-
|
208
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
209
|
-
if key == "value":
|
210
|
-
self._setitem(key, value, assume_field=True)
|
211
|
-
else:
|
212
|
-
self._setitem(key, value)
|
213
|
-
|
214
|
-
@property
|
215
|
-
def type(self) -> str:
|
216
|
-
"""
|
217
|
-
Alias of `self["type"]`.
|
218
|
-
"""
|
219
|
-
ret = self["type"]
|
220
|
-
if not isinstance(ret, str):
|
221
|
-
raise TypeError("type is not a string")
|
222
|
-
return ret
|
223
|
-
|
224
|
-
@type.setter
|
225
|
-
def type(self, value: str) -> None:
|
226
|
-
self["type"] = value
|
227
|
-
|
228
|
-
@property
|
229
|
-
def value(
|
230
|
-
self,
|
231
|
-
) -> Union[
|
232
|
-
int,
|
233
|
-
float,
|
234
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
235
|
-
"NDArray[np.generic]",
|
236
|
-
]:
|
237
|
-
"""
|
238
|
-
Alias of `self["value"]`.
|
239
|
-
"""
|
240
|
-
ret = self["value"]
|
241
|
-
if not isinstance(ret, (int, float, Sequence)):
|
242
|
-
raise TypeError("value is not a field")
|
243
|
-
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
244
|
-
|
245
|
-
@value.setter
|
246
|
-
def value(
|
247
|
-
self,
|
248
|
-
value: Union[
|
249
|
-
int,
|
250
|
-
float,
|
251
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
252
|
-
"NDArray[np.generic]",
|
253
|
-
],
|
254
|
-
) -> None:
|
255
|
-
self["value"] = value
|
256
|
-
|
257
|
-
@value.deleter
|
258
|
-
def value(self) -> None:
|
259
|
-
del self["value"]
|
260
|
-
|
261
|
-
def __repr__(self) -> str:
|
262
|
-
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
263
|
-
|
264
|
-
def __getitem__(self, key: str) -> Union[FoamFile.Value, _FoamDictionary]:
|
265
|
-
ret = super().__getitem__(key)
|
266
|
-
if key == "boundaryField" and isinstance(ret, _FoamDictionary):
|
267
|
-
ret = FoamFieldFile.BoundariesDictionary(self, [key])
|
268
|
-
return ret
|
269
|
-
|
270
|
-
def __setitem__(self, key: str, value: Any) -> None:
|
271
|
-
if key == "internalField":
|
272
|
-
self._setitem(key, value, assume_field=True)
|
273
|
-
elif key == "dimensions":
|
274
|
-
self._setitem(key, value, assume_dimensions=True)
|
275
|
-
else:
|
276
|
-
self._setitem(key, value)
|
277
|
-
|
278
|
-
@property
|
279
|
-
def dimensions(self) -> FoamFile.DimensionSet:
|
280
|
-
"""
|
281
|
-
Alias of `self["dimensions"]`.
|
282
|
-
"""
|
283
|
-
ret = self["dimensions"]
|
284
|
-
if not isinstance(ret, FoamFile.DimensionSet):
|
285
|
-
raise TypeError("dimensions is not a DimensionSet")
|
286
|
-
return ret
|
287
|
-
|
288
|
-
@dimensions.setter
|
289
|
-
def dimensions(
|
290
|
-
self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
|
291
|
-
) -> None:
|
292
|
-
self["dimensions"] = value
|
293
|
-
|
294
|
-
@property
|
295
|
-
def internal_field(
|
296
|
-
self,
|
297
|
-
) -> Union[
|
298
|
-
int,
|
299
|
-
float,
|
300
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
301
|
-
"NDArray[np.generic]",
|
302
|
-
]:
|
303
|
-
"""
|
304
|
-
Alias of `self["internalField"]`.
|
305
|
-
"""
|
306
|
-
ret = self["internalField"]
|
307
|
-
if not isinstance(ret, (int, float, Sequence)):
|
308
|
-
raise TypeError("internalField is not a field")
|
309
|
-
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
310
|
-
|
311
|
-
@internal_field.setter
|
312
|
-
def internal_field(
|
313
|
-
self,
|
314
|
-
value: Union[
|
315
|
-
int,
|
316
|
-
float,
|
317
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
318
|
-
"NDArray[np.generic]",
|
319
|
-
],
|
320
|
-
) -> None:
|
321
|
-
self["internalField"] = value
|
322
|
-
|
323
|
-
@property
|
324
|
-
def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
|
325
|
-
"""
|
326
|
-
Alias of `self["boundaryField"]`.
|
327
|
-
"""
|
328
|
-
ret = self["boundaryField"]
|
329
|
-
if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
|
330
|
-
assert not isinstance(ret, _FoamDictionary)
|
331
|
-
raise TypeError("boundaryField is not a dictionary")
|
332
|
-
return ret
|
333
|
-
|
334
|
-
|
335
|
-
_YES = Keyword("yes").set_parse_action(lambda: True)
|
336
|
-
_NO = Keyword("no").set_parse_action(lambda: False)
|
337
|
-
_DIMENSIONS = (
|
338
|
-
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
339
|
-
).set_parse_action(lambda tks: FoamFile.DimensionSet(*tks))
|
340
|
-
|
341
|
-
|
342
|
-
def _list_of(elem: ParserElement) -> ParserElement:
|
343
|
-
return Opt(
|
344
|
-
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
345
|
-
).suppress() + (
|
346
|
-
(
|
347
|
-
Opt(common.integer).suppress()
|
348
|
-
+ (
|
349
|
-
Literal("(").suppress()
|
350
|
-
+ Group((elem)[...]).set_parse_action(lambda tks: tks.as_list())
|
351
|
-
+ Literal(")").suppress()
|
352
|
-
)
|
353
|
-
)
|
354
|
-
| (
|
355
|
-
common.integer + Literal("{").suppress() + elem + Literal("}").suppress()
|
356
|
-
).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
|
357
|
-
)
|
358
|
-
|
359
|
-
|
360
|
-
_TENSOR = _list_of(common.number) | common.number
|
361
|
-
_IDENTIFIER = Word(identbodychars + "$", identbodychars + "({,./:^!)}")
|
362
|
-
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
363
|
-
lambda tks: FoamFile.Dimensioned(*reversed(tks.as_list()))
|
364
|
-
)
|
365
|
-
_FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
|
366
|
-
Keyword("nonuniform").suppress() + _list_of(_TENSOR)
|
367
|
-
)
|
368
|
-
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
369
|
-
_DICTIONARY = Forward()
|
370
|
-
_SUBDICT = Literal("{").suppress() + _DICTIONARY + Literal("}").suppress()
|
371
|
-
_ITEM = Forward()
|
372
|
-
_LIST = _list_of(_ITEM)
|
373
|
-
_ITEM <<= (
|
374
|
-
_FIELD
|
375
|
-
| _LIST
|
376
|
-
| _SUBDICT
|
377
|
-
| _DIMENSIONED
|
378
|
-
| _DIMENSIONS
|
379
|
-
| common.number
|
380
|
-
| _YES
|
381
|
-
| _NO
|
382
|
-
| _TOKEN
|
383
|
-
)
|
384
|
-
_TOKENS = (
|
385
|
-
QuotedString('"', unquote_results=False) | Word(printables.replace(";", ""))
|
386
|
-
)[2, ...].set_parse_action(lambda tks: " ".join(tks))
|
387
|
-
|
388
|
-
_VALUE = _ITEM ^ _TOKENS
|
389
|
-
|
390
|
-
_ENTRY = _IDENTIFIER + (
|
391
|
-
(Opt(_VALUE, default=None) + Literal(";").suppress()) | _SUBDICT
|
392
|
-
)
|
393
|
-
_DICTIONARY <<= (
|
394
|
-
Dict(Group(_ENTRY)[...])
|
395
|
-
.set_parse_action(lambda tks: {} if not tks else tks)
|
396
|
-
.ignore(c_style_comment)
|
397
|
-
.ignore(cpp_style_comment)
|
398
|
-
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
|
399
|
-
)
|
400
|
-
|
401
|
-
|
402
|
-
def _serialize_bool(value: Any) -> str:
|
403
|
-
if value is True:
|
404
|
-
return "yes"
|
405
|
-
elif value is False:
|
406
|
-
return "no"
|
407
|
-
else:
|
408
|
-
raise TypeError(f"Not a bool: {type(value)}")
|
409
|
-
|
410
|
-
|
411
|
-
def _is_sequence(value: Any) -> bool:
|
412
|
-
return (
|
413
|
-
isinstance(value, Sequence)
|
414
|
-
and not isinstance(value, str)
|
415
|
-
or numpy
|
416
|
-
and isinstance(value, np.ndarray)
|
417
|
-
)
|
418
|
-
|
419
|
-
|
420
|
-
def _serialize_list(value: Any) -> str:
|
421
|
-
if _is_sequence(value):
|
422
|
-
return f"({' '.join(serialize(v) for v in value)})"
|
423
|
-
else:
|
424
|
-
raise TypeError(f"Not a valid sequence: {type(value)}")
|
425
|
-
|
426
|
-
|
427
|
-
def _serialize_field(value: Any) -> str:
|
428
|
-
if _is_sequence(value):
|
429
|
-
try:
|
430
|
-
s = _serialize_list(value)
|
431
|
-
except TypeError:
|
432
|
-
raise TypeError(f"Not a valid field: {type(value)}") from None
|
433
|
-
else:
|
434
|
-
if len(value) < 10:
|
435
|
-
return f"uniform {s}"
|
436
|
-
else:
|
437
|
-
if isinstance(value[0], (int, float)):
|
438
|
-
kind = "scalar"
|
439
|
-
elif len(value[0]) == 3:
|
440
|
-
kind = "vector"
|
441
|
-
elif len(value[0]) == 6:
|
442
|
-
kind = "symmTensor"
|
443
|
-
elif len(value[0]) == 9:
|
444
|
-
kind = "tensor"
|
445
|
-
else:
|
446
|
-
raise TypeError(
|
447
|
-
f"Unsupported sequence length for field: {len(value[0])}"
|
448
|
-
)
|
449
|
-
return f"nonuniform List<{kind}> {len(value)}{s}"
|
450
|
-
else:
|
451
|
-
return f"uniform {value}"
|
452
|
-
|
453
|
-
|
454
|
-
def _serialize_dimensions(value: Any) -> str:
|
455
|
-
if _is_sequence(value) and len(value) == 7:
|
456
|
-
return f"[{' '.join(str(v) for v in value)}]"
|
457
|
-
else:
|
458
|
-
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
459
|
-
|
460
|
-
|
461
|
-
def _serialize_dimensioned(value: Any) -> str:
|
462
|
-
if isinstance(value, FoamFile.Dimensioned):
|
463
|
-
if value.name is not None:
|
464
|
-
return f"{value.name} {_serialize_dimensions(value.dimensions)} {serialize(value.value)}"
|
465
|
-
else:
|
466
|
-
return f"{_serialize_dimensions(value.dimensions)} {serialize(value.value)}"
|
467
|
-
else:
|
468
|
-
raise TypeError(f"Not a valid dimensioned value: {type(value)}")
|
469
|
-
|
470
|
-
|
471
|
-
def serialize(
|
472
|
-
value: Any, *, assume_field: bool = False, assume_dimensions: bool = False
|
473
|
-
) -> str:
|
474
|
-
if isinstance(value, FoamFile.DimensionSet) or assume_dimensions:
|
475
|
-
with suppress(TypeError):
|
476
|
-
return _serialize_dimensions(value)
|
477
|
-
|
478
|
-
if assume_field:
|
479
|
-
with suppress(TypeError):
|
480
|
-
return _serialize_field(value)
|
481
|
-
|
482
|
-
with suppress(TypeError):
|
483
|
-
return _serialize_dimensioned(value)
|
484
|
-
|
485
|
-
with suppress(TypeError):
|
486
|
-
return _serialize_list(value)
|
487
|
-
|
488
|
-
with suppress(TypeError):
|
489
|
-
return _serialize_bool(value)
|
490
|
-
|
491
|
-
return str(value)
|
foamlib-0.2.2.dist-info/RECORD
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=EFlApZJu-niDqPz_q3gjFdJ4ScRjPAjKC1gfKtXLMDQ,241
|
2
|
-
foamlib/_cases.py,sha256=4f3c5BXnsHPhFvgXNjUcGGHyu7I0WZT6zxlvGhb9kMY,21213
|
3
|
-
foamlib/_dictionaries.py,sha256=5hElrfAk_2SjINqmFE1dYK4H7lg8rRTzO-vwzMWj5QM,14874
|
4
|
-
foamlib/_subprocesses.py,sha256=5vqdQvpN_2v4GgDqxi-s88NGhZ6doFxkh0XY89ZWuHA,1926
|
5
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
foamlib-0.2.2.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
7
|
-
foamlib-0.2.2.dist-info/METADATA,sha256=3J72tulIUFzK32O6aGy_LDzNI1LXWzcLM-9yVRVeJVY,4600
|
8
|
-
foamlib-0.2.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
9
|
-
foamlib-0.2.2.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
10
|
-
foamlib-0.2.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|