foamlib 0.1.15__tar.gz → 0.2.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {foamlib-0.1.15 → foamlib-0.2.1}/PKG-INFO +2 -2
- {foamlib-0.1.15 → foamlib-0.2.1}/README.md +1 -1
- foamlib-0.2.1/foamlib/__init__.py +12 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib/_cases.py +60 -59
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib/_dictionaries.py +306 -264
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib.egg-info/PKG-INFO +2 -2
- {foamlib-0.1.15 → foamlib-0.2.1}/pyproject.toml +0 -3
- {foamlib-0.1.15 → foamlib-0.2.1}/tests/test_dictionaries.py +52 -38
- foamlib-0.1.15/foamlib/__init__.py +0 -26
- {foamlib-0.1.15 → foamlib-0.2.1}/LICENSE.txt +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib/_subprocesses.py +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib/py.typed +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib.egg-info/SOURCES.txt +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib.egg-info/dependency_links.txt +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib.egg-info/requires.txt +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/foamlib.egg-info/top_level.txt +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/setup.cfg +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/tests/test_basic.py +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/tests/test_flange.py +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/tests/test_flange_async.py +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/tests/test_pitz.py +0 -0
- {foamlib-0.1.15 → foamlib-0.2.1}/tests/test_pitz_async.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.1
|
3
|
+
Version: 0.2.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
|
@@ -62,7 +62,7 @@ It offers the following classes (among a few others):
|
|
62
62
|
|
63
63
|
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
|
64
64
|
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
65
|
-
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s
|
65
|
+
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s.
|
66
66
|
|
67
67
|
## Get started
|
68
68
|
|
@@ -15,7 +15,7 @@ It offers the following classes (among a few others):
|
|
15
15
|
|
16
16
|
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
|
17
17
|
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
18
|
-
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s
|
18
|
+
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s.
|
19
19
|
|
20
20
|
## Get started
|
21
21
|
|
@@ -24,14 +24,65 @@ from ._subprocesses import run_process, run_process_async, CalledProcessError
|
|
24
24
|
from ._dictionaries import FoamFile, FoamFieldFile
|
25
25
|
|
26
26
|
|
27
|
-
class FoamCaseBase(Sequence["
|
27
|
+
class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
28
28
|
def __init__(self, path: Union[Path, str]):
|
29
29
|
self.path = Path(path).absolute()
|
30
30
|
if not self.path.is_dir():
|
31
31
|
raise NotADirectoryError(f"{self.path} is not a directory")
|
32
32
|
|
33
|
+
class TimeDirectory(Set[FoamFieldFile]):
|
34
|
+
"""
|
35
|
+
An OpenFOAM time directory in a case.
|
36
|
+
|
37
|
+
Use to access field files in the directory, e.g. `time["U"]`.
|
38
|
+
|
39
|
+
:param path: The path to the time directory.
|
40
|
+
"""
|
41
|
+
|
42
|
+
def __init__(self, path: Union[Path, str]):
|
43
|
+
self.path = Path(path).absolute()
|
44
|
+
if not self.path.is_dir():
|
45
|
+
raise NotADirectoryError(f"{self.path} is not a directory")
|
46
|
+
|
47
|
+
@property
|
48
|
+
def time(self) -> float:
|
49
|
+
"""
|
50
|
+
The time that corresponds to this directory.
|
51
|
+
"""
|
52
|
+
return float(self.path.name)
|
53
|
+
|
54
|
+
@property
|
55
|
+
def name(self) -> str:
|
56
|
+
"""
|
57
|
+
The name of this time directory.
|
58
|
+
"""
|
59
|
+
return self.path.name
|
60
|
+
|
61
|
+
def __getitem__(self, key: str) -> FoamFieldFile:
|
62
|
+
try:
|
63
|
+
return FoamFieldFile(self.path / key)
|
64
|
+
except FileNotFoundError as e:
|
65
|
+
raise KeyError(key) from e
|
66
|
+
|
67
|
+
def __iter__(self) -> Iterator[FoamFieldFile]:
|
68
|
+
for p in self.path.iterdir():
|
69
|
+
if p.is_file():
|
70
|
+
yield FoamFieldFile(p.name)
|
71
|
+
|
72
|
+
def __len__(self) -> int:
|
73
|
+
return len(list(iter(self)))
|
74
|
+
|
75
|
+
def __fspath__(self) -> str:
|
76
|
+
return str(self.path)
|
77
|
+
|
78
|
+
def __repr__(self) -> str:
|
79
|
+
return f"{type(self).__name__}({self.path})"
|
80
|
+
|
81
|
+
def __str__(self) -> str:
|
82
|
+
return str(self.path)
|
83
|
+
|
33
84
|
@property
|
34
|
-
def _times(self) -> Sequence["
|
85
|
+
def _times(self) -> Sequence["FoamCaseBase.TimeDirectory"]:
|
35
86
|
times = []
|
36
87
|
for p in self.path.iterdir():
|
37
88
|
if p.is_dir():
|
@@ -40,23 +91,25 @@ class FoamCaseBase(Sequence["FoamTimeDirectory"]):
|
|
40
91
|
except ValueError:
|
41
92
|
pass
|
42
93
|
else:
|
43
|
-
times.append(
|
94
|
+
times.append(FoamCaseBase.TimeDirectory(p))
|
44
95
|
|
45
96
|
times.sort(key=lambda t: t.time)
|
46
97
|
|
47
98
|
return times
|
48
99
|
|
49
100
|
@overload
|
50
|
-
def __getitem__(
|
101
|
+
def __getitem__(
|
102
|
+
self, index: Union[int, float, str]
|
103
|
+
) -> "FoamCaseBase.TimeDirectory": ...
|
51
104
|
|
52
105
|
@overload
|
53
|
-
def __getitem__(self, index: slice) -> Sequence["
|
106
|
+
def __getitem__(self, index: slice) -> Sequence["FoamCaseBase.TimeDirectory"]: ...
|
54
107
|
|
55
108
|
def __getitem__(
|
56
109
|
self, index: Union[int, slice, float, str]
|
57
|
-
) -> Union["
|
110
|
+
) -> Union["FoamCaseBase.TimeDirectory", Sequence["FoamCaseBase.TimeDirectory"]]:
|
58
111
|
if isinstance(index, str):
|
59
|
-
return
|
112
|
+
return FoamCaseBase.TimeDirectory(self.path / str(index))
|
60
113
|
elif isinstance(index, float):
|
61
114
|
for time in self._times:
|
62
115
|
if time.time == index:
|
@@ -598,55 +651,3 @@ class AsyncFoamCase(FoamCaseBase):
|
|
598
651
|
)
|
599
652
|
|
600
653
|
return AsyncFoamCase(dest)
|
601
|
-
|
602
|
-
|
603
|
-
class FoamTimeDirectory(Mapping[str, FoamFieldFile]):
|
604
|
-
"""
|
605
|
-
An OpenFOAM time directory in a case.
|
606
|
-
|
607
|
-
Use as a mapping to access field files in the directory, e.g. `time["U"]`.
|
608
|
-
|
609
|
-
:param path: The path to the time directory.
|
610
|
-
"""
|
611
|
-
|
612
|
-
def __init__(self, path: Union[Path, str]):
|
613
|
-
self.path = Path(path).absolute()
|
614
|
-
if not self.path.is_dir():
|
615
|
-
raise NotADirectoryError(f"{self.path} is not a directory")
|
616
|
-
|
617
|
-
@property
|
618
|
-
def time(self) -> float:
|
619
|
-
"""
|
620
|
-
The time that corresponds to this directory.
|
621
|
-
"""
|
622
|
-
return float(self.path.name)
|
623
|
-
|
624
|
-
@property
|
625
|
-
def name(self) -> str:
|
626
|
-
"""
|
627
|
-
The name of this time directory.
|
628
|
-
"""
|
629
|
-
return self.path.name
|
630
|
-
|
631
|
-
def __getitem__(self, key: str) -> FoamFieldFile:
|
632
|
-
try:
|
633
|
-
return FoamFieldFile(self.path / key)
|
634
|
-
except FileNotFoundError as e:
|
635
|
-
raise KeyError(key) from e
|
636
|
-
|
637
|
-
def __iter__(self) -> Iterator[str]:
|
638
|
-
for p in self.path.iterdir():
|
639
|
-
if p.is_file():
|
640
|
-
yield p.name
|
641
|
-
|
642
|
-
def __len__(self) -> int:
|
643
|
-
return len(list(iter(self)))
|
644
|
-
|
645
|
-
def __fspath__(self) -> str:
|
646
|
-
return str(self.path)
|
647
|
-
|
648
|
-
def __repr__(self) -> str:
|
649
|
-
return f"{type(self).__name__}({self.path})"
|
650
|
-
|
651
|
-
def __str__(self) -> str:
|
652
|
-
return str(self.path)
|
@@ -1,4 +1,7 @@
|
|
1
1
|
from pathlib import Path
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from collections import namedtuple
|
4
|
+
from contextlib import suppress
|
2
5
|
from typing import (
|
3
6
|
Any,
|
4
7
|
Union,
|
@@ -9,204 +12,38 @@ from typing import (
|
|
9
12
|
MutableMapping,
|
10
13
|
cast,
|
11
14
|
)
|
12
|
-
from collections import namedtuple
|
13
|
-
from dataclasses import dataclass
|
14
|
-
from contextlib import suppress
|
15
|
-
|
16
|
-
from ._subprocesses import run_process, CalledProcessError
|
17
|
-
|
18
|
-
try:
|
19
|
-
import numpy as np
|
20
|
-
from numpy.typing import NDArray
|
21
|
-
except ModuleNotFoundError:
|
22
|
-
numpy = False
|
23
|
-
else:
|
24
|
-
numpy = True
|
25
15
|
|
26
16
|
from pyparsing import (
|
17
|
+
Dict,
|
27
18
|
Forward,
|
28
19
|
Group,
|
29
20
|
Keyword,
|
21
|
+
LineEnd,
|
30
22
|
Literal,
|
23
|
+
Located,
|
31
24
|
Opt,
|
32
25
|
QuotedString,
|
33
26
|
Word,
|
27
|
+
c_style_comment,
|
34
28
|
common,
|
29
|
+
cpp_style_comment,
|
35
30
|
printables,
|
31
|
+
identchars,
|
32
|
+
identbodychars,
|
36
33
|
)
|
37
34
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
"moles",
|
46
|
-
"current",
|
47
|
-
"luminous_intensity",
|
48
|
-
],
|
49
|
-
defaults=(0, 0, 0, 0, 0, 0, 0),
|
50
|
-
)
|
51
|
-
|
52
|
-
|
53
|
-
@dataclass
|
54
|
-
class FoamDimensioned:
|
55
|
-
value: Union[int, float, Sequence[Union[int, float]]] = 0
|
56
|
-
dimensions: Union[FoamDimensionSet, Sequence[Union[int, float]]] = (
|
57
|
-
FoamDimensionSet()
|
58
|
-
)
|
59
|
-
name: Optional[str] = None
|
60
|
-
|
61
|
-
def __post_init__(self) -> None:
|
62
|
-
if not isinstance(self.dimensions, FoamDimensionSet):
|
63
|
-
self.dimensions = FoamDimensionSet(*self.dimensions)
|
64
|
-
|
65
|
-
|
66
|
-
FoamValue = Union[
|
67
|
-
str, int, float, bool, FoamDimensioned, FoamDimensionSet, Sequence["FoamValue"]
|
68
|
-
]
|
69
|
-
"""
|
70
|
-
A value that can be stored in an OpenFOAM dictionary.
|
71
|
-
"""
|
72
|
-
|
73
|
-
_YES = Keyword("yes").set_parse_action(lambda s, loc, tks: True)
|
74
|
-
_NO = Keyword("no").set_parse_action(lambda s, loc, tks: False)
|
75
|
-
_DIMENSIONS = (
|
76
|
-
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
77
|
-
).set_parse_action(lambda s, loc, tks: FoamDimensionSet(*tks))
|
78
|
-
_TOKEN = common.identifier | QuotedString('"', unquote_results=False)
|
79
|
-
_ITEM = Forward()
|
80
|
-
_LIST = Opt(
|
81
|
-
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
82
|
-
).suppress() + (
|
83
|
-
(
|
84
|
-
Opt(common.integer).suppress()
|
85
|
-
+ Literal("(").suppress()
|
86
|
-
+ Group(_ITEM[...])
|
87
|
-
+ Literal(")").suppress()
|
88
|
-
)
|
89
|
-
| (
|
90
|
-
common.integer + Literal("{").suppress() + _ITEM + Literal("}").suppress()
|
91
|
-
).set_parse_action(lambda s, loc, tks: [tks[1]] * tks[0])
|
92
|
-
)
|
93
|
-
_FIELD = (Keyword("uniform").suppress() + _ITEM) | (
|
94
|
-
Keyword("nonuniform").suppress() + _LIST
|
95
|
-
)
|
96
|
-
_DIMENSIONED = (Opt(common.identifier) + _DIMENSIONS + _ITEM).set_parse_action(
|
97
|
-
lambda s, loc, tks: FoamDimensioned(*reversed(tks.as_list()))
|
98
|
-
)
|
99
|
-
_ITEM <<= (
|
100
|
-
_FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _YES | _NO | _TOKEN
|
101
|
-
)
|
102
|
-
|
103
|
-
_TOKENS = (QuotedString('"', unquote_results=False) | Word(printables))[
|
104
|
-
1, ...
|
105
|
-
].set_parse_action(lambda s, loc, tks: " ".join(tks))
|
106
|
-
|
107
|
-
_VALUE = _ITEM ^ _TOKENS
|
108
|
-
|
109
|
-
|
110
|
-
def _parse(value: str) -> FoamValue:
|
111
|
-
return cast(FoamValue, _VALUE.parse_string(value, parse_all=True).as_list()[0])
|
112
|
-
|
113
|
-
|
114
|
-
def _serialize_bool(value: Any) -> str:
|
115
|
-
if value is True:
|
116
|
-
return "yes"
|
117
|
-
elif value is False:
|
118
|
-
return "no"
|
119
|
-
else:
|
120
|
-
raise TypeError(f"Not a bool: {type(value)}")
|
121
|
-
|
122
|
-
|
123
|
-
def _is_sequence(value: Any) -> bool:
|
124
|
-
return (
|
125
|
-
isinstance(value, Sequence)
|
126
|
-
and not isinstance(value, str)
|
127
|
-
or numpy
|
128
|
-
and isinstance(value, np.ndarray)
|
129
|
-
)
|
130
|
-
|
131
|
-
|
132
|
-
def _serialize_list(value: Any) -> str:
|
133
|
-
if _is_sequence(value):
|
134
|
-
return f"({' '.join(_serialize(v) for v in value)})"
|
135
|
-
else:
|
136
|
-
raise TypeError(f"Not a valid sequence: {type(value)}")
|
137
|
-
|
138
|
-
|
139
|
-
def _serialize_field(value: Any) -> str:
|
140
|
-
if _is_sequence(value):
|
141
|
-
try:
|
142
|
-
s = _serialize_list(value)
|
143
|
-
except TypeError:
|
144
|
-
raise TypeError(f"Not a valid field: {type(value)}") from None
|
145
|
-
else:
|
146
|
-
if len(value) < 10:
|
147
|
-
return f"uniform {s}"
|
148
|
-
else:
|
149
|
-
if isinstance(value[0], (int, float)):
|
150
|
-
kind = "scalar"
|
151
|
-
elif len(value[0]) == 3:
|
152
|
-
kind = "vector"
|
153
|
-
elif len(value[0]) == 6:
|
154
|
-
kind = "symmTensor"
|
155
|
-
elif len(value[0]) == 9:
|
156
|
-
kind = "tensor"
|
157
|
-
else:
|
158
|
-
raise TypeError(
|
159
|
-
f"Unsupported sequence length for field: {len(value[0])}"
|
160
|
-
)
|
161
|
-
return f"nonuniform List<{kind}> {len(value)}{s}"
|
162
|
-
else:
|
163
|
-
return f"uniform {value}"
|
164
|
-
|
165
|
-
|
166
|
-
def _serialize_dimensions(value: Any) -> str:
|
167
|
-
if _is_sequence(value) and len(value) == 7:
|
168
|
-
return f"[{' '.join(str(v) for v in value)}]"
|
169
|
-
else:
|
170
|
-
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
171
|
-
|
172
|
-
|
173
|
-
def _serialize_dimensioned(value: Any) -> str:
|
174
|
-
if isinstance(value, FoamDimensioned):
|
175
|
-
if value.name is not None:
|
176
|
-
return f"{value.name} {_serialize_dimensions(value.dimensions)} {_serialize(value.value)}"
|
177
|
-
else:
|
178
|
-
return (
|
179
|
-
f"{_serialize_dimensions(value.dimensions)} {_serialize(value.value)}"
|
180
|
-
)
|
181
|
-
else:
|
182
|
-
raise TypeError(f"Not a valid dimensioned value: {type(value)}")
|
183
|
-
|
184
|
-
|
185
|
-
def _serialize(
|
186
|
-
value: Any, *, assume_field: bool = False, assume_dimensions: bool = False
|
187
|
-
) -> str:
|
188
|
-
if isinstance(value, FoamDimensionSet) or assume_dimensions:
|
189
|
-
with suppress(TypeError):
|
190
|
-
return _serialize_dimensions(value)
|
191
|
-
|
192
|
-
if assume_field:
|
193
|
-
with suppress(TypeError):
|
194
|
-
return _serialize_field(value)
|
195
|
-
|
196
|
-
with suppress(TypeError):
|
197
|
-
return _serialize_dimensioned(value)
|
198
|
-
|
199
|
-
with suppress(TypeError):
|
200
|
-
return _serialize_list(value)
|
201
|
-
|
202
|
-
with suppress(TypeError):
|
203
|
-
return _serialize_bool(value)
|
35
|
+
try:
|
36
|
+
import numpy as np
|
37
|
+
from numpy.typing import NDArray
|
38
|
+
except ModuleNotFoundError:
|
39
|
+
numpy = False
|
40
|
+
else:
|
41
|
+
numpy = True
|
204
42
|
|
205
|
-
|
43
|
+
from ._subprocesses import run_process, CalledProcessError
|
206
44
|
|
207
45
|
|
208
|
-
class
|
209
|
-
Value = FoamValue # for backwards compatibility
|
46
|
+
class _FoamDictionary(MutableMapping[str, Union["FoamFile.Value", "_FoamDictionary"]]):
|
210
47
|
|
211
48
|
def __init__(self, _file: "FoamFile", _keywords: Sequence[str]) -> None:
|
212
49
|
self._file = _file
|
@@ -238,14 +75,18 @@ class FoamDictionary(MutableMapping[str, Union[FoamValue, "FoamDictionary"]]):
|
|
238
75
|
f"{e.cmd} failed with return code {e.returncode}\n{e.stderr.decode()}"
|
239
76
|
) from None
|
240
77
|
|
241
|
-
def __getitem__(self, key: str) -> Union[
|
242
|
-
|
78
|
+
def __getitem__(self, key: str) -> Union["FoamFile.Value", "_FoamDictionary"]:
|
79
|
+
contents = self._file.path.read_text()
|
80
|
+
value = _DICTIONARY.parse_string(contents, parse_all=True).as_dict()
|
81
|
+
|
82
|
+
for key in [*self._keywords, key]:
|
83
|
+
value = value[key]
|
243
84
|
|
244
|
-
if value
|
245
|
-
|
246
|
-
return FoamDictionary(self._file, [*self._keywords, key])
|
85
|
+
if isinstance(value, dict):
|
86
|
+
return _FoamDictionary(self._file, [*self._keywords, key])
|
247
87
|
else:
|
248
|
-
|
88
|
+
start, end = value
|
89
|
+
return _VALUE.parse_string(contents[start:end], parse_all=True).as_list()[0]
|
249
90
|
|
250
91
|
def _setitem(
|
251
92
|
self,
|
@@ -255,17 +96,18 @@ class FoamDictionary(MutableMapping[str, Union[FoamValue, "FoamDictionary"]]):
|
|
255
96
|
assume_field: bool = False,
|
256
97
|
assume_dimensions: bool = False,
|
257
98
|
) -> None:
|
258
|
-
if isinstance(value,
|
99
|
+
if isinstance(value, _FoamDictionary):
|
259
100
|
value = value._cmd(["-value"])
|
260
101
|
elif isinstance(value, Mapping):
|
261
102
|
self._cmd(["-set", "{}"], key=key)
|
262
103
|
subdict = self[key]
|
263
|
-
|
104
|
+
print(subdict)
|
105
|
+
assert isinstance(subdict, _FoamDictionary)
|
264
106
|
for k, v in value.items():
|
265
107
|
subdict[k] = v
|
266
108
|
return
|
267
109
|
else:
|
268
|
-
value =
|
110
|
+
value = serialize(
|
269
111
|
value, assume_field=assume_field, assume_dimensions=assume_dimensions
|
270
112
|
)
|
271
113
|
|
@@ -286,82 +128,53 @@ class FoamDictionary(MutableMapping[str, Union[FoamValue, "FoamDictionary"]]):
|
|
286
128
|
self._cmd(["-remove"], key=key)
|
287
129
|
|
288
130
|
def __iter__(self) -> Iterator[str]:
|
289
|
-
|
131
|
+
value = _DICTIONARY.parse_file(self._file.path, parse_all=True).as_dict()
|
132
|
+
|
133
|
+
for key in self._keywords:
|
134
|
+
value = value[key]
|
135
|
+
|
136
|
+
yield from value
|
290
137
|
|
291
138
|
def __len__(self) -> int:
|
292
139
|
return len(list(iter(self)))
|
293
140
|
|
294
141
|
def __repr__(self) -> str:
|
295
|
-
return
|
296
|
-
|
142
|
+
return "FoamFile.Dictionary"
|
297
143
|
|
298
|
-
class FoamBoundaryDictionary(FoamDictionary):
|
299
|
-
"""An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
|
300
144
|
|
301
|
-
|
302
|
-
|
303
|
-
self._setitem(key, value, assume_field=True)
|
304
|
-
else:
|
305
|
-
self._setitem(key, value)
|
306
|
-
|
307
|
-
@property
|
308
|
-
def type(self) -> str:
|
309
|
-
"""
|
310
|
-
Alias of `self["type"]`.
|
311
|
-
"""
|
312
|
-
ret = self["type"]
|
313
|
-
if not isinstance(ret, str):
|
314
|
-
raise TypeError("type is not a string")
|
315
|
-
return ret
|
316
|
-
|
317
|
-
@type.setter
|
318
|
-
def type(self, value: str) -> None:
|
319
|
-
self["type"] = value
|
320
|
-
|
321
|
-
@property
|
322
|
-
def value(
|
323
|
-
self,
|
324
|
-
) -> Union[
|
325
|
-
int,
|
326
|
-
float,
|
327
|
-
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
328
|
-
"NDArray[np.generic]",
|
329
|
-
]:
|
330
|
-
"""
|
331
|
-
Alias of `self["value"]`.
|
332
|
-
"""
|
333
|
-
ret = self["value"]
|
334
|
-
if not isinstance(ret, (int, float, Sequence)):
|
335
|
-
raise TypeError("value is not a field")
|
336
|
-
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
145
|
+
class FoamFile(_FoamDictionary):
|
146
|
+
"""An OpenFOAM dictionary file as a mutable mapping."""
|
337
147
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
"
|
148
|
+
Dictionary = _FoamDictionary
|
149
|
+
|
150
|
+
DimensionSet = namedtuple(
|
151
|
+
"DimensionSet",
|
152
|
+
[
|
153
|
+
"mass",
|
154
|
+
"length",
|
155
|
+
"time",
|
156
|
+
"temperature",
|
157
|
+
"moles",
|
158
|
+
"current",
|
159
|
+
"luminous_intensity",
|
346
160
|
],
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
@value.deleter
|
351
|
-
def value(self) -> None:
|
352
|
-
del self["value"]
|
353
|
-
|
161
|
+
defaults=(0, 0, 0, 0, 0, 0, 0),
|
162
|
+
)
|
354
163
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
return ret
|
164
|
+
@dataclass
|
165
|
+
class Dimensioned:
|
166
|
+
value: Union[int, float, Sequence[Union[int, float]]] = 0
|
167
|
+
dimensions: Union["FoamFile.DimensionSet", Sequence[Union[int, float]]] = ()
|
168
|
+
name: Optional[str] = None
|
361
169
|
|
170
|
+
def __post_init__(self) -> None:
|
171
|
+
if not isinstance(self.dimensions, FoamFile.DimensionSet):
|
172
|
+
self.dimensions = FoamFile.DimensionSet(*self.dimensions)
|
362
173
|
|
363
|
-
|
364
|
-
"""
|
174
|
+
Value = Union[str, int, float, bool, Dimensioned, DimensionSet, Sequence["Value"]]
|
175
|
+
"""
|
176
|
+
A value that can be stored in an OpenFOAM dictionary.
|
177
|
+
"""
|
365
178
|
|
366
179
|
def __init__(self, path: Union[str, Path]) -> None:
|
367
180
|
super().__init__(self, [])
|
@@ -381,10 +194,83 @@ class FoamFile(FoamDictionary):
|
|
381
194
|
class FoamFieldFile(FoamFile):
|
382
195
|
"""An OpenFOAM dictionary file representing a field as a mutable mapping."""
|
383
196
|
|
384
|
-
|
197
|
+
class BoundariesDictionary(_FoamDictionary):
|
198
|
+
def __getitem__(
|
199
|
+
self, key: str
|
200
|
+
) -> Union["FoamFile.Value", "FoamFieldFile.BoundaryDictionary"]:
|
201
|
+
ret = super().__getitem__(key)
|
202
|
+
if isinstance(ret, _FoamDictionary):
|
203
|
+
ret = FoamFieldFile.BoundaryDictionary(
|
204
|
+
self._file, [*self._keywords, key]
|
205
|
+
)
|
206
|
+
return ret
|
207
|
+
|
208
|
+
def __repr__(self) -> str:
|
209
|
+
return "FoamFieldFile.BoundariesDictionary"
|
210
|
+
|
211
|
+
class BoundaryDictionary(_FoamDictionary):
|
212
|
+
"""An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
|
213
|
+
|
214
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
215
|
+
if key == "value":
|
216
|
+
self._setitem(key, value, assume_field=True)
|
217
|
+
else:
|
218
|
+
self._setitem(key, value)
|
219
|
+
|
220
|
+
@property
|
221
|
+
def type(self) -> str:
|
222
|
+
"""
|
223
|
+
Alias of `self["type"]`.
|
224
|
+
"""
|
225
|
+
ret = self["type"]
|
226
|
+
if not isinstance(ret, str):
|
227
|
+
raise TypeError("type is not a string")
|
228
|
+
return ret
|
229
|
+
|
230
|
+
@type.setter
|
231
|
+
def type(self, value: str) -> None:
|
232
|
+
self["type"] = value
|
233
|
+
|
234
|
+
@property
|
235
|
+
def value(
|
236
|
+
self,
|
237
|
+
) -> Union[
|
238
|
+
int,
|
239
|
+
float,
|
240
|
+
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
241
|
+
"NDArray[np.generic]",
|
242
|
+
]:
|
243
|
+
"""
|
244
|
+
Alias of `self["value"]`.
|
245
|
+
"""
|
246
|
+
ret = self["value"]
|
247
|
+
if not isinstance(ret, (int, float, Sequence)):
|
248
|
+
raise TypeError("value is not a field")
|
249
|
+
return cast(Union[int, float, Sequence[Union[int, float]]], ret)
|
250
|
+
|
251
|
+
@value.setter
|
252
|
+
def value(
|
253
|
+
self,
|
254
|
+
value: Union[
|
255
|
+
int,
|
256
|
+
float,
|
257
|
+
Sequence[Union[int, float, Sequence[Union[int, float]]]],
|
258
|
+
"NDArray[np.generic]",
|
259
|
+
],
|
260
|
+
) -> None:
|
261
|
+
self["value"] = value
|
262
|
+
|
263
|
+
@value.deleter
|
264
|
+
def value(self) -> None:
|
265
|
+
del self["value"]
|
266
|
+
|
267
|
+
def __repr__(self) -> str:
|
268
|
+
return "FoamFieldFile.BoundaryDictionary"
|
269
|
+
|
270
|
+
def __getitem__(self, key: str) -> Union[FoamFile.Value, _FoamDictionary]:
|
385
271
|
ret = super().__getitem__(key)
|
386
|
-
if key == "boundaryField" and isinstance(ret,
|
387
|
-
ret =
|
272
|
+
if key == "boundaryField" and isinstance(ret, _FoamDictionary):
|
273
|
+
ret = FoamFieldFile.BoundariesDictionary(self, [key])
|
388
274
|
return ret
|
389
275
|
|
390
276
|
def __setitem__(self, key: str, value: Any) -> None:
|
@@ -396,18 +282,18 @@ class FoamFieldFile(FoamFile):
|
|
396
282
|
self._setitem(key, value)
|
397
283
|
|
398
284
|
@property
|
399
|
-
def dimensions(self) ->
|
285
|
+
def dimensions(self) -> FoamFile.DimensionSet:
|
400
286
|
"""
|
401
287
|
Alias of `self["dimensions"]`.
|
402
288
|
"""
|
403
289
|
ret = self["dimensions"]
|
404
|
-
if not isinstance(ret,
|
290
|
+
if not isinstance(ret, FoamFile.DimensionSet):
|
405
291
|
raise TypeError("dimensions is not a DimensionSet")
|
406
292
|
return ret
|
407
293
|
|
408
294
|
@dimensions.setter
|
409
295
|
def dimensions(
|
410
|
-
self, value: Union[
|
296
|
+
self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
|
411
297
|
) -> None:
|
412
298
|
self["dimensions"] = value
|
413
299
|
|
@@ -441,12 +327,168 @@ class FoamFieldFile(FoamFile):
|
|
441
327
|
self["internalField"] = value
|
442
328
|
|
443
329
|
@property
|
444
|
-
def boundary_field(self) ->
|
330
|
+
def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
|
445
331
|
"""
|
446
332
|
Alias of `self["boundaryField"]`.
|
447
333
|
"""
|
448
334
|
ret = self["boundaryField"]
|
449
|
-
if not isinstance(ret,
|
450
|
-
assert not isinstance(ret,
|
335
|
+
if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
|
336
|
+
assert not isinstance(ret, _FoamDictionary)
|
451
337
|
raise TypeError("boundaryField is not a dictionary")
|
452
338
|
return ret
|
339
|
+
|
340
|
+
|
341
|
+
_YES = Keyword("yes").set_parse_action(lambda: True)
|
342
|
+
_NO = Keyword("no").set_parse_action(lambda: False)
|
343
|
+
_DIMENSIONS = (
|
344
|
+
Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
|
345
|
+
).set_parse_action(lambda tks: FoamFile.DimensionSet(*tks))
|
346
|
+
_TOKEN = QuotedString('"', unquote_results=False) | Word(
|
347
|
+
identchars + "$", identbodychars
|
348
|
+
)
|
349
|
+
_ITEM = Forward()
|
350
|
+
_LIST = Opt(
|
351
|
+
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
352
|
+
).suppress() + (
|
353
|
+
(
|
354
|
+
Opt(common.integer).suppress()
|
355
|
+
+ Literal("(").suppress()
|
356
|
+
+ Group(_ITEM[...])
|
357
|
+
+ Literal(")").suppress()
|
358
|
+
)
|
359
|
+
| (
|
360
|
+
common.integer + Literal("{").suppress() + _ITEM + Literal("}").suppress()
|
361
|
+
).set_parse_action(lambda tks: [tks[1]] * tks[0])
|
362
|
+
)
|
363
|
+
_FIELD = (Keyword("uniform").suppress() + _ITEM) | (
|
364
|
+
Keyword("nonuniform").suppress() + _LIST
|
365
|
+
)
|
366
|
+
_DIMENSIONED = (Opt(common.identifier) + _DIMENSIONS + _ITEM).set_parse_action(
|
367
|
+
lambda tks: FoamFile.Dimensioned(*reversed(tks.as_list()))
|
368
|
+
)
|
369
|
+
_ITEM <<= (
|
370
|
+
_FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _YES | _NO | _TOKEN
|
371
|
+
)
|
372
|
+
|
373
|
+
_TOKENS = (
|
374
|
+
QuotedString('"', unquote_results=False)
|
375
|
+
| Word(printables.replace(";", "").replace("{", "").replace("}", ""))
|
376
|
+
)[2, ...].set_parse_action(lambda tks: " ".join(tks))
|
377
|
+
|
378
|
+
_VALUE = (_ITEM ^ _TOKENS).ignore(c_style_comment).ignore(cpp_style_comment)
|
379
|
+
|
380
|
+
|
381
|
+
_UNPARSED_VALUE = (
|
382
|
+
QuotedString('"', unquote_results=False)
|
383
|
+
| Word(printables.replace(";", "").replace("{", "").replace("}", ""))
|
384
|
+
)[...]
|
385
|
+
_KEYWORD = QuotedString('"', unquote_results=False) | Word(
|
386
|
+
identchars + "$(,.)", identbodychars + "$(,.)"
|
387
|
+
)
|
388
|
+
_DICTIONARY = Forward()
|
389
|
+
_ENTRY = _KEYWORD + (
|
390
|
+
(
|
391
|
+
Located(_UNPARSED_VALUE).set_parse_action(lambda tks: (tks[0], tks[2]))
|
392
|
+
+ Literal(";").suppress()
|
393
|
+
)
|
394
|
+
| (Literal("{").suppress() + _DICTIONARY + Literal("}").suppress())
|
395
|
+
)
|
396
|
+
_DICTIONARY <<= (
|
397
|
+
Dict(Group(_ENTRY)[...])
|
398
|
+
.set_parse_action(lambda tks: {} if not tks else tks)
|
399
|
+
.ignore(c_style_comment)
|
400
|
+
.ignore(cpp_style_comment)
|
401
|
+
.ignore(Literal("#include") + ... + LineEnd()) # type: ignore
|
402
|
+
)
|
403
|
+
|
404
|
+
|
405
|
+
def _serialize_bool(value: Any) -> str:
|
406
|
+
if value is True:
|
407
|
+
return "yes"
|
408
|
+
elif value is False:
|
409
|
+
return "no"
|
410
|
+
else:
|
411
|
+
raise TypeError(f"Not a bool: {type(value)}")
|
412
|
+
|
413
|
+
|
414
|
+
def _is_sequence(value: Any) -> bool:
|
415
|
+
return (
|
416
|
+
isinstance(value, Sequence)
|
417
|
+
and not isinstance(value, str)
|
418
|
+
or numpy
|
419
|
+
and isinstance(value, np.ndarray)
|
420
|
+
)
|
421
|
+
|
422
|
+
|
423
|
+
def _serialize_list(value: Any) -> str:
|
424
|
+
if _is_sequence(value):
|
425
|
+
return f"({' '.join(serialize(v) for v in value)})"
|
426
|
+
else:
|
427
|
+
raise TypeError(f"Not a valid sequence: {type(value)}")
|
428
|
+
|
429
|
+
|
430
|
+
def _serialize_field(value: Any) -> str:
|
431
|
+
if _is_sequence(value):
|
432
|
+
try:
|
433
|
+
s = _serialize_list(value)
|
434
|
+
except TypeError:
|
435
|
+
raise TypeError(f"Not a valid field: {type(value)}") from None
|
436
|
+
else:
|
437
|
+
if len(value) < 10:
|
438
|
+
return f"uniform {s}"
|
439
|
+
else:
|
440
|
+
if isinstance(value[0], (int, float)):
|
441
|
+
kind = "scalar"
|
442
|
+
elif len(value[0]) == 3:
|
443
|
+
kind = "vector"
|
444
|
+
elif len(value[0]) == 6:
|
445
|
+
kind = "symmTensor"
|
446
|
+
elif len(value[0]) == 9:
|
447
|
+
kind = "tensor"
|
448
|
+
else:
|
449
|
+
raise TypeError(
|
450
|
+
f"Unsupported sequence length for field: {len(value[0])}"
|
451
|
+
)
|
452
|
+
return f"nonuniform List<{kind}> {len(value)}{s}"
|
453
|
+
else:
|
454
|
+
return f"uniform {value}"
|
455
|
+
|
456
|
+
|
457
|
+
def _serialize_dimensions(value: Any) -> str:
|
458
|
+
if _is_sequence(value) and len(value) == 7:
|
459
|
+
return f"[{' '.join(str(v) for v in value)}]"
|
460
|
+
else:
|
461
|
+
raise TypeError(f"Not a valid dimension set: {type(value)}")
|
462
|
+
|
463
|
+
|
464
|
+
def _serialize_dimensioned(value: Any) -> str:
|
465
|
+
if isinstance(value, FoamFile.Dimensioned):
|
466
|
+
if value.name is not None:
|
467
|
+
return f"{value.name} {_serialize_dimensions(value.dimensions)} {serialize(value.value)}"
|
468
|
+
else:
|
469
|
+
return f"{_serialize_dimensions(value.dimensions)} {serialize(value.value)}"
|
470
|
+
else:
|
471
|
+
raise TypeError(f"Not a valid dimensioned value: {type(value)}")
|
472
|
+
|
473
|
+
|
474
|
+
def serialize(
|
475
|
+
value: Any, *, assume_field: bool = False, assume_dimensions: bool = False
|
476
|
+
) -> str:
|
477
|
+
if isinstance(value, FoamFile.DimensionSet) or assume_dimensions:
|
478
|
+
with suppress(TypeError):
|
479
|
+
return _serialize_dimensions(value)
|
480
|
+
|
481
|
+
if assume_field:
|
482
|
+
with suppress(TypeError):
|
483
|
+
return _serialize_field(value)
|
484
|
+
|
485
|
+
with suppress(TypeError):
|
486
|
+
return _serialize_dimensioned(value)
|
487
|
+
|
488
|
+
with suppress(TypeError):
|
489
|
+
return _serialize_list(value)
|
490
|
+
|
491
|
+
with suppress(TypeError):
|
492
|
+
return _serialize_bool(value)
|
493
|
+
|
494
|
+
return str(value)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.1
|
3
|
+
Version: 0.2.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
|
@@ -62,7 +62,7 @@ It offers the following classes (among a few others):
|
|
62
62
|
|
63
63
|
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
|
64
64
|
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
65
|
-
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s
|
65
|
+
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s.
|
66
66
|
|
67
67
|
## Get started
|
68
68
|
|
@@ -61,9 +61,6 @@ Homepage = "https://github.com/gerlero/foamlib"
|
|
61
61
|
Repository = "https://github.com/gerlero/foamlib"
|
62
62
|
Documentation = "https://foamlib.readthedocs.io"
|
63
63
|
|
64
|
-
[tool.setuptools]
|
65
|
-
packages = ["foamlib"]
|
66
|
-
|
67
64
|
[tool.setuptools.dynamic]
|
68
65
|
version = {attr = "foamlib.__version__"}
|
69
66
|
|
@@ -7,40 +7,54 @@ from typing import Sequence
|
|
7
7
|
import numpy as np
|
8
8
|
|
9
9
|
from foamlib import *
|
10
|
-
from foamlib._dictionaries import
|
11
|
-
|
12
|
-
|
13
|
-
def
|
14
|
-
assert
|
15
|
-
assert
|
16
|
-
assert
|
17
|
-
assert
|
18
|
-
assert
|
19
|
-
assert
|
20
|
-
assert
|
21
|
-
assert
|
22
|
-
assert
|
23
|
-
assert
|
24
|
-
assert
|
25
|
-
assert
|
26
|
-
assert
|
27
|
-
assert
|
28
|
-
assert
|
29
|
-
assert
|
10
|
+
from foamlib._dictionaries import _VALUE
|
11
|
+
|
12
|
+
|
13
|
+
def test_parse_value() -> None:
|
14
|
+
assert _VALUE.parse_string("1").as_list()[0] == 1
|
15
|
+
assert _VALUE.parse_string("1.0").as_list()[0] == 1.0
|
16
|
+
assert _VALUE.parse_string("1.0e-3").as_list()[0] == 1.0e-3
|
17
|
+
assert _VALUE.parse_string("yes").as_list()[0] is True
|
18
|
+
assert _VALUE.parse_string("no").as_list()[0] is False
|
19
|
+
assert _VALUE.parse_string("word").as_list()[0] == "word"
|
20
|
+
assert _VALUE.parse_string("word word").as_list()[0] == "word word"
|
21
|
+
assert _VALUE.parse_string('"a string"').as_list()[0] == '"a string"'
|
22
|
+
assert _VALUE.parse_string("uniform 1").as_list()[0] == 1
|
23
|
+
assert _VALUE.parse_string("uniform 1.0").as_list()[0] == 1.0
|
24
|
+
assert _VALUE.parse_string("uniform 1.0e-3").as_list()[0] == 1.0e-3
|
25
|
+
assert _VALUE.parse_string("(1.0 2.0 3.0)").as_list()[0] == [1.0, 2.0, 3.0]
|
26
|
+
assert _VALUE.parse_string("uniform (1 2 3)").as_list()[0] == [1, 2, 3]
|
27
|
+
assert _VALUE.parse_string("nonuniform List<scalar> 2(1 2)").as_list()[0] == [1, 2]
|
28
|
+
assert _VALUE.parse_string("3(1 2 3)").as_list()[0] == [1, 2, 3]
|
29
|
+
assert _VALUE.parse_string("2((1 2 3) (4 5 6))").as_list()[0] == [
|
30
30
|
[1, 2, 3],
|
31
31
|
[4, 5, 6],
|
32
32
|
]
|
33
|
-
assert
|
34
|
-
|
33
|
+
assert _VALUE.parse_string("nonuniform List<vector> 2((1 2 3) (4 5 6))").as_list()[
|
34
|
+
0
|
35
|
+
] == [
|
36
|
+
[1, 2, 3],
|
37
|
+
[4, 5, 6],
|
38
|
+
]
|
39
|
+
assert _VALUE.parse_string("[1 1 -2 0 0 0 0]").as_list()[
|
40
|
+
0
|
41
|
+
] == FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
42
|
+
assert _VALUE.parse_string("g [1 1 -2 0 0 0 0] (0 0 -9.81)").as_list()[
|
43
|
+
0
|
44
|
+
] == FoamFile.Dimensioned(
|
35
45
|
name="g",
|
36
|
-
dimensions=
|
46
|
+
dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2),
|
37
47
|
value=[0, 0, -9.81],
|
38
48
|
)
|
39
|
-
assert
|
40
|
-
|
49
|
+
assert _VALUE.parse_string("[1 1 -2 0 0 0 0] 9.81").as_list()[
|
50
|
+
0
|
51
|
+
] == FoamFile.Dimensioned(
|
52
|
+
dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2), value=9.81
|
41
53
|
)
|
42
54
|
assert (
|
43
|
-
|
55
|
+
_VALUE.parse_string(
|
56
|
+
"hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)"
|
57
|
+
).as_list()[0]
|
44
58
|
== "hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)"
|
45
59
|
)
|
46
60
|
|
@@ -70,21 +84,21 @@ def test_write_read(tmp_path: Path) -> None:
|
|
70
84
|
|
71
85
|
d["subdict"] = {"key": "value"}
|
72
86
|
sd = d["subdict"]
|
73
|
-
assert isinstance(sd,
|
87
|
+
assert isinstance(sd, FoamFile.Dictionary)
|
74
88
|
assert sd["key"] == "value"
|
75
89
|
assert len(sd) == 1
|
76
90
|
assert list(sd) == ["key"]
|
77
91
|
|
78
92
|
d["subdict2"] = d["subdict"]
|
79
93
|
sd2 = d["subdict2"]
|
80
|
-
assert isinstance(sd2,
|
94
|
+
assert isinstance(sd2, FoamFile.Dictionary)
|
81
95
|
assert sd2["key"] == "value"
|
82
96
|
assert len(sd) == 1
|
83
97
|
assert list(sd) == ["key"]
|
84
98
|
|
85
99
|
sd["subsubdict"] = d["subdict"]
|
86
100
|
ssd = sd["subsubdict"]
|
87
|
-
assert isinstance(ssd,
|
101
|
+
assert isinstance(ssd, FoamFile.Dictionary)
|
88
102
|
assert ssd["key"] == "value"
|
89
103
|
|
90
104
|
sd["list"] = [1, 2, 3]
|
@@ -93,12 +107,12 @@ def test_write_read(tmp_path: Path) -> None:
|
|
93
107
|
sd["nestedList"] = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
94
108
|
assert sd["nestedList"] == [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
|
95
109
|
|
96
|
-
sd["g"] =
|
110
|
+
sd["g"] = FoamFile.Dimensioned(
|
97
111
|
name="g", dimensions=[1, 1, -2, 0, 0, 0, 0], value=[0, 0, -9.81]
|
98
112
|
)
|
99
|
-
assert sd["g"] ==
|
113
|
+
assert sd["g"] == FoamFile.Dimensioned(
|
100
114
|
name="g",
|
101
|
-
dimensions=
|
115
|
+
dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2),
|
102
116
|
value=[0, 0, -9.81],
|
103
117
|
)
|
104
118
|
|
@@ -114,17 +128,17 @@ def pitz(tmp_path: Path) -> FoamCase:
|
|
114
128
|
|
115
129
|
|
116
130
|
def test_dimensions(pitz: FoamCase) -> None:
|
117
|
-
assert pitz[0]["p"].dimensions ==
|
118
|
-
assert pitz[0]["U"].dimensions ==
|
131
|
+
assert pitz[0]["p"].dimensions == FoamFile.DimensionSet(length=2, time=-2)
|
132
|
+
assert pitz[0]["U"].dimensions == FoamFile.DimensionSet(length=1, time=-1)
|
119
133
|
|
120
|
-
pitz[0]["p"].dimensions =
|
134
|
+
pitz[0]["p"].dimensions = FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
121
135
|
|
122
|
-
assert pitz[0]["p"].dimensions ==
|
136
|
+
assert pitz[0]["p"].dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
|
123
137
|
|
124
138
|
|
125
139
|
def test_boundary_field(pitz: FoamCase) -> None:
|
126
140
|
outlet = pitz[0]["p"].boundary_field["outlet"]
|
127
|
-
assert isinstance(outlet,
|
141
|
+
assert isinstance(outlet, FoamFieldFile.BoundaryDictionary)
|
128
142
|
assert outlet.type == "fixedValue"
|
129
143
|
assert outlet.value == 0
|
130
144
|
|
@@ -182,7 +196,7 @@ def test_internal_field(pitz: FoamCase) -> None:
|
|
182
196
|
|
183
197
|
def test_fv_schemes(pitz: FoamCase) -> None:
|
184
198
|
div_schemes = pitz.fv_schemes["divSchemes"]
|
185
|
-
assert isinstance(div_schemes,
|
199
|
+
assert isinstance(div_schemes, FoamFile.Dictionary)
|
186
200
|
scheme = div_schemes["div(phi,U)"]
|
187
201
|
assert isinstance(scheme, str)
|
188
202
|
assert scheme == "bounded Gauss linearUpwind grad(U)"
|
@@ -1,26 +0,0 @@
|
|
1
|
-
__version__ = "0.1.15"
|
2
|
-
|
3
|
-
from ._cases import FoamCase, AsyncFoamCase, FoamTimeDirectory, FoamCaseBase
|
4
|
-
from ._dictionaries import (
|
5
|
-
FoamFile,
|
6
|
-
FoamFieldFile,
|
7
|
-
FoamDictionary,
|
8
|
-
FoamBoundariesDictionary,
|
9
|
-
FoamBoundaryDictionary,
|
10
|
-
FoamDimensioned,
|
11
|
-
FoamDimensionSet,
|
12
|
-
)
|
13
|
-
|
14
|
-
__all__ = [
|
15
|
-
"FoamCase",
|
16
|
-
"AsyncFoamCase",
|
17
|
-
"FoamTimeDirectory",
|
18
|
-
"FoamCaseBase",
|
19
|
-
"FoamFile",
|
20
|
-
"FoamFieldFile",
|
21
|
-
"FoamDictionary",
|
22
|
-
"FoamBoundariesDictionary",
|
23
|
-
"FoamBoundaryDictionary",
|
24
|
-
"FoamDimensioned",
|
25
|
-
"FoamDimensionSet",
|
26
|
-
]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|