foamlib 0.2.10__py3-none-any.whl → 0.3.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 +5 -3
- foamlib/_cases.py +18 -30
- foamlib/_files/__init__.py +2 -2
- foamlib/_files/_base.py +22 -13
- foamlib/_files/_fields.py +31 -24
- foamlib/_files/_files.py +24 -24
- foamlib/_files/_parsing.py +42 -43
- foamlib/_files/_serialization.py +65 -111
- {foamlib-0.2.10.dist-info → foamlib-0.3.1.dist-info}/METADATA +1 -1
- foamlib-0.3.1.dist-info/RECORD +16 -0
- foamlib-0.2.10.dist-info/RECORD +0 -16
- {foamlib-0.2.10.dist-info → foamlib-0.3.1.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.2.10.dist-info → foamlib-0.3.1.dist-info}/WHEEL +0 -0
- {foamlib-0.2.10.dist-info → foamlib-0.3.1.dist-info}/top_level.txt +0 -0
foamlib/__init__.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
"""A Python interface for interacting with OpenFOAM."""
|
2
2
|
|
3
|
-
__version__ = "0.
|
3
|
+
__version__ = "0.3.1"
|
4
4
|
|
5
5
|
from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
|
6
|
-
from ._files import
|
6
|
+
from ._files import FoamDict, FoamFieldFile, FoamFile
|
7
|
+
from ._util import CalledProcessError
|
7
8
|
|
8
9
|
__all__ = [
|
9
10
|
"FoamCase",
|
@@ -11,5 +12,6 @@ __all__ = [
|
|
11
12
|
"FoamCaseBase",
|
12
13
|
"FoamFile",
|
13
14
|
"FoamFieldFile",
|
14
|
-
"
|
15
|
+
"FoamDict",
|
16
|
+
"CalledProcessError",
|
15
17
|
]
|
foamlib/_cases.py
CHANGED
@@ -28,7 +28,7 @@ else:
|
|
28
28
|
import aioshutil
|
29
29
|
|
30
30
|
from ._files import FoamFieldFile, FoamFile
|
31
|
-
from ._util import
|
31
|
+
from ._util import is_sequence, run_process, run_process_async
|
32
32
|
|
33
33
|
|
34
34
|
class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
@@ -325,7 +325,7 @@ class FoamCase(FoamCaseBase):
|
|
325
325
|
Clean this case.
|
326
326
|
|
327
327
|
:param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
|
328
|
-
:param check: If True, raise a
|
328
|
+
:param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
|
329
329
|
"""
|
330
330
|
script_path = self._clean_script() if script else None
|
331
331
|
|
@@ -349,24 +349,18 @@ class FoamCase(FoamCaseBase):
|
|
349
349
|
:param cmd: The command to run. If None, run the case. If a sequence, the first element is the command and the rest are arguments. If a string, `cmd` is executed in a shell.
|
350
350
|
:param script: If True and `cmd` is None, use an (All)run(-parallel) script if it exists for running the case. If False or no run script is found, autodetermine the command(s) needed to run the case.
|
351
351
|
:param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
|
352
|
-
:param check: If True, raise a
|
352
|
+
:param check: If True, raise a CalledProcessError if any command returns a non-zero exit code.
|
353
353
|
"""
|
354
354
|
if cmd is not None:
|
355
355
|
if parallel:
|
356
356
|
cmd = self._parallel_cmd(cmd)
|
357
357
|
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
)
|
365
|
-
except CalledProcessError as e:
|
366
|
-
raise RuntimeError(
|
367
|
-
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
|
368
|
-
) from None
|
369
|
-
|
358
|
+
run_process(
|
359
|
+
cmd,
|
360
|
+
check=check,
|
361
|
+
cwd=self.path,
|
362
|
+
env=self._env(),
|
363
|
+
)
|
370
364
|
else:
|
371
365
|
script_path = self._run_script(parallel=parallel) if script else None
|
372
366
|
|
@@ -485,7 +479,7 @@ class AsyncFoamCase(FoamCaseBase):
|
|
485
479
|
Clean this case.
|
486
480
|
|
487
481
|
:param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
|
488
|
-
:param check: If True, raise a
|
482
|
+
:param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
|
489
483
|
"""
|
490
484
|
script_path = self._clean_script() if script else None
|
491
485
|
|
@@ -511,7 +505,7 @@ class AsyncFoamCase(FoamCaseBase):
|
|
511
505
|
:param script: If True and `cmd` is None, use an (All)run(-parallel) script if it exists for running the case. If False or no run script is found, autodetermine the command(s) needed to run the case.
|
512
506
|
:param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
|
513
507
|
:param cpus: The number of CPUs to reserve for the run. The run will wait until the requested number of CPUs is available. If None, autodetect the number of CPUs to reserve.
|
514
|
-
:param check: If True, raise a
|
508
|
+
:param check: If True, raise a CalledProcessError if a command returns a non-zero exit code.
|
515
509
|
"""
|
516
510
|
if cmd is not None:
|
517
511
|
if cpus is None:
|
@@ -523,19 +517,13 @@ class AsyncFoamCase(FoamCaseBase):
|
|
523
517
|
if parallel:
|
524
518
|
cmd = self._parallel_cmd(cmd)
|
525
519
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
)
|
534
|
-
except CalledProcessError as e:
|
535
|
-
raise RuntimeError(
|
536
|
-
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
|
537
|
-
) from None
|
538
|
-
|
520
|
+
async with self._cpus(cpus):
|
521
|
+
await run_process_async(
|
522
|
+
cmd,
|
523
|
+
check=check,
|
524
|
+
cwd=self.path,
|
525
|
+
env=self._env(),
|
526
|
+
)
|
539
527
|
else:
|
540
528
|
script_path = self._run_script(parallel=parallel) if script else None
|
541
529
|
|
foamlib/_files/__init__.py
CHANGED
foamlib/_files/_base.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import sys
|
2
2
|
from abc import abstractmethod
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import Dict, NamedTuple, Optional, Union
|
4
|
+
from typing import Dict, NamedTuple, Optional, Tuple, Union
|
5
5
|
|
6
6
|
if sys.version_info >= (3, 9):
|
7
7
|
from collections.abc import Mapping, Sequence
|
@@ -10,12 +10,11 @@ else:
|
|
10
10
|
|
11
11
|
try:
|
12
12
|
import numpy as np
|
13
|
-
from numpy.typing import NDArray
|
14
13
|
except ModuleNotFoundError:
|
15
14
|
pass
|
16
15
|
|
17
16
|
|
18
|
-
class
|
17
|
+
class FoamDict:
|
19
18
|
class DimensionSet(NamedTuple):
|
20
19
|
mass: Union[int, float] = 0
|
21
20
|
length: Union[int, float] = 0
|
@@ -31,34 +30,44 @@ class FoamDictionaryBase:
|
|
31
30
|
@dataclass
|
32
31
|
class Dimensioned:
|
33
32
|
value: Union[int, float, Sequence[Union[int, float]]] = 0
|
34
|
-
dimensions: Union[
|
35
|
-
"FoamDictionaryBase.DimensionSet", Sequence[Union[int, float]]
|
36
|
-
] = ()
|
33
|
+
dimensions: Union["FoamDict.DimensionSet", Sequence[Union[int, float]]] = ()
|
37
34
|
name: Optional[str] = None
|
38
35
|
|
39
36
|
def __post_init__(self) -> None:
|
40
|
-
if not isinstance(self.dimensions,
|
41
|
-
self.dimensions =
|
37
|
+
if not isinstance(self.dimensions, FoamDict.DimensionSet):
|
38
|
+
self.dimensions = FoamDict.DimensionSet(*self.dimensions)
|
42
39
|
|
43
|
-
|
40
|
+
Data = Union[
|
44
41
|
str,
|
45
42
|
int,
|
46
43
|
float,
|
47
44
|
bool,
|
48
45
|
Dimensioned,
|
49
46
|
DimensionSet,
|
50
|
-
Sequence["
|
51
|
-
Mapping[str, "
|
47
|
+
Sequence["Data"],
|
48
|
+
Mapping[str, "Data"],
|
52
49
|
]
|
53
50
|
"""
|
54
51
|
A value that can be stored in an OpenFOAM dictionary.
|
55
52
|
"""
|
56
53
|
|
57
|
-
_Dict = Dict[str, Union["
|
54
|
+
_Dict = Dict[str, Union["Data", "_Dict"]]
|
58
55
|
|
59
56
|
@abstractmethod
|
60
57
|
def as_dict(self) -> _Dict:
|
61
58
|
"""Return a nested dict representation of the dictionary."""
|
62
59
|
raise NotImplementedError
|
63
60
|
|
64
|
-
|
61
|
+
_SetData = Union[
|
62
|
+
str,
|
63
|
+
int,
|
64
|
+
float,
|
65
|
+
bool,
|
66
|
+
Dimensioned,
|
67
|
+
DimensionSet,
|
68
|
+
Sequence["_SetData"],
|
69
|
+
Mapping[str, "_SetData"],
|
70
|
+
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
71
|
+
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
72
|
+
"np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
|
73
|
+
]
|
foamlib/_files/_fields.py
CHANGED
@@ -10,7 +10,6 @@ from ._files import FoamFile
|
|
10
10
|
|
11
11
|
try:
|
12
12
|
import numpy as np
|
13
|
-
from numpy.typing import NDArray
|
14
13
|
except ModuleNotFoundError:
|
15
14
|
pass
|
16
15
|
|
@@ -18,26 +17,26 @@ except ModuleNotFoundError:
|
|
18
17
|
class FoamFieldFile(FoamFile):
|
19
18
|
"""An OpenFOAM dictionary file representing a field as a mutable mapping."""
|
20
19
|
|
21
|
-
class
|
22
|
-
def __getitem__(self, keyword: str) -> "FoamFieldFile.
|
20
|
+
class BoundariesSubDict(FoamFile.SubDict):
|
21
|
+
def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundarySubDict":
|
23
22
|
value = super().__getitem__(keyword)
|
24
|
-
if not isinstance(value, FoamFieldFile.
|
25
|
-
assert not isinstance(value, FoamFile.
|
23
|
+
if not isinstance(value, FoamFieldFile.BoundarySubDict):
|
24
|
+
assert not isinstance(value, FoamFile.SubDict)
|
26
25
|
raise TypeError(f"boundary {keyword} is not a dictionary")
|
27
26
|
return value
|
28
27
|
|
29
|
-
class
|
28
|
+
class BoundarySubDict(FoamFile.SubDict):
|
30
29
|
"""An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
|
31
30
|
|
32
31
|
def __setitem__(
|
33
32
|
self,
|
34
|
-
|
35
|
-
|
33
|
+
keyword: str,
|
34
|
+
data: FoamFile._SetData,
|
36
35
|
) -> None:
|
37
|
-
if
|
38
|
-
self._setitem(
|
36
|
+
if keyword == "value":
|
37
|
+
self._setitem(keyword, data, assume_field=True)
|
39
38
|
else:
|
40
|
-
self._setitem(
|
39
|
+
self._setitem(keyword, data)
|
41
40
|
|
42
41
|
@property
|
43
42
|
def type(self) -> str:
|
@@ -48,8 +47,8 @@ class FoamFieldFile(FoamFile):
|
|
48
47
|
return ret
|
49
48
|
|
50
49
|
@type.setter
|
51
|
-
def type(self,
|
52
|
-
self["type"] =
|
50
|
+
def type(self, data: str) -> None:
|
51
|
+
self["type"] = data
|
53
52
|
|
54
53
|
@property
|
55
54
|
def value(
|
@@ -58,7 +57,9 @@ class FoamFieldFile(FoamFile):
|
|
58
57
|
int,
|
59
58
|
float,
|
60
59
|
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
61
|
-
"
|
60
|
+
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
61
|
+
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
62
|
+
"np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
|
62
63
|
]:
|
63
64
|
"""Alias of `self["value"]`."""
|
64
65
|
ret = self["value"]
|
@@ -73,7 +74,9 @@ class FoamFieldFile(FoamFile):
|
|
73
74
|
int,
|
74
75
|
float,
|
75
76
|
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
76
|
-
"
|
77
|
+
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
78
|
+
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
79
|
+
"np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
|
77
80
|
],
|
78
81
|
) -> None:
|
79
82
|
self["value"] = value
|
@@ -84,16 +87,16 @@ class FoamFieldFile(FoamFile):
|
|
84
87
|
|
85
88
|
def __getitem__(
|
86
89
|
self, keywords: Union[str, Tuple[str, ...]]
|
87
|
-
) -> Union[FoamFile.
|
90
|
+
) -> Union[FoamFile.Data, FoamFile.SubDict]:
|
88
91
|
if not isinstance(keywords, tuple):
|
89
92
|
keywords = (keywords,)
|
90
93
|
|
91
94
|
ret = super().__getitem__(keywords)
|
92
|
-
if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.
|
95
|
+
if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.SubDict):
|
93
96
|
if len(keywords) == 1:
|
94
|
-
ret = FoamFieldFile.
|
97
|
+
ret = FoamFieldFile.BoundariesSubDict(self, keywords)
|
95
98
|
elif len(keywords) == 2:
|
96
|
-
ret = FoamFieldFile.
|
99
|
+
ret = FoamFieldFile.BoundarySubDict(self, keywords)
|
97
100
|
return ret
|
98
101
|
|
99
102
|
def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
|
@@ -128,7 +131,9 @@ class FoamFieldFile(FoamFile):
|
|
128
131
|
int,
|
129
132
|
float,
|
130
133
|
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
131
|
-
"
|
134
|
+
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
135
|
+
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
136
|
+
"np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
|
132
137
|
]:
|
133
138
|
"""Alias of `self["internalField"]`."""
|
134
139
|
ret = self["internalField"]
|
@@ -143,16 +148,18 @@ class FoamFieldFile(FoamFile):
|
|
143
148
|
int,
|
144
149
|
float,
|
145
150
|
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
146
|
-
"
|
151
|
+
"np.ndarray[Tuple[()], np.dtype[np.generic]]",
|
152
|
+
"np.ndarray[Tuple[int], np.dtype[np.generic]]",
|
153
|
+
"np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
|
147
154
|
],
|
148
155
|
) -> None:
|
149
156
|
self["internalField"] = value
|
150
157
|
|
151
158
|
@property
|
152
|
-
def boundary_field(self) -> "FoamFieldFile.
|
159
|
+
def boundary_field(self) -> "FoamFieldFile.BoundariesSubDict":
|
153
160
|
"""Alias of `self["boundaryField"]`."""
|
154
161
|
ret = self["boundaryField"]
|
155
|
-
if not isinstance(ret, FoamFieldFile.
|
156
|
-
assert not isinstance(ret, FoamFile.
|
162
|
+
if not isinstance(ret, FoamFieldFile.BoundariesSubDict):
|
163
|
+
assert not isinstance(ret, FoamFile.SubDict)
|
157
164
|
raise TypeError("boundaryField is not a dictionary")
|
158
165
|
return ret
|
foamlib/_files/_files.py
CHANGED
@@ -10,15 +10,15 @@ if sys.version_info >= (3, 9):
|
|
10
10
|
else:
|
11
11
|
from typing import Iterator, Mapping, MutableMapping
|
12
12
|
|
13
|
-
from ._base import
|
13
|
+
from ._base import FoamDict
|
14
14
|
from ._io import FoamFileIO
|
15
|
-
from ._serialization import
|
15
|
+
from ._serialization import serialize
|
16
16
|
|
17
17
|
|
18
18
|
class FoamFile(
|
19
|
-
|
19
|
+
FoamDict,
|
20
20
|
MutableMapping[
|
21
|
-
Union[str, Tuple[str, ...]], Union["FoamFile.
|
21
|
+
Union[str, Tuple[str, ...]], Union["FoamFile.Data", "FoamFile.SubDict"]
|
22
22
|
],
|
23
23
|
FoamFileIO,
|
24
24
|
):
|
@@ -30,9 +30,9 @@ class FoamFile(
|
|
30
30
|
Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
|
31
31
|
"""
|
32
32
|
|
33
|
-
class
|
34
|
-
|
35
|
-
MutableMapping[str, Union["FoamFile.
|
33
|
+
class SubDict(
|
34
|
+
FoamDict,
|
35
|
+
MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
|
36
36
|
):
|
37
37
|
"""An OpenFOAM dictionary within a file as a mutable mapping."""
|
38
38
|
|
@@ -42,25 +42,25 @@ class FoamFile(
|
|
42
42
|
|
43
43
|
def __getitem__(
|
44
44
|
self, keyword: str
|
45
|
-
) -> Union["FoamFile.
|
45
|
+
) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
|
46
46
|
return self._file[(*self._keywords, keyword)]
|
47
47
|
|
48
48
|
def _setitem(
|
49
49
|
self,
|
50
50
|
keyword: str,
|
51
|
-
|
51
|
+
data: Any,
|
52
52
|
*,
|
53
53
|
assume_field: bool = False,
|
54
54
|
assume_dimensions: bool = False,
|
55
55
|
) -> None:
|
56
56
|
self._file._setitem(
|
57
57
|
(*self._keywords, keyword),
|
58
|
-
|
58
|
+
data,
|
59
59
|
assume_field=assume_field,
|
60
60
|
assume_dimensions=assume_dimensions,
|
61
61
|
)
|
62
62
|
|
63
|
-
def __setitem__(self, keyword: str, value: "FoamFile.
|
63
|
+
def __setitem__(self, keyword: str, value: "FoamFile._SetData") -> None:
|
64
64
|
self._setitem(keyword, value)
|
65
65
|
|
66
66
|
def __delitem__(self, keyword: str) -> None:
|
@@ -86,7 +86,7 @@ class FoamFile(
|
|
86
86
|
def __repr__(self) -> str:
|
87
87
|
return f"{type(self).__qualname__}({self._file}, {self._keywords})"
|
88
88
|
|
89
|
-
def as_dict(self) ->
|
89
|
+
def as_dict(self) -> FoamDict._Dict:
|
90
90
|
"""Return a nested dict representation of the dictionary."""
|
91
91
|
ret = self._file.as_dict()
|
92
92
|
|
@@ -100,7 +100,7 @@ class FoamFile(
|
|
100
100
|
|
101
101
|
def __getitem__(
|
102
102
|
self, keywords: Union[str, Tuple[str, ...]]
|
103
|
-
) -> Union["FoamFile.
|
103
|
+
) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
|
104
104
|
if not isinstance(keywords, tuple):
|
105
105
|
keywords = (keywords,)
|
106
106
|
|
@@ -109,14 +109,14 @@ class FoamFile(
|
|
109
109
|
value = parsed[keywords]
|
110
110
|
|
111
111
|
if value is ...:
|
112
|
-
return FoamFile.
|
112
|
+
return FoamFile.SubDict(self, keywords)
|
113
113
|
else:
|
114
114
|
return value # type: ignore [return-value]
|
115
115
|
|
116
116
|
def _setitem(
|
117
117
|
self,
|
118
118
|
keywords: Union[str, Tuple[str, ...]],
|
119
|
-
|
119
|
+
data: "FoamFile._SetData",
|
120
120
|
*,
|
121
121
|
assume_field: bool = False,
|
122
122
|
assume_dimensions: bool = False,
|
@@ -126,32 +126,32 @@ class FoamFile(
|
|
126
126
|
|
127
127
|
contents, parsed = self._read()
|
128
128
|
|
129
|
-
if isinstance(
|
129
|
+
if isinstance(data, Mapping):
|
130
130
|
with self:
|
131
|
-
if isinstance(
|
132
|
-
|
131
|
+
if isinstance(data, FoamDict):
|
132
|
+
data = data.as_dict()
|
133
133
|
|
134
134
|
start, end = parsed.entry_location(keywords, missing_ok=True)
|
135
135
|
|
136
136
|
self._write(
|
137
|
-
f"{contents[:start]}\n{
|
137
|
+
f"{contents[:start]}\n{serialize({keywords[-1]: {}})}\n{contents[end:]}"
|
138
138
|
)
|
139
139
|
|
140
|
-
for k, v in
|
140
|
+
for k, v in data.items():
|
141
141
|
self[(*keywords, k)] = v
|
142
142
|
else:
|
143
143
|
start, end = parsed.entry_location(keywords, missing_ok=True)
|
144
144
|
|
145
145
|
self._write(
|
146
|
-
f"{contents[:start]}\n{
|
146
|
+
f"{contents[:start]}\n{serialize({keywords[-1]: data}, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
|
147
147
|
)
|
148
148
|
|
149
149
|
def __setitem__(
|
150
150
|
self,
|
151
151
|
keywords: Union[str, Tuple[str, ...]],
|
152
|
-
|
152
|
+
data: "FoamFile._SetData",
|
153
153
|
) -> None:
|
154
|
-
self._setitem(keywords,
|
154
|
+
self._setitem(keywords, data)
|
155
155
|
|
156
156
|
def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
|
157
157
|
if not isinstance(keywords, tuple):
|
@@ -197,7 +197,7 @@ class FoamFile(
|
|
197
197
|
def __repr__(self) -> str:
|
198
198
|
return f"{type(self).__name__}({self.path})"
|
199
199
|
|
200
|
-
def as_dict(self) ->
|
200
|
+
def as_dict(self) -> FoamDict._Dict:
|
201
201
|
"""Return a nested dict representation of the file."""
|
202
202
|
_, parsed = self._read()
|
203
203
|
return parsed.as_dict()
|
foamlib/_files/_parsing.py
CHANGED
@@ -31,10 +31,10 @@ from pyparsing import (
|
|
31
31
|
printables,
|
32
32
|
)
|
33
33
|
|
34
|
-
from ._base import
|
34
|
+
from ._base import FoamDict
|
35
35
|
|
36
36
|
|
37
|
-
def _list_of(
|
37
|
+
def _list_of(entry: ParserElement) -> ParserElement:
|
38
38
|
return Opt(
|
39
39
|
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
40
40
|
).suppress() + (
|
@@ -42,36 +42,36 @@ def _list_of(elem: ParserElement) -> ParserElement:
|
|
42
42
|
Opt(common.integer).suppress()
|
43
43
|
+ (
|
44
44
|
Literal("(").suppress()
|
45
|
-
+ Group((
|
45
|
+
+ Group((entry)[...], aslist=True)
|
46
46
|
+ Literal(")").suppress()
|
47
47
|
)
|
48
48
|
)
|
49
49
|
| (
|
50
|
-
common.integer + Literal("{").suppress() +
|
50
|
+
common.integer + Literal("{").suppress() + entry + Literal("}").suppress()
|
51
51
|
).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
|
52
52
|
)
|
53
53
|
|
54
54
|
|
55
55
|
def _dictionary_of(
|
56
56
|
keyword: ParserElement,
|
57
|
-
|
57
|
+
data_entries: ParserElement,
|
58
58
|
*,
|
59
59
|
len: Union[int, EllipsisType] = ...,
|
60
60
|
located: bool = False,
|
61
61
|
) -> ParserElement:
|
62
62
|
subdict = Forward()
|
63
63
|
|
64
|
-
|
64
|
+
keyword_entry = keyword + (
|
65
65
|
(Literal("{").suppress() + subdict + Literal("}").suppress())
|
66
|
-
| (
|
66
|
+
| (data_entries + Literal(";").suppress())
|
67
67
|
)
|
68
68
|
|
69
69
|
if located:
|
70
|
-
|
70
|
+
keyword_entry = Located(keyword_entry)
|
71
71
|
|
72
|
-
subdict <<= Dict(Group(
|
72
|
+
subdict <<= Dict(Group(keyword_entry)[...], asdict=not located)
|
73
73
|
|
74
|
-
return Dict(Group(
|
74
|
+
return Dict(Group(keyword_entry)[len], asdict=not located)
|
75
75
|
|
76
76
|
|
77
77
|
_SWITCH = (
|
@@ -81,41 +81,42 @@ _SWITCH = (
|
|
81
81
|
).set_parse_action(lambda: False)
|
82
82
|
_DIMENSIONS = (
|
83
83
|
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
84
|
-
).set_parse_action(lambda tks:
|
85
|
-
|
84
|
+
).set_parse_action(lambda tks: FoamDict.DimensionSet(*tks))
|
86
85
|
_TENSOR = _list_of(common.number) | common.number
|
87
86
|
_IDENTIFIER = Word(identbodychars + "$", printables.replace(";", ""))
|
88
87
|
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
89
|
-
lambda tks:
|
88
|
+
lambda tks: FoamDict.Dimensioned(*reversed(tks.as_list()))
|
90
89
|
)
|
91
90
|
_FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
|
92
91
|
Keyword("nonuniform").suppress() + _list_of(_TENSOR)
|
93
92
|
)
|
94
93
|
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
)
|
94
|
+
_DATA = Forward()
|
95
|
+
_KEYWORD_ENTRY = _dictionary_of(_TOKEN, _DATA, len=1)
|
96
|
+
_DATA_ENTRY = Forward()
|
97
|
+
_LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
|
98
|
+
_LIST = _list_of(_LIST_ENTRY)
|
99
|
+
_DATA_ENTRY <<= (
|
100
|
+
_FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
|
101
|
+
)
|
103
102
|
|
104
|
-
|
103
|
+
_DATA <<= _DATA_ENTRY[1, ...].set_parse_action(
|
104
|
+
lambda tks: tuple(tks) if len(tks) > 1 else [tks[0]]
|
105
|
+
)
|
105
106
|
|
106
107
|
_FILE = (
|
107
|
-
_dictionary_of(_TOKEN, Opt(
|
108
|
+
_dictionary_of(_TOKEN, Opt(_DATA, default=""), located=True)
|
108
109
|
.ignore(c_style_comment)
|
109
110
|
.ignore(cpp_style_comment)
|
110
111
|
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
|
111
112
|
)
|
112
113
|
|
113
114
|
|
114
|
-
class Parsed(Mapping[Tuple[str, ...], Union[
|
115
|
+
class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
|
115
116
|
def __init__(self, contents: str) -> None:
|
116
117
|
self._parsed: MutableMapping[
|
117
118
|
Tuple[str, ...],
|
118
|
-
Tuple[int, Union[
|
119
|
+
Tuple[int, Union[FoamDict.Data, EllipsisType], int],
|
119
120
|
] = {}
|
120
121
|
for parse_result in _FILE.parse_string(contents, parse_all=True):
|
121
122
|
self._parsed.update(self._flatten_result(parse_result))
|
@@ -123,12 +124,10 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
|
|
123
124
|
@staticmethod
|
124
125
|
def _flatten_result(
|
125
126
|
parse_result: ParseResults, *, _keywords: Tuple[str, ...] = ()
|
126
|
-
) -> Mapping[
|
127
|
-
Tuple[str, ...], Tuple[int, Union[FoamDictionaryBase.Value, EllipsisType], int]
|
128
|
-
]:
|
127
|
+
) -> Mapping[Tuple[str, ...], Tuple[int, Union[FoamDict.Data, EllipsisType], int]]:
|
129
128
|
ret: MutableMapping[
|
130
129
|
Tuple[str, ...],
|
131
|
-
Tuple[int, Union[
|
130
|
+
Tuple[int, Union[FoamDict.Data, EllipsisType], int],
|
132
131
|
] = {}
|
133
132
|
start = parse_result.locn_start
|
134
133
|
assert isinstance(start, int)
|
@@ -136,21 +135,21 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
|
|
136
135
|
assert isinstance(item, Sequence)
|
137
136
|
end = parse_result.locn_end
|
138
137
|
assert isinstance(end, int)
|
139
|
-
|
140
|
-
assert isinstance(
|
141
|
-
ret[(*_keywords,
|
142
|
-
for
|
143
|
-
if isinstance(
|
144
|
-
ret.update(Parsed._flatten_result(
|
138
|
+
keyword, *data = item
|
139
|
+
assert isinstance(keyword, str)
|
140
|
+
ret[(*_keywords, keyword)] = (start, ..., end)
|
141
|
+
for d in data:
|
142
|
+
if isinstance(d, ParseResults):
|
143
|
+
ret.update(Parsed._flatten_result(d, _keywords=(*_keywords, keyword)))
|
145
144
|
else:
|
146
|
-
ret[(*_keywords,
|
145
|
+
ret[(*_keywords, keyword)] = (start, d, end)
|
147
146
|
return ret
|
148
147
|
|
149
148
|
def __getitem__(
|
150
149
|
self, keywords: Tuple[str, ...]
|
151
|
-
) -> Union[
|
152
|
-
_,
|
153
|
-
return
|
150
|
+
) -> Union[FoamDict.Data, EllipsisType]:
|
151
|
+
_, data, _ = self._parsed[keywords]
|
152
|
+
return data
|
154
153
|
|
155
154
|
def __contains__(self, keywords: object) -> bool:
|
156
155
|
return keywords in self._parsed
|
@@ -180,9 +179,9 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
|
|
180
179
|
|
181
180
|
return start, end
|
182
181
|
|
183
|
-
def as_dict(self) ->
|
184
|
-
ret:
|
185
|
-
for keywords, (_,
|
182
|
+
def as_dict(self) -> FoamDict._Dict:
|
183
|
+
ret: FoamDict._Dict = {}
|
184
|
+
for keywords, (_, data, _) in self._parsed.items():
|
186
185
|
r = ret
|
187
186
|
for k in keywords[:-1]:
|
188
187
|
assert isinstance(r, dict)
|
@@ -191,6 +190,6 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
|
|
191
190
|
r = v
|
192
191
|
|
193
192
|
assert isinstance(r, dict)
|
194
|
-
r[keywords[-1]] = {} if
|
193
|
+
r[keywords[-1]] = {} if data is ... else data # type: ignore [assignment]
|
195
194
|
|
196
195
|
return ret
|
foamlib/_files/_serialization.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import sys
|
2
|
-
from contextlib import suppress
|
3
2
|
|
4
3
|
if sys.version_info >= (3, 9):
|
5
4
|
from collections.abc import Mapping
|
@@ -7,123 +6,78 @@ else:
|
|
7
6
|
from typing import Mapping
|
8
7
|
|
9
8
|
from .._util import is_sequence
|
10
|
-
from ._base import
|
9
|
+
from ._base import FoamDict
|
11
10
|
|
12
11
|
|
13
|
-
def
|
14
|
-
|
15
|
-
return "yes"
|
16
|
-
elif value is False:
|
17
|
-
return "no"
|
18
|
-
else:
|
19
|
-
raise TypeError(f"Not a bool: {type(value)}")
|
20
|
-
|
21
|
-
|
22
|
-
def _serialize_list(
|
23
|
-
value: FoamDictionaryBase._SetValue,
|
24
|
-
) -> str:
|
25
|
-
if is_sequence(value):
|
26
|
-
return f"({' '.join(_serialize_value(v) for v in value)})"
|
27
|
-
else:
|
28
|
-
raise TypeError(f"Not a valid sequence: {type(value)}")
|
29
|
-
|
30
|
-
|
31
|
-
def _serialize_field(
|
32
|
-
value: FoamDictionaryBase._SetValue,
|
33
|
-
) -> str:
|
34
|
-
if is_sequence(value):
|
35
|
-
try:
|
36
|
-
s = _serialize_list(value)
|
37
|
-
except TypeError:
|
38
|
-
raise TypeError(f"Not a valid field: {type(value)}") from None
|
39
|
-
else:
|
40
|
-
if not is_sequence(value[0]) and len(value) < 10:
|
41
|
-
return f"uniform {s}"
|
42
|
-
else:
|
43
|
-
if not is_sequence(value[0]):
|
44
|
-
kind = "scalar"
|
45
|
-
elif len(value[0]) == 3:
|
46
|
-
kind = "vector"
|
47
|
-
elif len(value[0]) == 6:
|
48
|
-
kind = "symmTensor"
|
49
|
-
elif len(value[0]) == 9:
|
50
|
-
kind = "tensor"
|
51
|
-
else:
|
52
|
-
raise TypeError(
|
53
|
-
f"Unsupported sequence length for field: {len(value[0])}"
|
54
|
-
)
|
55
|
-
return f"nonuniform List<{kind}> {len(value)}{s}"
|
56
|
-
else:
|
57
|
-
return f"uniform {value}"
|
58
|
-
|
59
|
-
|
60
|
-
def _serialize_dimensions(
|
61
|
-
value: FoamDictionaryBase._SetValue,
|
62
|
-
) -> str:
|
63
|
-
if is_sequence(value) and len(value) == 7:
|
64
|
-
return f"[{' '.join(str(v) for v in value)}]"
|
65
|
-
else:
|
66
|
-
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
67
|
-
|
68
|
-
|
69
|
-
def _serialize_dimensioned(
|
70
|
-
value: FoamDictionaryBase._SetValue,
|
71
|
-
) -> str:
|
72
|
-
if isinstance(value, FoamDictionaryBase.Dimensioned):
|
73
|
-
if value.name is not None:
|
74
|
-
return f"{value.name} {_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
|
75
|
-
else:
|
76
|
-
return f"{_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
|
77
|
-
else:
|
78
|
-
raise TypeError(f"Not a valid dimensioned value: {type(value)}")
|
79
|
-
|
80
|
-
|
81
|
-
def _serialize_value(
|
82
|
-
value: FoamDictionaryBase._SetValue,
|
12
|
+
def serialize(
|
13
|
+
data: FoamDict._SetData,
|
83
14
|
*,
|
84
15
|
assume_field: bool = False,
|
85
16
|
assume_dimensions: bool = False,
|
17
|
+
assume_data_entries: bool = False,
|
86
18
|
) -> str:
|
87
|
-
if isinstance(
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
return
|
103
|
-
|
104
|
-
|
105
|
-
|
19
|
+
if isinstance(data, Mapping):
|
20
|
+
entries = []
|
21
|
+
for k, v in data.items():
|
22
|
+
s = serialize(
|
23
|
+
v,
|
24
|
+
assume_field=assume_field,
|
25
|
+
assume_dimensions=assume_dimensions,
|
26
|
+
assume_data_entries=True,
|
27
|
+
)
|
28
|
+
if isinstance(v, Mapping):
|
29
|
+
entries.append(f"{k}\n{{\n{s}\n}}")
|
30
|
+
elif s:
|
31
|
+
entries.append(f"{k} {s};")
|
32
|
+
else:
|
33
|
+
entries.append(f"{k};")
|
34
|
+
return "\n".join(entries)
|
35
|
+
|
36
|
+
elif isinstance(data, FoamDict.DimensionSet) or (
|
37
|
+
assume_dimensions and is_sequence(data) and len(data) == 7
|
38
|
+
):
|
39
|
+
return f"[{' '.join(str(v) for v in data)}]"
|
40
|
+
|
41
|
+
elif assume_field and isinstance(data, (int, float)):
|
42
|
+
return f"uniform {data}"
|
43
|
+
|
44
|
+
elif assume_field and is_sequence(data):
|
45
|
+
if isinstance(data[0], (int, float)) and len(data) in (3, 6, 9):
|
46
|
+
return f"uniform {serialize(data)}"
|
47
|
+
elif isinstance(data[0], (int, float)):
|
48
|
+
return f"nonuniform List<scalar> {len(data)}{serialize(data)}"
|
49
|
+
elif len(data[0]) == 3:
|
50
|
+
return f"nonuniform List<vector> {len(data)}{serialize(data)}"
|
51
|
+
elif len(data[0]) == 6:
|
52
|
+
return f"nonuniform List<symmTensor> {len(data)}{serialize(data)}"
|
53
|
+
elif len(data[0]) == 9:
|
54
|
+
return f"nonuniform List<tensor> {len(data)}{serialize(data)}"
|
55
|
+
else:
|
56
|
+
return serialize(
|
57
|
+
data,
|
58
|
+
assume_dimensions=assume_dimensions,
|
59
|
+
assume_data_entries=assume_data_entries,
|
60
|
+
)
|
61
|
+
|
62
|
+
elif assume_data_entries and isinstance(data, tuple):
|
63
|
+
return " ".join(
|
64
|
+
serialize(v, assume_field=assume_field, assume_dimensions=assume_dimensions)
|
65
|
+
for v in data
|
66
|
+
)
|
67
|
+
|
68
|
+
elif isinstance(data, FoamDict.Dimensioned):
|
69
|
+
if data.name is not None:
|
70
|
+
return f"{data.name} {serialize(data.dimensions, assume_dimensions=True)} {serialize(data.value)}"
|
71
|
+
else:
|
72
|
+
return f"{serialize(data.dimensions, assume_dimensions=True)} {serialize(data.value)}"
|
106
73
|
|
107
|
-
|
74
|
+
elif is_sequence(data):
|
75
|
+
return f"({' '.join(serialize(v) for v in data)})"
|
108
76
|
|
77
|
+
elif data is True:
|
78
|
+
return "yes"
|
79
|
+
elif data is False:
|
80
|
+
return "no"
|
109
81
|
|
110
|
-
def _serialize_dictionary(
|
111
|
-
value: FoamDictionaryBase._SetValue,
|
112
|
-
) -> str:
|
113
|
-
if isinstance(value, Mapping):
|
114
|
-
return "\n".join(serialize_entry(k, v) for k, v in value.items())
|
115
82
|
else:
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
def serialize_entry(
|
120
|
-
keyword: str,
|
121
|
-
value: FoamDictionaryBase._SetValue,
|
122
|
-
*,
|
123
|
-
assume_field: bool = False,
|
124
|
-
assume_dimensions: bool = False,
|
125
|
-
) -> str:
|
126
|
-
try:
|
127
|
-
return f"{keyword}\n{{\n{_serialize_dictionary(value)}\n}}"
|
128
|
-
except TypeError:
|
129
|
-
return f"{keyword} {_serialize_value(value, assume_field=assume_field, assume_dimensions=assume_dimensions)};"
|
83
|
+
return str(data)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
foamlib/__init__.py,sha256=tO70QZaLcvSKedqwp14wd35SdnVPg57RHiaPV_q33wI,381
|
2
|
+
foamlib/_cases.py,sha256=SkUthKb98zXPMYACaGyfGC0Fo1jzD6cC4qEGesHfNfc,20554
|
3
|
+
foamlib/_util.py,sha256=PBTpBwt_j1GXASncSDZUR8pH2u_h8UyJXm8GeFKebTY,2552
|
4
|
+
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
foamlib/_files/__init__.py,sha256=m4y_7wFV2Voly1aJrTYALjT3S1Aq7EbaijxdGFbJrmU,160
|
6
|
+
foamlib/_files/_base.py,sha256=YA5a-i5HZuA3JslCD6r-DwZzpSA8r42dqSXef286Ako,2050
|
7
|
+
foamlib/_files/_fields.py,sha256=jVHOHEV70i6QrWzqhF_W5CywUZyJxN5Eh5S1MrNn1G0,5632
|
8
|
+
foamlib/_files/_files.py,sha256=EyZnvP9okZmElzL9db4FkOD82qizeJrgb_ZGIM4xoXI,5987
|
9
|
+
foamlib/_files/_io.py,sha256=alxMyxQh0zb6BZYqomZwOo9dQujMpRuS5yfpIKBEnaM,1976
|
10
|
+
foamlib/_files/_parsing.py,sha256=blyt1kpYruoW5I6DMDg8jhg6f5oz7gzedmAImuJ5b4k,5966
|
11
|
+
foamlib/_files/_serialization.py,sha256=Pq45Y3_DmblAk7ccUHf6RvipC_GQqU3SV89NOtV9CEg,2695
|
12
|
+
foamlib-0.3.1.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
13
|
+
foamlib-0.3.1.dist-info/METADATA,sha256=blCma_pulIQtuhsmJZ36RCRZxhdEnQxB6u2wfK-AVZY,4650
|
14
|
+
foamlib-0.3.1.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
15
|
+
foamlib-0.3.1.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
16
|
+
foamlib-0.3.1.dist-info/RECORD,,
|
foamlib-0.2.10.dist-info/RECORD
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=gNl0z905tzKAvIMYD8-SNPEEsHJmL-vCFFpXm4LXHxI,338
|
2
|
-
foamlib/_cases.py,sha256=BTbYqTjYgqzu7y3cHuhngSHdWEwy-Zn7rN71DWsw3dE,21018
|
3
|
-
foamlib/_util.py,sha256=PBTpBwt_j1GXASncSDZUR8pH2u_h8UyJXm8GeFKebTY,2552
|
4
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
foamlib/_files/__init__.py,sha256=Gd3Zz5k4X8pGa5d_RjYfFyMrz663Y6tutMzvqbTKPR8,180
|
6
|
-
foamlib/_files/_base.py,sha256=0GkUmj268EmrGfB553JGLuNM6xq8DTIYK2NdN1rvFYU,1836
|
7
|
-
foamlib/_files/_fields.py,sha256=y_CpRp1aCFg2zguYbuzJA9RtyoRSu6bSUWauiR3vhxM,5106
|
8
|
-
foamlib/_files/_files.py,sha256=z9yHEqfUKCQkg0-RW21JUb3AWuznO3_8j1W2kR9B6qE,6097
|
9
|
-
foamlib/_files/_io.py,sha256=alxMyxQh0zb6BZYqomZwOo9dQujMpRuS5yfpIKBEnaM,1976
|
10
|
-
foamlib/_files/_parsing.py,sha256=v8RhsVUFOaw2v1EtLigDe-qNI993i_ZUAme6I2HGslg,6020
|
11
|
-
foamlib/_files/_serialization.py,sha256=tbg63f0Nro89iqN2fsEnRzVPoxOVxYDZA4H9YhUiGtw,3776
|
12
|
-
foamlib-0.2.10.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
13
|
-
foamlib-0.2.10.dist-info/METADATA,sha256=_EMLC2XN4idlySBRDtbs74k7syYO_anh855_vC3F9lo,4651
|
14
|
-
foamlib-0.2.10.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
15
|
-
foamlib-0.2.10.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
16
|
-
foamlib-0.2.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|