foamlib 0.3.23__py3-none-any.whl → 0.4.1__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 -3
- foamlib/_cases/_base.py +12 -4
- foamlib/_files/__init__.py +2 -2
- foamlib/_files/_base.py +5 -11
- foamlib/_files/_files.py +192 -136
- foamlib/_files/_io.py +1 -1
- foamlib/_files/_parsing.py +33 -19
- foamlib/_files/_serialization.py +7 -9
- {foamlib-0.3.23.dist-info → foamlib-0.4.1.dist-info}/METADATA +2 -2
- foamlib-0.4.1.dist-info/RECORD +19 -0
- {foamlib-0.3.23.dist-info → foamlib-0.4.1.dist-info}/WHEEL +1 -1
- foamlib-0.3.23.dist-info/RECORD +0 -19
- {foamlib-0.3.23.dist-info → foamlib-0.4.1.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.3.23.dist-info → foamlib-0.4.1.dist-info}/top_level.txt +0 -0
foamlib/__init__.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""A Python interface for interacting with OpenFOAM."""
|
2
2
|
|
3
|
-
__version__ = "0.
|
3
|
+
__version__ = "0.4.1"
|
4
4
|
|
5
5
|
from ._cases import (
|
6
6
|
AsyncFoamCase,
|
@@ -9,7 +9,7 @@ from ._cases import (
|
|
9
9
|
FoamCase,
|
10
10
|
FoamCaseBase,
|
11
11
|
)
|
12
|
-
from ._files import
|
12
|
+
from ._files import FoamFieldFile, FoamFile, FoamFileBase
|
13
13
|
|
14
14
|
__all__ = [
|
15
15
|
"FoamCase",
|
@@ -17,7 +17,7 @@ __all__ = [
|
|
17
17
|
"FoamCaseBase",
|
18
18
|
"FoamFile",
|
19
19
|
"FoamFieldFile",
|
20
|
-
"
|
20
|
+
"FoamFileBase",
|
21
21
|
"CalledProcessError",
|
22
22
|
"CalledProcessWarning",
|
23
23
|
]
|
foamlib/_cases/_base.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import os
|
2
|
+
import shutil
|
2
3
|
import sys
|
3
4
|
from pathlib import Path
|
4
5
|
from typing import (
|
@@ -62,12 +63,10 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
62
63
|
return self.path.name
|
63
64
|
|
64
65
|
def __getitem__(self, key: str) -> FoamFieldFile:
|
65
|
-
if (self.path / key).is_file():
|
66
|
-
return FoamFieldFile(self.path / key)
|
67
|
-
elif (self.path / f"{key}.gz").is_file():
|
66
|
+
if (self.path / f"{key}.gz").is_file() and not (self.path / key).is_file():
|
68
67
|
return FoamFieldFile(self.path / f"{key}.gz")
|
69
68
|
else:
|
70
|
-
|
69
|
+
return FoamFieldFile(self.path / key)
|
71
70
|
|
72
71
|
def __contains__(self, obj: object) -> bool:
|
73
72
|
if isinstance(obj, FoamFieldFile):
|
@@ -89,6 +88,12 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
89
88
|
def __len__(self) -> int:
|
90
89
|
return len(list(iter(self)))
|
91
90
|
|
91
|
+
def __delitem__(self, key: str) -> None:
|
92
|
+
if (self.path / f"{key}.gz").is_file() and not (self.path / key).is_file():
|
93
|
+
(self.path / f"{key}.gz").unlink()
|
94
|
+
else:
|
95
|
+
(self.path / key).unlink()
|
96
|
+
|
92
97
|
def __fspath__(self) -> str:
|
93
98
|
return str(self.path)
|
94
99
|
|
@@ -167,6 +172,9 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
167
172
|
|
168
173
|
return paths
|
169
174
|
|
175
|
+
def __delitem__(self, key: Union[int, float, str]) -> None:
|
176
|
+
shutil.rmtree(self[key].path)
|
177
|
+
|
170
178
|
def _clone_ignore(
|
171
179
|
self,
|
172
180
|
) -> Callable[[Union[Path, str], Collection[str]], Collection[str]]:
|
foamlib/_files/__init__.py
CHANGED
foamlib/_files/_base.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import sys
|
2
|
-
from abc import abstractmethod
|
3
2
|
from dataclasses import dataclass
|
4
3
|
from typing import Dict, NamedTuple, Optional, Tuple, Union
|
5
4
|
|
@@ -14,7 +13,7 @@ except ModuleNotFoundError:
|
|
14
13
|
pass
|
15
14
|
|
16
15
|
|
17
|
-
class
|
16
|
+
class FoamFileBase:
|
18
17
|
class DimensionSet(NamedTuple):
|
19
18
|
mass: Union[int, float] = 0
|
20
19
|
length: Union[int, float] = 0
|
@@ -30,12 +29,12 @@ class FoamDict:
|
|
30
29
|
@dataclass
|
31
30
|
class Dimensioned:
|
32
31
|
value: Union[int, float, Sequence[Union[int, float]]] = 0
|
33
|
-
dimensions: Union["
|
32
|
+
dimensions: Union["FoamFileBase.DimensionSet", Sequence[Union[int, float]]] = ()
|
34
33
|
name: Optional[str] = None
|
35
34
|
|
36
35
|
def __post_init__(self) -> None:
|
37
|
-
if not isinstance(self.dimensions,
|
38
|
-
self.dimensions =
|
36
|
+
if not isinstance(self.dimensions, FoamFileBase.DimensionSet):
|
37
|
+
self.dimensions = FoamFileBase.DimensionSet(*self.dimensions)
|
39
38
|
|
40
39
|
Data = Union[
|
41
40
|
str,
|
@@ -48,16 +47,11 @@ class FoamDict:
|
|
48
47
|
Mapping[str, "Data"],
|
49
48
|
]
|
50
49
|
"""
|
51
|
-
A value that can be stored in an OpenFOAM
|
50
|
+
A value that can be stored in an OpenFOAM file.
|
52
51
|
"""
|
53
52
|
|
54
53
|
_Dict = Dict[str, Union["Data", "_Dict"]]
|
55
54
|
|
56
|
-
@abstractmethod
|
57
|
-
def as_dict(self) -> _Dict:
|
58
|
-
"""Return a nested dict representation of the dictionary."""
|
59
|
-
raise NotImplementedError
|
60
|
-
|
61
55
|
_SetData = Union[
|
62
56
|
str,
|
63
57
|
int,
|
foamlib/_files/_files.py
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
import sys
|
2
|
-
from typing import Any, Tuple, Union, cast
|
2
|
+
from typing import Any, Optional, Tuple, Union, cast
|
3
|
+
|
4
|
+
if sys.version_info >= (3, 8):
|
5
|
+
from typing import Literal
|
6
|
+
else:
|
7
|
+
from typing_extensions import Literal
|
3
8
|
|
4
9
|
if sys.version_info >= (3, 9):
|
5
10
|
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
6
11
|
else:
|
7
12
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
8
13
|
|
14
|
+
if sys.version_info >= (3, 11):
|
15
|
+
from typing import Self
|
16
|
+
else:
|
17
|
+
from typing_extensions import Self
|
18
|
+
|
9
19
|
from .._util import is_sequence
|
10
|
-
from ._base import
|
11
|
-
from ._io import
|
20
|
+
from ._base import FoamFileBase
|
21
|
+
from ._io import _FoamFileIO
|
12
22
|
from ._serialization import Kind, dumpb
|
13
23
|
|
14
24
|
try:
|
@@ -18,11 +28,12 @@ except ModuleNotFoundError:
|
|
18
28
|
|
19
29
|
|
20
30
|
class FoamFile(
|
21
|
-
|
31
|
+
FoamFileBase,
|
22
32
|
MutableMapping[
|
23
|
-
Union[str, Tuple[str, ...]],
|
33
|
+
Optional[Union[str, Tuple[str, ...]]],
|
34
|
+
Union["FoamFile.Data", "FoamFile.SubDict"],
|
24
35
|
],
|
25
|
-
|
36
|
+
_FoamFileIO,
|
26
37
|
):
|
27
38
|
"""
|
28
39
|
An OpenFOAM data file.
|
@@ -33,7 +44,6 @@ class FoamFile(
|
|
33
44
|
"""
|
34
45
|
|
35
46
|
class SubDict(
|
36
|
-
FoamDict,
|
37
47
|
MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
|
38
48
|
):
|
39
49
|
"""An OpenFOAM dictionary within a file as a mutable mapping."""
|
@@ -58,7 +68,9 @@ class FoamFile(
|
|
58
68
|
del self._file[(*self._keywords, keyword)]
|
59
69
|
|
60
70
|
def __iter__(self) -> Iterator[str]:
|
61
|
-
|
71
|
+
for k in self._file._iter(self._keywords):
|
72
|
+
assert k is not None
|
73
|
+
yield k
|
62
74
|
|
63
75
|
def __contains__(self, keyword: object) -> bool:
|
64
76
|
return (*self._keywords, keyword) in self._file
|
@@ -77,7 +89,7 @@ class FoamFile(
|
|
77
89
|
def __repr__(self) -> str:
|
78
90
|
return f"{type(self).__qualname__}('{self._file}', {self._keywords})"
|
79
91
|
|
80
|
-
def as_dict(self) ->
|
92
|
+
def as_dict(self) -> FoamFileBase._Dict:
|
81
93
|
"""Return a nested dict representation of the dictionary."""
|
82
94
|
ret = self._file.as_dict()
|
83
95
|
|
@@ -89,85 +101,111 @@ class FoamFile(
|
|
89
101
|
|
90
102
|
return ret
|
91
103
|
|
92
|
-
|
93
|
-
"""
|
104
|
+
def create(self, *, exist_ok: bool = False, parents: bool = False) -> Self:
|
105
|
+
"""
|
106
|
+
Create the file.
|
107
|
+
|
108
|
+
Parameters
|
109
|
+
----------
|
110
|
+
exist_ok : bool, optional
|
111
|
+
If False (the default), raise a FileExistsError if the file already exists.
|
112
|
+
If True, do nothing if the file already exists.
|
113
|
+
parents : bool, optional
|
114
|
+
If True, also create parent directories as needed.
|
115
|
+
"""
|
116
|
+
if self.path.exists():
|
117
|
+
if not exist_ok:
|
118
|
+
raise FileExistsError(self.path)
|
119
|
+
else:
|
120
|
+
return self
|
94
121
|
|
95
|
-
|
96
|
-
|
122
|
+
if parents:
|
123
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
97
124
|
|
98
|
-
|
99
|
-
|
100
|
-
"""Alias of `self["version"]`."""
|
101
|
-
ret = self["version"]
|
102
|
-
if not isinstance(ret, float):
|
103
|
-
raise TypeError("version is not a float")
|
104
|
-
return ret
|
125
|
+
self.path.touch()
|
126
|
+
self._write_header()
|
105
127
|
|
106
|
-
|
107
|
-
def version(self, data: float) -> None:
|
108
|
-
self["version"] = data
|
128
|
+
return self
|
109
129
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
130
|
+
@property
|
131
|
+
def version(self) -> float:
|
132
|
+
"""Alias of `self["FoamFile", "version"]`."""
|
133
|
+
ret = self["FoamFile", "version"]
|
134
|
+
if not isinstance(ret, float):
|
135
|
+
raise TypeError("version is not a float")
|
136
|
+
return ret
|
117
137
|
|
118
|
-
|
119
|
-
|
120
|
-
|
138
|
+
@version.setter
|
139
|
+
def version(self, value: float) -> None:
|
140
|
+
self["FoamFile", "version"] = value
|
121
141
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
142
|
+
@property
|
143
|
+
def format(self) -> Literal["ascii", "binary"]:
|
144
|
+
"""Alias of `self["FoamFile", "format"]`."""
|
145
|
+
ret = self["FoamFile", "format"]
|
146
|
+
if not isinstance(ret, str):
|
147
|
+
raise TypeError("format is not a string")
|
148
|
+
if ret not in ("ascii", "binary"):
|
149
|
+
raise ValueError("format is not 'ascii' or 'binary'")
|
150
|
+
return cast(Literal["ascii", "binary"], ret)
|
151
|
+
|
152
|
+
@format.setter
|
153
|
+
def format(self, value: Literal["ascii", "binary"]) -> None:
|
154
|
+
self["FoamFile", "format"] = value
|
129
155
|
|
130
|
-
|
131
|
-
|
132
|
-
|
156
|
+
@property
|
157
|
+
def class_(self) -> str:
|
158
|
+
"""Alias of `self["FoamFile", "class"]`."""
|
159
|
+
ret = self["FoamFile", "class"]
|
160
|
+
if not isinstance(ret, str):
|
161
|
+
raise TypeError("class is not a string")
|
162
|
+
return ret
|
133
163
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
ret = self["location"]
|
138
|
-
if not isinstance(ret, str):
|
139
|
-
raise TypeError("location is not a string")
|
140
|
-
return ret
|
164
|
+
@class_.setter
|
165
|
+
def class_(self, value: str) -> None:
|
166
|
+
self["FoamFile", "class"] = value
|
141
167
|
|
142
|
-
|
143
|
-
|
144
|
-
|
168
|
+
@property
|
169
|
+
def location(self) -> str:
|
170
|
+
"""Alias of `self["FoamFile", "location"]`."""
|
171
|
+
ret = self["FoamFile", "location"]
|
172
|
+
if not isinstance(ret, str):
|
173
|
+
raise TypeError("location is not a string")
|
174
|
+
return ret
|
145
175
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
ret = self["object"]
|
150
|
-
if not isinstance(ret, str):
|
151
|
-
raise TypeError("object is not a string")
|
152
|
-
return ret
|
176
|
+
@location.setter
|
177
|
+
def location(self, value: str) -> None:
|
178
|
+
self["FoamFile", "location"] = value
|
153
179
|
|
154
180
|
@property
|
155
|
-
def
|
156
|
-
"""Alias of `self["FoamFile"]`."""
|
157
|
-
ret = self["FoamFile"]
|
158
|
-
if not isinstance(ret,
|
159
|
-
|
160
|
-
raise TypeError("FoamFile is not a dictionary")
|
181
|
+
def object_(self) -> str:
|
182
|
+
"""Alias of `self["FoamFile", "object"]`."""
|
183
|
+
ret = self["FoamFile", "object"]
|
184
|
+
if not isinstance(ret, str):
|
185
|
+
raise TypeError("object is not a string")
|
161
186
|
return ret
|
162
187
|
|
163
|
-
@
|
164
|
-
def
|
165
|
-
self["FoamFile"] =
|
188
|
+
@object_.setter
|
189
|
+
def object_(self, value: str) -> None:
|
190
|
+
self["FoamFile", "object"] = value
|
191
|
+
|
192
|
+
def _write_header(self) -> None:
|
193
|
+
assert "FoamFile" not in self
|
194
|
+
assert not self
|
195
|
+
|
196
|
+
self["FoamFile"] = {}
|
197
|
+
self.version = 2.0
|
198
|
+
self.format = "ascii"
|
199
|
+
self.class_ = "dictionary"
|
200
|
+
self.location = f'"{self.path.parent.name}"'
|
201
|
+
self.object_ = self.path.name
|
166
202
|
|
167
203
|
def __getitem__(
|
168
|
-
self, keywords: Union[str, Tuple[str, ...]]
|
204
|
+
self, keywords: Optional[Union[str, Tuple[str, ...]]]
|
169
205
|
) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
|
170
|
-
if not
|
206
|
+
if not keywords:
|
207
|
+
keywords = ()
|
208
|
+
elif not isinstance(keywords, tuple):
|
171
209
|
keywords = (keywords,)
|
172
210
|
|
173
211
|
_, parsed = self._read()
|
@@ -175,28 +213,21 @@ class FoamFile(
|
|
175
213
|
value = parsed[keywords]
|
176
214
|
|
177
215
|
if value is ...:
|
178
|
-
|
179
|
-
return FoamFile.Header(self)
|
180
|
-
else:
|
181
|
-
return FoamFile.SubDict(self, keywords)
|
216
|
+
return FoamFile.SubDict(self, keywords)
|
182
217
|
else:
|
183
218
|
return value
|
184
219
|
|
185
220
|
def __setitem__(
|
186
|
-
self, keywords: Union[str, Tuple[str, ...]], data: "FoamFile._SetData"
|
221
|
+
self, keywords: Optional[Union[str, Tuple[str, ...]]], data: "FoamFile._SetData"
|
187
222
|
) -> None:
|
188
223
|
with self:
|
189
|
-
if not
|
224
|
+
if not keywords:
|
225
|
+
keywords = ()
|
226
|
+
elif not isinstance(keywords, tuple):
|
190
227
|
keywords = (keywords,)
|
191
228
|
|
192
|
-
if not self and keywords[0] != "FoamFile":
|
193
|
-
self.
|
194
|
-
"version": 2.0,
|
195
|
-
"format": "ascii",
|
196
|
-
"class": "dictionary",
|
197
|
-
"location": f'"{self.path.parent.name}"',
|
198
|
-
"object": self.path.name,
|
199
|
-
} # type: ignore [assignment]
|
229
|
+
if not self and "FoamFile" not in self and keywords[0] != "FoamFile":
|
230
|
+
self._write_header()
|
200
231
|
|
201
232
|
kind = Kind.DEFAULT
|
202
233
|
if keywords == ("internalField",) or (
|
@@ -204,35 +235,13 @@ class FoamFile(
|
|
204
235
|
and keywords[0] == "boundaryField"
|
205
236
|
and keywords[2] == "value"
|
206
237
|
):
|
207
|
-
kind =
|
208
|
-
Kind.BINARY_FIELD if self.header.format == "binary" else Kind.FIELD
|
209
|
-
)
|
238
|
+
kind = Kind.BINARY_FIELD if self.format == "binary" else Kind.FIELD
|
210
239
|
elif keywords == ("dimensions",):
|
211
240
|
kind = Kind.DIMENSIONS
|
212
241
|
|
213
|
-
|
214
|
-
|
215
|
-
if isinstance(data, Mapping):
|
216
|
-
if isinstance(data, FoamDict):
|
217
|
-
data = data.as_dict()
|
218
|
-
|
219
|
-
start, end = parsed.entry_location(keywords, missing_ok=True)
|
220
|
-
|
221
|
-
self._write(
|
222
|
-
contents[:start]
|
223
|
-
+ (b"\n" if contents[start - 1 : start] != b"\n" else b"")
|
224
|
-
+ (b" " * (len(keywords) - 1) if keywords else b"")
|
225
|
-
+ dumpb({keywords[-1]: {}})
|
226
|
-
+ b"\n"
|
227
|
-
+ contents[end:]
|
228
|
-
)
|
229
|
-
|
230
|
-
for k, v in data.items():
|
231
|
-
self[(*keywords, k)] = v
|
232
|
-
|
233
|
-
elif (
|
242
|
+
if (
|
234
243
|
kind == Kind.FIELD or kind == Kind.BINARY_FIELD
|
235
|
-
) and self.
|
244
|
+
) and self.class_ == "dictionary":
|
236
245
|
if not is_sequence(data):
|
237
246
|
class_ = "volScalarField"
|
238
247
|
elif (len(data) == 3 and not is_sequence(data[0])) or len(data[0]) == 3:
|
@@ -244,27 +253,65 @@ class FoamFile(
|
|
244
253
|
else:
|
245
254
|
class_ = "volScalarField"
|
246
255
|
|
247
|
-
self.
|
256
|
+
self.class_ = class_
|
248
257
|
self[keywords] = data
|
249
258
|
|
250
259
|
else:
|
260
|
+
contents, parsed = self._read()
|
261
|
+
|
251
262
|
start, end = parsed.entry_location(keywords, missing_ok=True)
|
252
263
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
264
|
+
before = contents[:start].rstrip() + b"\n"
|
265
|
+
if len(keywords) <= 1:
|
266
|
+
before += b"\n"
|
267
|
+
|
268
|
+
after = contents[end:]
|
269
|
+
if after.startswith(b"}"):
|
270
|
+
after = b" " * (len(keywords) - 2) + after
|
271
|
+
if not after or after[:1] != b"\n":
|
272
|
+
after = b"\n" + after
|
273
|
+
if len(keywords) <= 1 and len(after) > 1 and after[:2] != b"\n\n":
|
274
|
+
after = b"\n" + after
|
275
|
+
|
276
|
+
indentation = b" " * (len(keywords) - 1)
|
277
|
+
|
278
|
+
if isinstance(data, Mapping):
|
279
|
+
if isinstance(data, (FoamFile, FoamFile.SubDict)):
|
280
|
+
data = data.as_dict()
|
281
|
+
|
282
|
+
self._write(
|
283
|
+
before
|
284
|
+
+ indentation
|
285
|
+
+ dumpb(keywords[-1])
|
286
|
+
+ b"\n"
|
287
|
+
+ indentation
|
288
|
+
+ b"{\n"
|
289
|
+
+ indentation
|
290
|
+
+ b"}"
|
291
|
+
+ after
|
292
|
+
)
|
293
|
+
|
294
|
+
for k, v in data.items():
|
295
|
+
self[(*keywords, k)] = v
|
296
|
+
|
297
|
+
elif keywords:
|
298
|
+
self._write(
|
299
|
+
before
|
300
|
+
+ indentation
|
301
|
+
+ dumpb(keywords[-1])
|
302
|
+
+ b" "
|
303
|
+
+ dumpb(data, kind=kind)
|
304
|
+
+ b";"
|
305
|
+
+ after
|
259
306
|
)
|
260
|
-
|
261
|
-
|
262
|
-
+
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
307
|
+
|
308
|
+
else:
|
309
|
+
self._write(before + dumpb(data, kind=kind) + after)
|
310
|
+
|
311
|
+
def __delitem__(self, keywords: Optional[Union[str, Tuple[str, ...]]]) -> None:
|
312
|
+
if not keywords:
|
313
|
+
keywords = ()
|
314
|
+
elif not isinstance(keywords, tuple):
|
268
315
|
keywords = (keywords,)
|
269
316
|
|
270
317
|
contents, parsed = self._read()
|
@@ -273,21 +320,26 @@ class FoamFile(
|
|
273
320
|
|
274
321
|
self._write(contents[:start] + contents[end:])
|
275
322
|
|
276
|
-
def _iter(self, keywords:
|
277
|
-
if not isinstance(keywords, tuple):
|
278
|
-
keywords = (keywords,)
|
279
|
-
|
323
|
+
def _iter(self, keywords: Tuple[str, ...] = ()) -> Iterator[Optional[str]]:
|
280
324
|
_, parsed = self._read()
|
281
325
|
|
282
|
-
yield from (
|
326
|
+
yield from (
|
327
|
+
k[-1] if k else None
|
328
|
+
for k in parsed
|
329
|
+
if k != ("FoamFile",) and k[:-1] == keywords
|
330
|
+
)
|
283
331
|
|
284
|
-
def __iter__(self) -> Iterator[str]:
|
332
|
+
def __iter__(self) -> Iterator[Optional[str]]:
|
285
333
|
return self._iter()
|
286
334
|
|
287
335
|
def __contains__(self, keywords: object) -> bool:
|
288
|
-
if not
|
336
|
+
if not keywords:
|
337
|
+
keywords = ()
|
338
|
+
elif not isinstance(keywords, tuple):
|
289
339
|
keywords = (keywords,)
|
340
|
+
|
290
341
|
_, parsed = self._read()
|
342
|
+
|
291
343
|
return keywords in parsed
|
292
344
|
|
293
345
|
def __len__(self) -> int:
|
@@ -304,10 +356,12 @@ class FoamFile(
|
|
304
356
|
def __fspath__(self) -> str:
|
305
357
|
return str(self.path)
|
306
358
|
|
307
|
-
def as_dict(self) ->
|
359
|
+
def as_dict(self) -> FoamFileBase._Dict:
|
308
360
|
"""Return a nested dict representation of the file."""
|
309
361
|
_, parsed = self._read()
|
310
|
-
|
362
|
+
d = parsed.as_dict()
|
363
|
+
del d["FoamFile"]
|
364
|
+
return d
|
311
365
|
|
312
366
|
|
313
367
|
class FoamFieldFile(FoamFile):
|
@@ -377,9 +431,11 @@ class FoamFieldFile(FoamFile):
|
|
377
431
|
del self["value"]
|
378
432
|
|
379
433
|
def __getitem__(
|
380
|
-
self, keywords: Union[str, Tuple[str, ...]]
|
434
|
+
self, keywords: Optional[Union[str, Tuple[str, ...]]]
|
381
435
|
) -> Union[FoamFile.Data, FoamFile.SubDict]:
|
382
|
-
if not
|
436
|
+
if not keywords:
|
437
|
+
keywords = ()
|
438
|
+
elif not isinstance(keywords, tuple):
|
383
439
|
keywords = (keywords,)
|
384
440
|
|
385
441
|
ret = super().__getitem__(keywords)
|
foamlib/_files/_io.py
CHANGED
foamlib/_files/_parsing.py
CHANGED
@@ -14,6 +14,7 @@ else:
|
|
14
14
|
|
15
15
|
from pyparsing import (
|
16
16
|
CharsNotIn,
|
17
|
+
Combine,
|
17
18
|
Dict,
|
18
19
|
Forward,
|
19
20
|
Group,
|
@@ -33,7 +34,7 @@ from pyparsing import (
|
|
33
34
|
printables,
|
34
35
|
)
|
35
36
|
|
36
|
-
from ._base import
|
37
|
+
from ._base import FoamFileBase
|
37
38
|
|
38
39
|
|
39
40
|
def _list_of(entry: ParserElement) -> ParserElement:
|
@@ -132,11 +133,14 @@ _SWITCH = (
|
|
132
133
|
).set_parse_action(lambda: False)
|
133
134
|
_DIMENSIONS = (
|
134
135
|
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
135
|
-
).set_parse_action(lambda tks:
|
136
|
+
).set_parse_action(lambda tks: FoamFileBase.DimensionSet(*tks))
|
136
137
|
_TENSOR = _list_of(common.number) | common.number
|
137
|
-
_IDENTIFIER =
|
138
|
+
_IDENTIFIER = Combine(
|
139
|
+
Word(identchars + "$", printables, exclude_chars="{(;)}")
|
140
|
+
+ Opt(Literal("(") + Word(printables, exclude_chars="{(;)}") + Literal(")"))
|
141
|
+
)
|
138
142
|
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
139
|
-
lambda tks:
|
143
|
+
lambda tks: FoamFileBase.Dimensioned(*reversed(tks.as_list()))
|
140
144
|
)
|
141
145
|
_FIELD = (
|
142
146
|
(Keyword("uniform").suppress() + _TENSOR)
|
@@ -164,7 +168,7 @@ _FILE = (
|
|
164
168
|
Group(
|
165
169
|
Located(
|
166
170
|
_DATA_ENTRY[1, ...].set_parse_action(
|
167
|
-
lambda tks: [
|
171
|
+
lambda tks: [None, tuple(tks) if len(tks) > 1 else tks[0]]
|
168
172
|
)
|
169
173
|
)
|
170
174
|
)
|
@@ -178,11 +182,11 @@ _FILE = (
|
|
178
182
|
)
|
179
183
|
|
180
184
|
|
181
|
-
class Parsed(Mapping[Tuple[str, ...], Union[
|
185
|
+
class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
|
182
186
|
def __init__(self, contents: bytes) -> None:
|
183
187
|
self._parsed: MutableMapping[
|
184
188
|
Tuple[str, ...],
|
185
|
-
Tuple[int, Union[
|
189
|
+
Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
|
186
190
|
] = {}
|
187
191
|
self._end = len(contents)
|
188
192
|
|
@@ -194,10 +198,12 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
194
198
|
@staticmethod
|
195
199
|
def _flatten_result(
|
196
200
|
parse_result: ParseResults, *, _keywords: Tuple[str, ...] = ()
|
197
|
-
) -> Mapping[
|
201
|
+
) -> Mapping[
|
202
|
+
Tuple[str, ...], Tuple[int, Union[FoamFileBase.Data, EllipsisType], int]
|
203
|
+
]:
|
198
204
|
ret: MutableMapping[
|
199
205
|
Tuple[str, ...],
|
200
|
-
Tuple[int, Union[
|
206
|
+
Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
|
201
207
|
] = {}
|
202
208
|
start = parse_result.locn_start
|
203
209
|
assert isinstance(start, int)
|
@@ -206,18 +212,26 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
206
212
|
end = parse_result.locn_end
|
207
213
|
assert isinstance(end, int)
|
208
214
|
keyword, *data = item
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
215
|
+
if keyword is None:
|
216
|
+
assert not _keywords
|
217
|
+
assert len(data) == 1
|
218
|
+
assert not isinstance(data[0], ParseResults)
|
219
|
+
ret[()] = (start, data[0], end)
|
220
|
+
else:
|
221
|
+
assert isinstance(keyword, str)
|
222
|
+
ret[(*_keywords, keyword)] = (start, ..., end)
|
223
|
+
for d in data:
|
224
|
+
if isinstance(d, ParseResults):
|
225
|
+
ret.update(
|
226
|
+
Parsed._flatten_result(d, _keywords=(*_keywords, keyword))
|
227
|
+
)
|
228
|
+
else:
|
229
|
+
ret[(*_keywords, keyword)] = (start, d, end)
|
216
230
|
return ret
|
217
231
|
|
218
232
|
def __getitem__(
|
219
233
|
self, keywords: Union[str, Tuple[str, ...]]
|
220
|
-
) -> Union[
|
234
|
+
) -> Union[FoamFileBase.Data, EllipsisType]:
|
221
235
|
if isinstance(keywords, str):
|
222
236
|
keywords = (keywords,)
|
223
237
|
|
@@ -252,8 +266,8 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
252
266
|
|
253
267
|
return start, end
|
254
268
|
|
255
|
-
def as_dict(self) ->
|
256
|
-
ret:
|
269
|
+
def as_dict(self) -> FoamFileBase._Dict:
|
270
|
+
ret: FoamFileBase._Dict = {}
|
257
271
|
for keywords, (_, data, _) in self._parsed.items():
|
258
272
|
r = ret
|
259
273
|
for k in keywords[:-1]:
|
foamlib/_files/_serialization.py
CHANGED
@@ -9,7 +9,7 @@ else:
|
|
9
9
|
from typing import Mapping
|
10
10
|
|
11
11
|
from .._util import is_sequence
|
12
|
-
from ._base import
|
12
|
+
from ._base import FoamFileBase
|
13
13
|
|
14
14
|
try:
|
15
15
|
import numpy as np
|
@@ -28,7 +28,7 @@ class Kind(Enum):
|
|
28
28
|
|
29
29
|
|
30
30
|
def dumpb(
|
31
|
-
data:
|
31
|
+
data: FoamFileBase._SetData,
|
32
32
|
*,
|
33
33
|
kind: Kind = Kind.DEFAULT,
|
34
34
|
) -> bytes:
|
@@ -39,18 +39,16 @@ def dumpb(
|
|
39
39
|
entries = []
|
40
40
|
for k, v in data.items():
|
41
41
|
b = dumpb(v, kind=kind)
|
42
|
-
if
|
43
|
-
entries.append(b)
|
44
|
-
elif isinstance(v, Mapping):
|
45
|
-
entries.append(dumpb(k) + b"\n" + b"{\n" + b + b"\n}")
|
42
|
+
if isinstance(v, Mapping):
|
43
|
+
entries.append(dumpb(k) + b" {" + b + b"}")
|
46
44
|
elif not b:
|
47
45
|
entries.append(dumpb(k) + b";")
|
48
46
|
else:
|
49
47
|
entries.append(dumpb(k) + b" " + b + b";")
|
50
48
|
|
51
|
-
return b"
|
49
|
+
return b" ".join(entries)
|
52
50
|
|
53
|
-
elif isinstance(data,
|
51
|
+
elif isinstance(data, FoamFileBase.DimensionSet) or (
|
54
52
|
kind == Kind.DIMENSIONS and is_sequence(data) and len(data) == 7
|
55
53
|
):
|
56
54
|
return b"[" + b" ".join(dumpb(v) for v in data) + b"]"
|
@@ -93,7 +91,7 @@ def dumpb(
|
|
93
91
|
elif kind != Kind.SINGLE_ENTRY and isinstance(data, tuple):
|
94
92
|
return b" ".join(dumpb(v) for v in data)
|
95
93
|
|
96
|
-
elif isinstance(data,
|
94
|
+
elif isinstance(data, FoamFileBase.Dimensioned):
|
97
95
|
if data.name is not None:
|
98
96
|
return (
|
99
97
|
dumpb(data.name)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.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
|
@@ -45,7 +45,7 @@ Requires-Dist: numpy <3,>=1 ; extra == 'numpy'
|
|
45
45
|
Provides-Extra: test
|
46
46
|
Requires-Dist: foamlib[numpy] ; extra == 'test'
|
47
47
|
Requires-Dist: pytest <9,>=7 ; extra == 'test'
|
48
|
-
Requires-Dist: pytest-asyncio <0.
|
48
|
+
Requires-Dist: pytest-asyncio <0.25,>=0.21 ; extra == 'test'
|
49
49
|
Requires-Dist: pytest-cov ; extra == 'test'
|
50
50
|
Provides-Extra: typing
|
51
51
|
Requires-Dist: foamlib[test] ; extra == 'typing'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
foamlib/__init__.py,sha256=Lm0oAsj1KsJFBiIAaNSSE231t6g92HybfoXNwBoFUBQ,446
|
2
|
+
foamlib/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
|
3
|
+
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
foamlib/_cases/__init__.py,sha256=xnQpR64EvFtCh07qnSz7kjQ1IhJXjRDwaZWwQGZhGv4,280
|
5
|
+
foamlib/_cases/_async.py,sha256=ehmAedDepDXaRdf8f0IznxDMYAQxoUWSIxbhxG_17Rw,6035
|
6
|
+
foamlib/_cases/_base.py,sha256=JrYJplmXV84qwgerdVSCHz2rCI-KWB4IvTBPMkYpecw,12657
|
7
|
+
foamlib/_cases/_sync.py,sha256=CloQgd-93jxfSXIt7J5NxcAu3N_iF3eXMKO-NfNOgi4,4522
|
8
|
+
foamlib/_cases/_util.py,sha256=v6sHxHCEgagsVuup0S1xJW-x9py5xj3bUye8PiFfb3o,925
|
9
|
+
foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
|
10
|
+
foamlib/_files/_base.py,sha256=sy1RP08pdv079KD_UBy7xUYEnXyNK35AiKVcCalYmPU,1873
|
11
|
+
foamlib/_files/_files.py,sha256=2bvl2YZ0bqHpbQV35vzYO7wWNOo_2WDF9LbyLagIvBE,16193
|
12
|
+
foamlib/_files/_io.py,sha256=f_tYI7AqaFsQ8mtK__fEoIUqpYb3YmrI8X5D8updmNM,2084
|
13
|
+
foamlib/_files/_parsing.py,sha256=eqY3grOnBdeXQLmM418VFKtCrpSd7FnLXuQ8h4mzD2M,8189
|
14
|
+
foamlib/_files/_serialization.py,sha256=3yb9fgjCpDoRfZoLsbZaIFrkZ3vGBzleFRw6IbaZuuY,3408
|
15
|
+
foamlib-0.4.1.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
16
|
+
foamlib-0.4.1.dist-info/METADATA,sha256=u17GpI0WPbXEBVwUo0lJI6J8sApdaTYzwX_DGbPr0rw,5457
|
17
|
+
foamlib-0.4.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
18
|
+
foamlib-0.4.1.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
19
|
+
foamlib-0.4.1.dist-info/RECORD,,
|
foamlib-0.3.23.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=pFmZVC3Cy4orJQeNLbr2JbQw3fVdOgShmuvNNIU0e9U,439
|
2
|
-
foamlib/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
|
3
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
foamlib/_cases/__init__.py,sha256=xnQpR64EvFtCh07qnSz7kjQ1IhJXjRDwaZWwQGZhGv4,280
|
5
|
-
foamlib/_cases/_async.py,sha256=ehmAedDepDXaRdf8f0IznxDMYAQxoUWSIxbhxG_17Rw,6035
|
6
|
-
foamlib/_cases/_base.py,sha256=jfKevsV5CtxsyORvp-sYrdnW4pUxs6Or0I6nGXZVjII,12336
|
7
|
-
foamlib/_cases/_sync.py,sha256=CloQgd-93jxfSXIt7J5NxcAu3N_iF3eXMKO-NfNOgi4,4522
|
8
|
-
foamlib/_cases/_util.py,sha256=v6sHxHCEgagsVuup0S1xJW-x9py5xj3bUye8PiFfb3o,925
|
9
|
-
foamlib/_files/__init__.py,sha256=vDkPj8u8bX_I_m2YfeKvXBgwg8D1ufyFCfHGHKN3JPQ,140
|
10
|
-
foamlib/_files/_base.py,sha256=YA5a-i5HZuA3JslCD6r-DwZzpSA8r42dqSXef286Ako,2050
|
11
|
-
foamlib/_files/_files.py,sha256=BPRbZLclOmxemuBTXpEeNoiUMx3VL_FUCGsX1YlidcA,14635
|
12
|
-
foamlib/_files/_io.py,sha256=hlcqQqU-1cdIbDc3YqxnMfxALo4SFAEcRIoZM2vMtnE,2083
|
13
|
-
foamlib/_files/_parsing.py,sha256=3Hp4pdVgrneCtVJl4G65cQYwOA9CwUOcH5jtIfNY014,7738
|
14
|
-
foamlib/_files/_serialization.py,sha256=LCeaLWtNvkcs0dfowL7nViiByxw7U_fvgueVjFliipU,3462
|
15
|
-
foamlib-0.3.23.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
16
|
-
foamlib-0.3.23.dist-info/METADATA,sha256=0i-sNTwsELdUk54SvL0Dz70CarcmkqU2_HYsLftSwps,5458
|
17
|
-
foamlib-0.3.23.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
|
18
|
-
foamlib-0.3.23.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
19
|
-
foamlib-0.3.23.dist-info/RECORD,,
|
File without changes
|
File without changes
|