foamlib 0.3.22__py3-none-any.whl → 0.4.0__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 -130
- foamlib/_files/_io.py +1 -1
- foamlib/_files/_parsing.py +31 -19
- foamlib/_files/_serialization.py +7 -9
- {foamlib-0.3.22.dist-info → foamlib-0.4.0.dist-info}/METADATA +2 -2
- foamlib-0.4.0.dist-info/RECORD +19 -0
- {foamlib-0.3.22.dist-info → foamlib-0.4.0.dist-info}/WHEEL +1 -1
- foamlib-0.3.22.dist-info/RECORD +0 -19
- {foamlib-0.3.22.dist-info → foamlib-0.4.0.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.3.22.dist-info → foamlib-0.4.0.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.0"
|
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,34 +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"
|
224
|
-
+ dumpb({keywords[-1]: {}})
|
225
|
-
+ b"\n"
|
226
|
-
+ contents[end:]
|
227
|
-
)
|
228
|
-
|
229
|
-
for k, v in data.items():
|
230
|
-
self[(*keywords, k)] = v
|
231
|
-
|
232
|
-
elif (
|
242
|
+
if (
|
233
243
|
kind == Kind.FIELD or kind == Kind.BINARY_FIELD
|
234
|
-
) and self.
|
244
|
+
) and self.class_ == "dictionary":
|
235
245
|
if not is_sequence(data):
|
236
246
|
class_ = "volScalarField"
|
237
247
|
elif (len(data) == 3 and not is_sequence(data[0])) or len(data[0]) == 3:
|
@@ -243,22 +253,65 @@ class FoamFile(
|
|
243
253
|
else:
|
244
254
|
class_ = "volScalarField"
|
245
255
|
|
246
|
-
self.
|
256
|
+
self.class_ = class_
|
247
257
|
self[keywords] = data
|
248
258
|
|
249
259
|
else:
|
260
|
+
contents, parsed = self._read()
|
261
|
+
|
250
262
|
start, end = parsed.entry_location(keywords, missing_ok=True)
|
251
263
|
|
252
|
-
|
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
|
306
|
+
)
|
307
|
+
|
308
|
+
else:
|
309
|
+
self._write(before + dumpb(data, kind=kind) + after)
|
259
310
|
|
260
|
-
def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
|
261
|
-
if not
|
311
|
+
def __delitem__(self, keywords: Optional[Union[str, Tuple[str, ...]]]) -> None:
|
312
|
+
if not keywords:
|
313
|
+
keywords = ()
|
314
|
+
elif not isinstance(keywords, tuple):
|
262
315
|
keywords = (keywords,)
|
263
316
|
|
264
317
|
contents, parsed = self._read()
|
@@ -267,21 +320,26 @@ class FoamFile(
|
|
267
320
|
|
268
321
|
self._write(contents[:start] + contents[end:])
|
269
322
|
|
270
|
-
def _iter(self, keywords:
|
271
|
-
if not isinstance(keywords, tuple):
|
272
|
-
keywords = (keywords,)
|
273
|
-
|
323
|
+
def _iter(self, keywords: Tuple[str, ...] = ()) -> Iterator[Optional[str]]:
|
274
324
|
_, parsed = self._read()
|
275
325
|
|
276
|
-
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
|
+
)
|
277
331
|
|
278
|
-
def __iter__(self) -> Iterator[str]:
|
332
|
+
def __iter__(self) -> Iterator[Optional[str]]:
|
279
333
|
return self._iter()
|
280
334
|
|
281
335
|
def __contains__(self, keywords: object) -> bool:
|
282
|
-
if not
|
336
|
+
if not keywords:
|
337
|
+
keywords = ()
|
338
|
+
elif not isinstance(keywords, tuple):
|
283
339
|
keywords = (keywords,)
|
340
|
+
|
284
341
|
_, parsed = self._read()
|
342
|
+
|
285
343
|
return keywords in parsed
|
286
344
|
|
287
345
|
def __len__(self) -> int:
|
@@ -298,10 +356,12 @@ class FoamFile(
|
|
298
356
|
def __fspath__(self) -> str:
|
299
357
|
return str(self.path)
|
300
358
|
|
301
|
-
def as_dict(self) ->
|
359
|
+
def as_dict(self) -> FoamFileBase._Dict:
|
302
360
|
"""Return a nested dict representation of the file."""
|
303
361
|
_, parsed = self._read()
|
304
|
-
|
362
|
+
d = parsed.as_dict()
|
363
|
+
del d["FoamFile"]
|
364
|
+
return d
|
305
365
|
|
306
366
|
|
307
367
|
class FoamFieldFile(FoamFile):
|
@@ -371,9 +431,11 @@ class FoamFieldFile(FoamFile):
|
|
371
431
|
del self["value"]
|
372
432
|
|
373
433
|
def __getitem__(
|
374
|
-
self, keywords: Union[str, Tuple[str, ...]]
|
434
|
+
self, keywords: Optional[Union[str, Tuple[str, ...]]]
|
375
435
|
) -> Union[FoamFile.Data, FoamFile.SubDict]:
|
376
|
-
if not
|
436
|
+
if not keywords:
|
437
|
+
keywords = ()
|
438
|
+
elif not isinstance(keywords, tuple):
|
377
439
|
keywords = (keywords,)
|
378
440
|
|
379
441
|
ret = super().__getitem__(keywords)
|
foamlib/_files/_io.py
CHANGED
foamlib/_files/_parsing.py
CHANGED
@@ -33,7 +33,7 @@ from pyparsing import (
|
|
33
33
|
printables,
|
34
34
|
)
|
35
35
|
|
36
|
-
from ._base import
|
36
|
+
from ._base import FoamFileBase
|
37
37
|
|
38
38
|
|
39
39
|
def _list_of(entry: ParserElement) -> ParserElement:
|
@@ -132,11 +132,11 @@ _SWITCH = (
|
|
132
132
|
).set_parse_action(lambda: False)
|
133
133
|
_DIMENSIONS = (
|
134
134
|
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
135
|
-
).set_parse_action(lambda tks:
|
135
|
+
).set_parse_action(lambda tks: FoamFileBase.DimensionSet(*tks))
|
136
136
|
_TENSOR = _list_of(common.number) | common.number
|
137
137
|
_IDENTIFIER = Word(identchars + "$", printables, exclude_chars="{;}")
|
138
138
|
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
139
|
-
lambda tks:
|
139
|
+
lambda tks: FoamFileBase.Dimensioned(*reversed(tks.as_list()))
|
140
140
|
)
|
141
141
|
_FIELD = (
|
142
142
|
(Keyword("uniform").suppress() + _TENSOR)
|
@@ -164,7 +164,7 @@ _FILE = (
|
|
164
164
|
Group(
|
165
165
|
Located(
|
166
166
|
_DATA_ENTRY[1, ...].set_parse_action(
|
167
|
-
lambda tks: [
|
167
|
+
lambda tks: [None, tuple(tks) if len(tks) > 1 else tks[0]]
|
168
168
|
)
|
169
169
|
)
|
170
170
|
)
|
@@ -178,12 +178,14 @@ _FILE = (
|
|
178
178
|
)
|
179
179
|
|
180
180
|
|
181
|
-
class Parsed(Mapping[Tuple[str, ...], Union[
|
181
|
+
class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
|
182
182
|
def __init__(self, contents: bytes) -> None:
|
183
183
|
self._parsed: MutableMapping[
|
184
184
|
Tuple[str, ...],
|
185
|
-
Tuple[int, Union[
|
185
|
+
Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
|
186
186
|
] = {}
|
187
|
+
self._end = len(contents)
|
188
|
+
|
187
189
|
for parse_result in _FILE.parse_string(
|
188
190
|
contents.decode("latin-1"), parse_all=True
|
189
191
|
):
|
@@ -192,10 +194,12 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
192
194
|
@staticmethod
|
193
195
|
def _flatten_result(
|
194
196
|
parse_result: ParseResults, *, _keywords: Tuple[str, ...] = ()
|
195
|
-
) -> Mapping[
|
197
|
+
) -> Mapping[
|
198
|
+
Tuple[str, ...], Tuple[int, Union[FoamFileBase.Data, EllipsisType], int]
|
199
|
+
]:
|
196
200
|
ret: MutableMapping[
|
197
201
|
Tuple[str, ...],
|
198
|
-
Tuple[int, Union[
|
202
|
+
Tuple[int, Union[FoamFileBase.Data, EllipsisType], int],
|
199
203
|
] = {}
|
200
204
|
start = parse_result.locn_start
|
201
205
|
assert isinstance(start, int)
|
@@ -204,18 +208,26 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
204
208
|
end = parse_result.locn_end
|
205
209
|
assert isinstance(end, int)
|
206
210
|
keyword, *data = item
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
211
|
+
if keyword is None:
|
212
|
+
assert not _keywords
|
213
|
+
assert len(data) == 1
|
214
|
+
assert not isinstance(data[0], ParseResults)
|
215
|
+
ret[()] = (start, data[0], end)
|
216
|
+
else:
|
217
|
+
assert isinstance(keyword, str)
|
218
|
+
ret[(*_keywords, keyword)] = (start, ..., end)
|
219
|
+
for d in data:
|
220
|
+
if isinstance(d, ParseResults):
|
221
|
+
ret.update(
|
222
|
+
Parsed._flatten_result(d, _keywords=(*_keywords, keyword))
|
223
|
+
)
|
224
|
+
else:
|
225
|
+
ret[(*_keywords, keyword)] = (start, d, end)
|
214
226
|
return ret
|
215
227
|
|
216
228
|
def __getitem__(
|
217
229
|
self, keywords: Union[str, Tuple[str, ...]]
|
218
|
-
) -> Union[
|
230
|
+
) -> Union[FoamFileBase.Data, EllipsisType]:
|
219
231
|
if isinstance(keywords, str):
|
220
232
|
keywords = (keywords,)
|
221
233
|
|
@@ -242,7 +254,7 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
242
254
|
_, _, end = self._parsed[keywords[:-1]]
|
243
255
|
end -= 1
|
244
256
|
else:
|
245
|
-
end =
|
257
|
+
end = self._end
|
246
258
|
|
247
259
|
start = end
|
248
260
|
else:
|
@@ -250,8 +262,8 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
|
250
262
|
|
251
263
|
return start, end
|
252
264
|
|
253
|
-
def as_dict(self) ->
|
254
|
-
ret:
|
265
|
+
def as_dict(self) -> FoamFileBase._Dict:
|
266
|
+
ret: FoamFileBase._Dict = {}
|
255
267
|
for keywords, (_, data, _) in self._parsed.items():
|
256
268
|
r = ret
|
257
269
|
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.0
|
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=ZL6X6uzZgGj8D5Thi9ZuZyr1Mpo6f78yT_Rf4sBU12I,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=bOJkqNUMUiXZJQXXEnIAb0bcdj_AgDmDhrr19oyY_Sw,8078
|
14
|
+
foamlib/_files/_serialization.py,sha256=3yb9fgjCpDoRfZoLsbZaIFrkZ3vGBzleFRw6IbaZuuY,3408
|
15
|
+
foamlib-0.4.0.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
16
|
+
foamlib-0.4.0.dist-info/METADATA,sha256=3DObrf-prhkIgLQ2crezzAVkKW4ZIuNrXEsbqT3ilUc,5457
|
17
|
+
foamlib-0.4.0.dist-info/WHEEL,sha256=uCRv0ZEik_232NlR4YDw4Pv3Ajt5bKvMH13NUU7hFuI,91
|
18
|
+
foamlib-0.4.0.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
19
|
+
foamlib-0.4.0.dist-info/RECORD,,
|
foamlib-0.3.22.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=ubVl04mBxekohoPKeF_Ap77kAq8Sg69BeDC5qKNy66k,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=rH__Zk7ScS4Q2jyuOcbWnrg0NX_sMSb3D4CiK13Z03A,14277
|
12
|
-
foamlib/_files/_io.py,sha256=hlcqQqU-1cdIbDc3YqxnMfxALo4SFAEcRIoZM2vMtnE,2083
|
13
|
-
foamlib/_files/_parsing.py,sha256=-IgvCOFMAERNOrpw2NaHtVXubBg4Ey6xR55bzEZb2B4,7696
|
14
|
-
foamlib/_files/_serialization.py,sha256=LCeaLWtNvkcs0dfowL7nViiByxw7U_fvgueVjFliipU,3462
|
15
|
-
foamlib-0.3.22.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
16
|
-
foamlib-0.3.22.dist-info/METADATA,sha256=8LhhVcQYHM8WC_dva0cmrFlwWGl3Nmw0NkrpzSgTkGk,5458
|
17
|
-
foamlib-0.3.22.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
18
|
-
foamlib-0.3.22.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
19
|
-
foamlib-0.3.22.dist-info/RECORD,,
|
File without changes
|
File without changes
|