foamlib 0.8.0__tar.gz → 0.8.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.8.0 → foamlib-0.8.1}/PKG-INFO +3 -12
- foamlib-0.8.1/benchmark.png +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/__init__.py +1 -1
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_files/_files.py +28 -42
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_files/_parsing.py +59 -67
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_files/_serialization.py +51 -75
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_files/_types.py +37 -6
- {foamlib-0.8.0 → foamlib-0.8.1}/pyproject.toml +2 -7
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_cases/test_cavity.py +3 -3
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_cases/test_cavity_async.py +3 -2
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_files/test_dumps.py +11 -12
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_files/test_files.py +21 -29
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_files/test_parsing.py +39 -19
- foamlib-0.8.0/benchmark.png +0 -0
- foamlib-0.8.0/foamlib/_files/_util.py +0 -23
- {foamlib-0.8.0 → foamlib-0.8.1}/.devcontainer.json +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.dockerignore +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.git-blame-ignore-revs +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.github/dependabot.yml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.github/workflows/ci.yml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.github/workflows/docker.yml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.github/workflows/dockerhub-description.yml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.github/workflows/pypi-publish.yml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.gitignore +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/.readthedocs.yaml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/Dockerfile +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/LICENSE.txt +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/README.md +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/Makefile +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/cases.rst +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/conf.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/files.rst +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/index.rst +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/make.bat +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/docs/ruff.toml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/__init__.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_async.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_base.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_run.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_slurm.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_subprocess.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_sync.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_cases/_util.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_files/__init__.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/_files/_io.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/foamlib/py.typed +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/logo.png +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/__init__.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/ruff.toml +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_cases/__init__.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_cases/test_flange.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_cases/test_flange_async.py +0 -0
- {foamlib-0.8.0 → foamlib-0.8.1}/tests/test_files/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.1
|
4
4
|
Summary: A Python interface for interacting with OpenFOAM
|
5
5
|
Project-URL: Homepage, https://github.com/gerlero/foamlib
|
6
6
|
Project-URL: Repository, https://github.com/gerlero/foamlib
|
@@ -25,12 +25,12 @@ Classifier: Topic :: Software Development
|
|
25
25
|
Classifier: Typing :: Typed
|
26
26
|
Requires-Python: >=3.7
|
27
27
|
Requires-Dist: aioshutil<2,>=1
|
28
|
+
Requires-Dist: numpy<3,>=1
|
29
|
+
Requires-Dist: numpy<3,>=1.25.0; python_version >= '3.10'
|
28
30
|
Requires-Dist: pyparsing<4,>=3
|
29
31
|
Requires-Dist: typing-extensions<5,>=4; python_version < '3.11'
|
30
32
|
Provides-Extra: dev
|
31
33
|
Requires-Dist: mypy<2,>=1; extra == 'dev'
|
32
|
-
Requires-Dist: numpy<3,>=1; extra == 'dev'
|
33
|
-
Requires-Dist: numpy<3,>=1.25.0; (python_version >= '3.10') and extra == 'dev'
|
34
34
|
Requires-Dist: pytest-asyncio<0.25,>=0.21; extra == 'dev'
|
35
35
|
Requires-Dist: pytest-cov; extra == 'dev'
|
36
36
|
Requires-Dist: pytest<9,>=7; extra == 'dev'
|
@@ -38,25 +38,16 @@ Requires-Dist: ruff; extra == 'dev'
|
|
38
38
|
Requires-Dist: sphinx-rtd-theme; extra == 'dev'
|
39
39
|
Requires-Dist: sphinx<9,>=5; extra == 'dev'
|
40
40
|
Provides-Extra: docs
|
41
|
-
Requires-Dist: numpy<3,>=1; extra == 'docs'
|
42
|
-
Requires-Dist: numpy<3,>=1.25.0; (python_version >= '3.10') and extra == 'docs'
|
43
41
|
Requires-Dist: sphinx-rtd-theme; extra == 'docs'
|
44
42
|
Requires-Dist: sphinx<9,>=5; extra == 'docs'
|
45
43
|
Provides-Extra: lint
|
46
44
|
Requires-Dist: ruff; extra == 'lint'
|
47
|
-
Provides-Extra: numpy
|
48
|
-
Requires-Dist: numpy<3,>=1; extra == 'numpy'
|
49
|
-
Requires-Dist: numpy<3,>=1.25.0; (python_version >= '3.10') and extra == 'numpy'
|
50
45
|
Provides-Extra: test
|
51
|
-
Requires-Dist: numpy<3,>=1; extra == 'test'
|
52
|
-
Requires-Dist: numpy<3,>=1.25.0; (python_version >= '3.10') and extra == 'test'
|
53
46
|
Requires-Dist: pytest-asyncio<0.25,>=0.21; extra == 'test'
|
54
47
|
Requires-Dist: pytest-cov; extra == 'test'
|
55
48
|
Requires-Dist: pytest<9,>=7; extra == 'test'
|
56
49
|
Provides-Extra: typing
|
57
50
|
Requires-Dist: mypy<2,>=1; extra == 'typing'
|
58
|
-
Requires-Dist: numpy<3,>=1; extra == 'typing'
|
59
|
-
Requires-Dist: numpy<3,>=1.25.0; (python_version >= '3.10') and extra == 'typing'
|
60
51
|
Requires-Dist: pytest-asyncio<0.25,>=0.21; extra == 'typing'
|
61
52
|
Requires-Dist: pytest-cov; extra == 'typing'
|
62
53
|
Requires-Dist: pytest<9,>=7; extra == 'typing'
|
Binary file
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import os
|
4
3
|
import sys
|
5
4
|
from copy import deepcopy
|
6
5
|
from typing import Any, Optional, Tuple, Union, cast
|
@@ -15,6 +14,8 @@ if sys.version_info >= (3, 9):
|
|
15
14
|
else:
|
16
15
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
17
16
|
|
17
|
+
import numpy as np
|
18
|
+
|
18
19
|
from ._io import FoamFileIO
|
19
20
|
from ._serialization import Kind, dumps, normalize
|
20
21
|
from ._types import (
|
@@ -27,7 +28,6 @@ from ._types import (
|
|
27
28
|
File,
|
28
29
|
MutableEntry,
|
29
30
|
)
|
30
|
-
from ._util import is_sequence
|
31
31
|
|
32
32
|
|
33
33
|
class FoamFile(
|
@@ -196,7 +196,7 @@ class FoamFile(
|
|
196
196
|
elif not isinstance(keywords, tuple):
|
197
197
|
keywords = (keywords,)
|
198
198
|
|
199
|
-
if keywords and not isinstance(normalize(keywords[-1]
|
199
|
+
if keywords and not isinstance(normalize(keywords[-1]), str):
|
200
200
|
msg = f"Invalid keyword: {keywords[-1]}"
|
201
201
|
raise ValueError(msg)
|
202
202
|
|
@@ -228,50 +228,36 @@ class FoamFile(
|
|
228
228
|
or keywords[2].endswith("Gradient")
|
229
229
|
)
|
230
230
|
):
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
if (arch is not None and "scalar=32" in arch) or (
|
235
|
-
arch is None
|
236
|
-
and os.environ.get("WM_PRECISION_OPTION", default="DP") == "SP"
|
237
|
-
):
|
238
|
-
kind = Kind.SINGLE_PRECISION_BINARY_FIELD
|
239
|
-
else:
|
240
|
-
kind = Kind.DOUBLE_PRECISION_BINARY_FIELD
|
241
|
-
else:
|
242
|
-
kind = Kind.ASCII_FIELD
|
231
|
+
kind = (
|
232
|
+
Kind.BINARY_FIELD if self.format == "binary" else Kind.ASCII_FIELD
|
233
|
+
)
|
243
234
|
elif keywords == ("dimensions",):
|
244
235
|
kind = Kind.DIMENSIONS
|
245
236
|
|
246
237
|
if (
|
247
|
-
kind
|
248
|
-
in (
|
249
|
-
Kind.ASCII_FIELD,
|
250
|
-
Kind.DOUBLE_PRECISION_BINARY_FIELD,
|
251
|
-
Kind.SINGLE_PRECISION_BINARY_FIELD,
|
252
|
-
)
|
238
|
+
kind in (Kind.ASCII_FIELD, Kind.BINARY_FIELD)
|
253
239
|
) and self.class_ == "dictionary":
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
elif (
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
):
|
270
|
-
if
|
240
|
+
try:
|
241
|
+
shape = np.shape(data) # type: ignore [arg-type]
|
242
|
+
except ValueError:
|
243
|
+
pass
|
244
|
+
else:
|
245
|
+
if not shape:
|
246
|
+
self.class_ = "volScalarField"
|
247
|
+
elif shape == (3,):
|
248
|
+
self.class_ = "volVectorField"
|
249
|
+
elif shape == (6,):
|
250
|
+
self.class_ = "volSymmTensorField"
|
251
|
+
elif shape == (9,):
|
252
|
+
self.class_ = "volTensorField"
|
253
|
+
elif len(shape) == 1:
|
254
|
+
self.class_ = "volScalarField"
|
255
|
+
elif len(shape) == 2:
|
256
|
+
if shape[1] == 3:
|
271
257
|
self.class_ = "volVectorField"
|
272
|
-
elif
|
258
|
+
elif shape[1] == 6:
|
273
259
|
self.class_ = "volSymmTensorField"
|
274
|
-
elif
|
260
|
+
elif shape[1] == 9:
|
275
261
|
self.class_ = "volTensorField"
|
276
262
|
|
277
263
|
parsed = self._get_parsed(missing_ok=True)
|
@@ -304,7 +290,7 @@ class FoamFile(
|
|
304
290
|
...,
|
305
291
|
before
|
306
292
|
+ indentation
|
307
|
-
+ dumps(keywords[-1]
|
293
|
+
+ dumps(keywords[-1])
|
308
294
|
+ b"\n"
|
309
295
|
+ indentation
|
310
296
|
+ b"{\n"
|
@@ -322,7 +308,7 @@ class FoamFile(
|
|
322
308
|
normalize(data, kind=kind),
|
323
309
|
before
|
324
310
|
+ indentation
|
325
|
-
+ dumps(keywords[-1]
|
311
|
+
+ dumps(keywords[-1])
|
326
312
|
+ b" "
|
327
313
|
+ dumps(data, kind=kind)
|
328
314
|
+ b";"
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import array
|
4
3
|
import re
|
5
4
|
import sys
|
6
5
|
from enum import Enum, auto
|
@@ -16,6 +15,7 @@ if sys.version_info >= (3, 10):
|
|
16
15
|
else:
|
17
16
|
EllipsisType = type(...)
|
18
17
|
|
18
|
+
import numpy as np
|
19
19
|
from pyparsing import (
|
20
20
|
Combine,
|
21
21
|
Dict,
|
@@ -40,34 +40,37 @@ from pyparsing import (
|
|
40
40
|
from ._types import Data, Dimensioned, DimensionSet, File
|
41
41
|
|
42
42
|
|
43
|
-
class
|
43
|
+
class _Tensor(Enum):
|
44
44
|
SCALAR = auto()
|
45
45
|
VECTOR = auto()
|
46
46
|
SYMM_TENSOR = auto()
|
47
47
|
TENSOR = auto()
|
48
48
|
|
49
49
|
@property
|
50
|
-
def shape(self) -> tuple[int
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
50
|
+
def shape(self) -> tuple[()] | tuple[int]:
|
51
|
+
if self == _Tensor.SCALAR:
|
52
|
+
return ()
|
53
|
+
if self == _Tensor.VECTOR:
|
54
|
+
return (3,)
|
55
|
+
if self == _Tensor.SYMM_TENSOR:
|
56
|
+
return (6,)
|
57
|
+
if self == _Tensor.TENSOR:
|
58
|
+
return (9,)
|
59
|
+
raise NotImplementedError
|
57
60
|
|
58
61
|
@property
|
59
62
|
def size(self) -> int:
|
60
63
|
return {
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
64
|
+
_Tensor.SCALAR: 1,
|
65
|
+
_Tensor.VECTOR: 3,
|
66
|
+
_Tensor.SYMM_TENSOR: 6,
|
67
|
+
_Tensor.TENSOR: 9,
|
65
68
|
}[self]
|
66
69
|
|
67
70
|
def pattern(self, *, ignore: Regex | None = None) -> str:
|
68
71
|
float_pattern = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
|
69
72
|
|
70
|
-
if self ==
|
73
|
+
if self == _Tensor.SCALAR:
|
71
74
|
return float_pattern
|
72
75
|
|
73
76
|
ignore_pattern = (
|
@@ -77,21 +80,21 @@ class Tensor(Enum):
|
|
77
80
|
return rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{self.size - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
|
78
81
|
|
79
82
|
def parser(self) -> ParserElement:
|
80
|
-
if self ==
|
83
|
+
if self == _Tensor.SCALAR:
|
81
84
|
return common.ieee_float
|
82
85
|
|
83
86
|
return (
|
84
87
|
Literal("(").suppress()
|
85
88
|
+ Group(common.ieee_float[self.size], aslist=True)
|
86
89
|
+ Literal(")").suppress()
|
87
|
-
)
|
90
|
+
).add_parse_action(lambda tks: np.array(tks[0], dtype=float))
|
88
91
|
|
89
92
|
def __str__(self) -> str:
|
90
93
|
return {
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
94
|
+
_Tensor.SCALAR: "scalar",
|
95
|
+
_Tensor.VECTOR: "vector",
|
96
|
+
_Tensor.SYMM_TENSOR: "symmTensor",
|
97
|
+
_Tensor.TENSOR: "tensor",
|
95
98
|
}[self]
|
96
99
|
|
97
100
|
|
@@ -115,52 +118,34 @@ def _list_of(entry: ParserElement) -> ParserElement:
|
|
115
118
|
|
116
119
|
|
117
120
|
def _parse_ascii_field(
|
118
|
-
s: str, tensor_kind:
|
119
|
-
) ->
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
.replace("(", " ")
|
124
|
-
.replace(")", " ")
|
125
|
-
.split()
|
126
|
-
]
|
121
|
+
s: str, tensor_kind: _Tensor, *, ignore: Regex | None
|
122
|
+
) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64]]:
|
123
|
+
if ignore is not None:
|
124
|
+
s = re.sub(ignore.re, " ", s)
|
125
|
+
s = s.replace("(", " ").replace(")", " ")
|
127
126
|
|
128
|
-
|
129
|
-
return values
|
130
|
-
|
131
|
-
return [
|
132
|
-
values[i : i + tensor_kind.size]
|
133
|
-
for i in range(0, len(values), tensor_kind.size)
|
134
|
-
]
|
127
|
+
return np.fromstring(s, dtype=float, sep=" ").reshape(-1, *tensor_kind.shape)
|
135
128
|
|
136
129
|
|
137
130
|
def _unpack_binary_field(
|
138
|
-
b: bytes, tensor_kind:
|
139
|
-
) ->
|
131
|
+
b: bytes, tensor_kind: _Tensor, *, length: int
|
132
|
+
) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]:
|
140
133
|
float_size = len(b) / tensor_kind.size / length
|
141
134
|
assert float_size in (4, 8)
|
142
135
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
if tensor_kind == Tensor.SCALAR:
|
147
|
-
return values
|
148
|
-
|
149
|
-
return [
|
150
|
-
values[i : i + tensor_kind.size]
|
151
|
-
for i in range(0, len(values), tensor_kind.size)
|
152
|
-
]
|
136
|
+
dtype = np.float32 if float_size == 4 else float
|
137
|
+
return np.frombuffer(b, dtype=dtype).reshape(-1, *tensor_kind.shape)
|
153
138
|
|
154
139
|
|
155
140
|
def _tensor_list(
|
156
|
-
tensor_kind:
|
141
|
+
tensor_kind: _Tensor | None = None, *, ignore: Regex | None = None
|
157
142
|
) -> ParserElement:
|
158
143
|
if tensor_kind is None:
|
159
144
|
return (
|
160
|
-
_tensor_list(
|
161
|
-
| _tensor_list(
|
162
|
-
| _tensor_list(
|
163
|
-
| _tensor_list(
|
145
|
+
_tensor_list(_Tensor.SCALAR, ignore=ignore)
|
146
|
+
| _tensor_list(_Tensor.VECTOR, ignore=ignore)
|
147
|
+
| _tensor_list(_Tensor.SYMM_TENSOR, ignore=ignore)
|
148
|
+
| _tensor_list(_Tensor.TENSOR, ignore=ignore)
|
164
149
|
)
|
165
150
|
|
166
151
|
tensor_pattern = tensor_kind.pattern(ignore=ignore)
|
@@ -187,7 +172,7 @@ def _tensor_list(
|
|
187
172
|
)
|
188
173
|
| Regex(
|
189
174
|
rf"\((?s:.{{{length * tensor_kind.size * 8}}}|.{{{length * tensor_kind.size * 4}}})\)"
|
190
|
-
).
|
175
|
+
).add_parse_action(
|
191
176
|
lambda tks: [
|
192
177
|
_unpack_binary_field(
|
193
178
|
tks[0][1:-1].encode("latin-1"), tensor_kind, length=length
|
@@ -196,7 +181,9 @@ def _tensor_list(
|
|
196
181
|
)
|
197
182
|
| (
|
198
183
|
Literal("{").suppress() + tensor_kind.parser() + Literal("}").suppress()
|
199
|
-
).
|
184
|
+
).add_parse_action(
|
185
|
+
lambda tks: [np.full((length, *tensor_kind.shape), tks[0], dtype=float)]
|
186
|
+
)
|
200
187
|
)
|
201
188
|
|
202
189
|
count = common.integer.copy().add_parse_action(count_parse_action)
|
@@ -274,10 +261,10 @@ _DIMENSIONS = (
|
|
274
261
|
Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
|
275
262
|
).set_parse_action(lambda tks: DimensionSet(*tks))
|
276
263
|
_TENSOR = (
|
277
|
-
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
264
|
+
_Tensor.SCALAR.parser()
|
265
|
+
| _Tensor.VECTOR.parser()
|
266
|
+
| _Tensor.SYMM_TENSOR.parser()
|
267
|
+
| _Tensor.TENSOR.parser()
|
281
268
|
)
|
282
269
|
_IDENTIFIER = Combine(
|
283
270
|
Word(_IDENTCHARS, _IDENTBODYCHARS, exclude_chars="()")
|
@@ -289,27 +276,32 @@ _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
|
289
276
|
_FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
290
277
|
Keyword("nonuniform", _IDENTBODYCHARS).suppress() + _tensor_list(ignore=_COMMENT)
|
291
278
|
)
|
292
|
-
|
293
|
-
|
294
|
-
_KEYWORD_ENTRY = _keyword_entry_of(
|
295
|
-
_DICT = _dict_of(
|
279
|
+
_TOKEN = dbl_quoted_string | _IDENTIFIER
|
280
|
+
_DATA = Forward()
|
281
|
+
_KEYWORD_ENTRY = _keyword_entry_of(_TOKEN | _list_of(_IDENTIFIER), _DATA)
|
282
|
+
_DICT = _dict_of(_TOKEN, _DATA)
|
296
283
|
_DATA_ENTRY = Forward()
|
297
284
|
_LIST_ENTRY = _DICT | _KEYWORD_ENTRY | _DATA_ENTRY
|
298
285
|
_LIST = _list_of(_LIST_ENTRY)
|
299
286
|
_NUMBER = common.signed_integer ^ common.ieee_float
|
300
|
-
_DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH |
|
287
|
+
_DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH | _TOKEN
|
301
288
|
|
302
|
-
|
289
|
+
_DATA <<= (
|
303
290
|
_DATA_ENTRY[1, ...]
|
304
291
|
.set_parse_action(lambda tks: [tuple(tks)] if len(tks) > 1 else [tks[0]])
|
305
292
|
.ignore(_COMMENT)
|
306
293
|
.parse_with_tabs()
|
307
294
|
)
|
308
295
|
|
296
|
+
|
297
|
+
def parse_data(s: str) -> Data:
|
298
|
+
return cast(Data, _DATA.parse_string(s, parse_all=True)[0])
|
299
|
+
|
300
|
+
|
309
301
|
_LOCATED_DICTIONARY = Group(
|
310
|
-
_keyword_entry_of(
|
302
|
+
_keyword_entry_of(_TOKEN, Opt(_DATA, default=""), located=True)
|
311
303
|
)[...]
|
312
|
-
_LOCATED_DATA = Group(Located(
|
304
|
+
_LOCATED_DATA = Group(Located(_DATA.copy().add_parse_action(lambda tks: ["", tks[0]])))
|
313
305
|
|
314
306
|
_FILE = (
|
315
307
|
Dict(_LOCATED_DICTIONARY + Opt(_LOCATED_DATA) + _LOCATED_DICTIONARY)
|
@@ -1,35 +1,25 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import array
|
4
|
-
import itertools
|
5
3
|
import sys
|
6
4
|
from enum import Enum, auto
|
7
|
-
from typing import
|
5
|
+
from typing import overload
|
8
6
|
|
9
7
|
if sys.version_info >= (3, 9):
|
10
|
-
from collections.abc import Mapping
|
8
|
+
from collections.abc import Mapping
|
11
9
|
else:
|
12
|
-
from typing import Mapping
|
10
|
+
from typing import Mapping
|
13
11
|
|
14
|
-
|
15
|
-
from ._types import Data, Dimensioned, DimensionSet, Entry
|
16
|
-
from ._util import is_sequence
|
12
|
+
import numpy as np
|
17
13
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
numpy = True
|
22
|
-
except ModuleNotFoundError:
|
23
|
-
numpy = False
|
14
|
+
from ._parsing import parse_data
|
15
|
+
from ._types import Data, Dimensioned, DimensionSet, Entry, is_sequence
|
24
16
|
|
25
17
|
|
26
18
|
class Kind(Enum):
|
27
19
|
DEFAULT = auto()
|
28
|
-
KEYWORD = auto()
|
29
20
|
SINGLE_ENTRY = auto()
|
30
21
|
ASCII_FIELD = auto()
|
31
|
-
|
32
|
-
SINGLE_PRECISION_BINARY_FIELD = auto()
|
22
|
+
BINARY_FIELD = auto()
|
33
23
|
DIMENSIONS = auto()
|
34
24
|
|
35
25
|
|
@@ -42,9 +32,29 @@ def normalize(data: Entry, *, kind: Kind = Kind.DEFAULT) -> Entry: ...
|
|
42
32
|
|
43
33
|
|
44
34
|
def normalize(data: Entry, *, kind: Kind = Kind.DEFAULT) -> Entry:
|
45
|
-
if
|
35
|
+
if kind in (Kind.ASCII_FIELD, Kind.BINARY_FIELD):
|
36
|
+
if is_sequence(data):
|
37
|
+
try:
|
38
|
+
arr = np.asarray(data)
|
39
|
+
except ValueError:
|
40
|
+
pass
|
41
|
+
else:
|
42
|
+
if not np.issubdtype(arr.dtype, np.floating):
|
43
|
+
arr = arr.astype(float)
|
44
|
+
|
45
|
+
if arr.ndim == 1 or (arr.ndim == 2 and arr.shape[1] in (3, 6, 9)):
|
46
|
+
return arr
|
47
|
+
|
48
|
+
return data
|
49
|
+
|
50
|
+
if isinstance(data, int):
|
51
|
+
return float(data)
|
52
|
+
|
53
|
+
return data
|
54
|
+
|
55
|
+
if isinstance(data, np.ndarray):
|
46
56
|
ret = data.tolist()
|
47
|
-
assert isinstance(ret, list)
|
57
|
+
assert isinstance(ret, (int, float, list))
|
48
58
|
return ret
|
49
59
|
|
50
60
|
if isinstance(data, Mapping):
|
@@ -56,32 +66,21 @@ def normalize(data: Entry, *, kind: Kind = Kind.DEFAULT) -> Entry:
|
|
56
66
|
and len(data) <= 7
|
57
67
|
and all(isinstance(d, (int, float)) for d in data)
|
58
68
|
):
|
59
|
-
data = cast(Sequence[float], data)
|
60
69
|
return DimensionSet(*data)
|
61
70
|
|
62
71
|
if isinstance(data, tuple) and kind == Kind.SINGLE_ENTRY and len(data) == 2:
|
63
72
|
k, v = data
|
64
|
-
return (normalize(k
|
73
|
+
return (normalize(k), normalize(v))
|
65
74
|
|
66
75
|
if is_sequence(data) and (kind == Kind.SINGLE_ENTRY or not isinstance(data, tuple)):
|
67
76
|
return [normalize(d, kind=Kind.SINGLE_ENTRY) for d in data]
|
68
77
|
|
69
|
-
if isinstance(data, Dimensioned):
|
70
|
-
value = normalize(data.value, kind=Kind.SINGLE_ENTRY)
|
71
|
-
assert isinstance(value, (int, float, list))
|
72
|
-
return Dimensioned(value, data.dimensions, data.name)
|
73
|
-
|
74
78
|
if isinstance(data, str):
|
75
|
-
|
76
|
-
data = TOKEN.parse_string(data, parse_all=True)[0]
|
77
|
-
assert isinstance(data, str)
|
78
|
-
return data
|
79
|
-
|
80
|
-
return cast(Data, DATA.parse_string(data, parse_all=True)[0])
|
79
|
+
return parse_data(data)
|
81
80
|
|
82
81
|
if isinstance(
|
83
82
|
data,
|
84
|
-
(int, float, bool, tuple, DimensionSet),
|
83
|
+
(int, float, bool, tuple, DimensionSet, Dimensioned),
|
85
84
|
):
|
86
85
|
return data
|
87
86
|
|
@@ -105,7 +104,7 @@ def dumps(
|
|
105
104
|
|
106
105
|
if isinstance(data, tuple) and kind == Kind.SINGLE_ENTRY and len(data) == 2:
|
107
106
|
k, v = data
|
108
|
-
ret = dumps(k
|
107
|
+
ret = dumps(k) + b" " + dumps(v)
|
109
108
|
if not isinstance(v, Mapping):
|
110
109
|
ret += b";"
|
111
110
|
return ret
|
@@ -113,58 +112,35 @@ def dumps(
|
|
113
112
|
if isinstance(data, DimensionSet):
|
114
113
|
return b"[" + b" ".join(dumps(v) for v in data) + b"]"
|
115
114
|
|
116
|
-
if kind in (
|
117
|
-
|
118
|
-
Kind.DOUBLE_PRECISION_BINARY_FIELD,
|
119
|
-
Kind.SINGLE_PRECISION_BINARY_FIELD,
|
120
|
-
) and (
|
121
|
-
isinstance(data, (int, float))
|
122
|
-
or (
|
123
|
-
is_sequence(data)
|
124
|
-
and data
|
125
|
-
and isinstance(data[0], (int, float))
|
126
|
-
and len(data) in (3, 6, 9)
|
127
|
-
)
|
115
|
+
if kind in (Kind.ASCII_FIELD, Kind.BINARY_FIELD) and (
|
116
|
+
isinstance(data, (int, float, np.ndarray))
|
128
117
|
):
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
if data and isinstance(data[0], (int, float)):
|
118
|
+
shape = np.shape(data)
|
119
|
+
if shape in ((), (3,), (6,), (9,)):
|
120
|
+
return b"uniform " + dumps(data, kind=Kind.SINGLE_ENTRY)
|
121
|
+
|
122
|
+
assert isinstance(data, np.ndarray)
|
123
|
+
ndim = len(shape)
|
124
|
+
if ndim == 1:
|
137
125
|
tensor_kind = b"scalar"
|
138
|
-
|
139
|
-
|
126
|
+
|
127
|
+
elif ndim == 2:
|
128
|
+
if shape[1] == 3:
|
140
129
|
tensor_kind = b"vector"
|
141
|
-
elif
|
130
|
+
elif shape[1] == 6:
|
142
131
|
tensor_kind = b"symmTensor"
|
143
|
-
elif
|
132
|
+
elif shape[1] == 9:
|
144
133
|
tensor_kind = b"tensor"
|
145
134
|
else:
|
146
135
|
return dumps(data)
|
136
|
+
|
147
137
|
else:
|
148
138
|
return dumps(data)
|
149
139
|
|
150
|
-
if kind
|
151
|
-
|
152
|
-
Kind.SINGLE_PRECISION_BINARY_FIELD,
|
153
|
-
):
|
154
|
-
typecode = "f" if kind == Kind.SINGLE_PRECISION_BINARY_FIELD else "d"
|
155
|
-
if tensor_kind == b"scalar":
|
156
|
-
data = cast(Sequence[float], data)
|
157
|
-
contents = b"(" + array.array(typecode, data).tobytes() + b")"
|
158
|
-
else:
|
159
|
-
data = cast(Sequence[Sequence[float]], data)
|
160
|
-
contents = (
|
161
|
-
b"("
|
162
|
-
+ array.array(
|
163
|
-
typecode, itertools.chain.from_iterable(data)
|
164
|
-
).tobytes()
|
165
|
-
+ b")"
|
166
|
-
)
|
140
|
+
if kind == Kind.BINARY_FIELD:
|
141
|
+
contents = b"(" + data.tobytes() + b")"
|
167
142
|
else:
|
143
|
+
assert kind == Kind.ASCII_FIELD
|
168
144
|
contents = dumps(data, kind=Kind.SINGLE_ENTRY)
|
169
145
|
|
170
146
|
return b"nonuniform List<" + tensor_kind + b"> " + dumps(len(data)) + contents
|
@@ -2,16 +2,20 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import sys
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from typing import
|
5
|
+
from typing import Dict, NamedTuple, Optional, Tuple, Union
|
6
6
|
|
7
|
-
|
8
|
-
import numpy as np
|
7
|
+
import numpy as np
|
9
8
|
|
10
9
|
if sys.version_info >= (3, 9):
|
11
10
|
from collections.abc import Mapping, MutableMapping, Sequence
|
12
11
|
else:
|
13
12
|
from typing import Mapping, MutableMapping, Sequence
|
14
13
|
|
14
|
+
if sys.version_info >= (3, 10):
|
15
|
+
from typing import TypeGuard
|
16
|
+
else:
|
17
|
+
from typing_extensions import TypeGuard
|
18
|
+
|
15
19
|
|
16
20
|
class DimensionSet(NamedTuple):
|
17
21
|
mass: float = 0
|
@@ -29,7 +33,7 @@ class DimensionSet(NamedTuple):
|
|
29
33
|
Tensor = Union[
|
30
34
|
float,
|
31
35
|
Sequence[float],
|
32
|
-
"np.ndarray[tuple[()] |
|
36
|
+
"np.ndarray[tuple[()] | Tuple[int], np.dtype[np.float64]]",
|
33
37
|
]
|
34
38
|
|
35
39
|
|
@@ -40,14 +44,30 @@ class Dimensioned:
|
|
40
44
|
name: str | None = None
|
41
45
|
|
42
46
|
def __post_init__(self) -> None:
|
47
|
+
if is_sequence(self.value):
|
48
|
+
self.value = np.asarray(self.value, dtype=float)
|
49
|
+
else:
|
50
|
+
assert isinstance(self.value, (int, float, np.ndarray))
|
51
|
+
self.value = float(self.value)
|
52
|
+
|
43
53
|
if not isinstance(self.dimensions, DimensionSet):
|
44
54
|
self.dimensions = DimensionSet(*self.dimensions)
|
45
55
|
|
56
|
+
def __eq__(self, other: object) -> bool:
|
57
|
+
if not isinstance(other, Dimensioned):
|
58
|
+
return NotImplemented
|
59
|
+
|
60
|
+
return (
|
61
|
+
self.dimensions == other.dimensions
|
62
|
+
and np.array_equal(self.value, other.value)
|
63
|
+
and self.name == other.name
|
64
|
+
)
|
65
|
+
|
46
66
|
|
47
67
|
Field = Union[
|
48
68
|
Tensor,
|
49
69
|
Sequence[Tensor],
|
50
|
-
"np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.
|
70
|
+
"np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]",
|
51
71
|
]
|
52
72
|
|
53
73
|
Data = Union[
|
@@ -58,7 +78,6 @@ Data = Union[
|
|
58
78
|
Dimensioned,
|
59
79
|
DimensionSet,
|
60
80
|
Sequence["Entry"],
|
61
|
-
Tensor,
|
62
81
|
Field,
|
63
82
|
]
|
64
83
|
|
@@ -70,6 +89,18 @@ Entry = Union[
|
|
70
89
|
A value that can be stored in an OpenFOAM file.
|
71
90
|
"""
|
72
91
|
|
92
|
+
|
93
|
+
def is_sequence(
|
94
|
+
value: Entry,
|
95
|
+
) -> TypeGuard[
|
96
|
+
Sequence[Entry]
|
97
|
+
| np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]
|
98
|
+
]:
|
99
|
+
return (isinstance(value, Sequence) and not isinstance(value, str)) or (
|
100
|
+
isinstance(value, np.ndarray) and value.ndim > 0
|
101
|
+
)
|
102
|
+
|
103
|
+
|
73
104
|
MutableEntry = Union[
|
74
105
|
Data,
|
75
106
|
MutableMapping[str, "MutableEntry"],
|
@@ -30,6 +30,8 @@ classifiers = [
|
|
30
30
|
|
31
31
|
dependencies = [
|
32
32
|
"aioshutil>=1,<2",
|
33
|
+
"numpy>=1.25.0,<3; python_version>='3.10'",
|
34
|
+
"numpy>=1,<3",
|
33
35
|
"pyparsing>=3,<4",
|
34
36
|
"typing-extensions>=4,<5; python_version<'3.11'",
|
35
37
|
]
|
@@ -37,13 +39,8 @@ dependencies = [
|
|
37
39
|
dynamic = ["version"]
|
38
40
|
|
39
41
|
[project.optional-dependencies]
|
40
|
-
numpy = [
|
41
|
-
"numpy>=1.25.0,<3; python_version>='3.10'",
|
42
|
-
"numpy>=1,<3"
|
43
|
-
]
|
44
42
|
lint = ["ruff"]
|
45
43
|
test = [
|
46
|
-
"foamlib[numpy]",
|
47
44
|
"pytest>=7,<9",
|
48
45
|
"pytest-asyncio>=0.21,<0.25",
|
49
46
|
"pytest-cov",
|
@@ -53,12 +50,10 @@ typing = [
|
|
53
50
|
"mypy>=1,<2",
|
54
51
|
]
|
55
52
|
docs = [
|
56
|
-
"foamlib[numpy]",
|
57
53
|
"sphinx>=5,<9",
|
58
54
|
"sphinx_rtd_theme",
|
59
55
|
]
|
60
56
|
dev = [
|
61
|
-
"foamlib[numpy]",
|
62
57
|
"foamlib[lint]",
|
63
58
|
"foamlib[test]",
|
64
59
|
"foamlib[typing]",
|
@@ -2,13 +2,13 @@ import os
|
|
2
2
|
import stat
|
3
3
|
import sys
|
4
4
|
from pathlib import Path
|
5
|
-
from typing import Sequence
|
6
5
|
|
7
6
|
if sys.version_info >= (3, 9):
|
8
7
|
from collections.abc import Generator
|
9
8
|
else:
|
10
9
|
from typing import Generator
|
11
10
|
|
11
|
+
import numpy as np
|
12
12
|
import pytest
|
13
13
|
from foamlib import FoamCase
|
14
14
|
|
@@ -48,7 +48,7 @@ def test_run(cavity: FoamCase) -> None:
|
|
48
48
|
cavity.run(parallel=False)
|
49
49
|
assert len(cavity) > 0
|
50
50
|
internal = cavity[-1]["U"].internal_field
|
51
|
-
assert isinstance(internal,
|
51
|
+
assert isinstance(internal, np.ndarray)
|
52
52
|
assert len(internal) == 400
|
53
53
|
|
54
54
|
|
@@ -61,5 +61,5 @@ def test_double_clean(cavity: FoamCase) -> None:
|
|
61
61
|
def test_cell_centers(cavity: FoamCase) -> None:
|
62
62
|
cavity.block_mesh()
|
63
63
|
C = cavity[0].cell_centers()
|
64
|
-
assert isinstance(C.internal_field,
|
64
|
+
assert isinstance(C.internal_field, np.ndarray)
|
65
65
|
assert len(C.internal_field) == 400
|
@@ -9,6 +9,7 @@ if sys.version_info >= (3, 9):
|
|
9
9
|
else:
|
10
10
|
from typing import AsyncGenerator
|
11
11
|
|
12
|
+
import numpy as np
|
12
13
|
import pytest
|
13
14
|
import pytest_asyncio
|
14
15
|
from foamlib import AsyncFoamCase
|
@@ -50,7 +51,7 @@ async def test_run(cavity: AsyncFoamCase) -> None:
|
|
50
51
|
await cavity.run(parallel=False)
|
51
52
|
assert len(cavity) > 0
|
52
53
|
internal = cavity[-1]["U"].internal_field
|
53
|
-
assert isinstance(internal,
|
54
|
+
assert isinstance(internal, np.ndarray)
|
54
55
|
assert len(internal) == 400
|
55
56
|
|
56
57
|
|
@@ -65,7 +66,7 @@ async def test_double_clean(cavity: AsyncFoamCase) -> None:
|
|
65
66
|
async def test_cell_centers(cavity: AsyncFoamCase) -> None:
|
66
67
|
await cavity.block_mesh()
|
67
68
|
C = await cavity[0].cell_centers()
|
68
|
-
assert isinstance(C.internal_field,
|
69
|
+
assert isinstance(C.internal_field, np.ndarray)
|
69
70
|
assert len(C.internal_field) == 400
|
70
71
|
|
71
72
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import numpy as np
|
1
2
|
from foamlib import FoamFile
|
2
3
|
from foamlib._files._serialization import Kind, dumps
|
3
4
|
|
@@ -11,34 +12,32 @@ def test_serialize_data() -> None:
|
|
11
12
|
assert dumps("word") == b"word"
|
12
13
|
assert dumps(("word", "word")) == b"word word"
|
13
14
|
assert dumps('"a string"') == b'"a string"'
|
14
|
-
assert dumps(1, kind=Kind.ASCII_FIELD) == b"uniform 1"
|
15
|
+
assert dumps(1, kind=Kind.ASCII_FIELD) == b"uniform 1.0"
|
15
16
|
assert dumps(1.0, kind=Kind.ASCII_FIELD) == b"uniform 1.0"
|
16
17
|
assert dumps(1.0e-3, kind=Kind.ASCII_FIELD) == b"uniform 0.001"
|
17
18
|
assert dumps([1.0, 2.0, 3.0]) == b"(1.0 2.0 3.0)"
|
18
|
-
assert dumps([1, 2, 3], kind=Kind.ASCII_FIELD) == b"uniform (1 2 3)"
|
19
|
+
assert dumps([1, 2, 3], kind=Kind.ASCII_FIELD) == b"uniform (1.0 2.0 3.0)"
|
19
20
|
assert (
|
20
21
|
dumps([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], kind=Kind.ASCII_FIELD)
|
21
|
-
== b"nonuniform List<scalar> 10(1 2 3 4 5 6 7 8 9 10)"
|
22
|
+
== b"nonuniform List<scalar> 10(1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0)"
|
22
23
|
)
|
23
24
|
assert (
|
24
25
|
dumps([[1, 2, 3], [4, 5, 6]], kind=Kind.ASCII_FIELD)
|
25
|
-
== b"nonuniform List<vector> 2((1 2 3) (4 5 6))"
|
26
|
+
== b"nonuniform List<vector> 2((1.0 2.0 3.0) (4.0 5.0 6.0))"
|
26
27
|
)
|
27
|
-
assert dumps(1, kind=Kind.
|
28
|
-
assert dumps(1.0, kind=Kind.
|
28
|
+
assert dumps(1, kind=Kind.BINARY_FIELD) == b"uniform 1.0"
|
29
|
+
assert dumps(1.0, kind=Kind.BINARY_FIELD) == b"uniform 1.0"
|
30
|
+
assert dumps([1, 2, 3], kind=Kind.BINARY_FIELD) == b"uniform (1.0 2.0 3.0)"
|
29
31
|
assert (
|
30
|
-
dumps([1, 2, 3], kind=Kind.
|
31
|
-
)
|
32
|
-
assert (
|
33
|
-
dumps([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], kind=Kind.DOUBLE_PRECISION_BINARY_FIELD)
|
32
|
+
dumps([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], kind=Kind.BINARY_FIELD)
|
34
33
|
== b'nonuniform List<scalar> 10(\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@\x00\x00\x00\x00\x00\x00\x1c@\x00\x00\x00\x00\x00\x00 @\x00\x00\x00\x00\x00\x00"@\x00\x00\x00\x00\x00\x00$@)'
|
35
34
|
)
|
36
35
|
assert (
|
37
|
-
dumps([[1, 2, 3], [4, 5, 6]], kind=Kind.
|
36
|
+
dumps([[1, 2, 3], [4, 5, 6]], kind=Kind.BINARY_FIELD)
|
38
37
|
== b"nonuniform List<vector> 2(\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@)"
|
39
38
|
)
|
40
39
|
assert (
|
41
|
-
dumps([1, 2], kind=Kind.
|
40
|
+
dumps(np.array([1, 2], dtype=np.float32), kind=Kind.BINARY_FIELD)
|
42
41
|
== b"nonuniform List<scalar> 2(\x00\x00\x80?\x00\x00\x00@)"
|
43
42
|
)
|
44
43
|
assert (
|
@@ -98,7 +98,9 @@ def test_new_field(tmp_path: Path) -> None:
|
|
98
98
|
Path(tmp_path / "testField").touch()
|
99
99
|
f = FoamFieldFile(tmp_path / "testField")
|
100
100
|
f.internal_field = [1, 2, 3]
|
101
|
-
|
101
|
+
field = f.internal_field
|
102
|
+
assert isinstance(field, np.ndarray)
|
103
|
+
assert np.array_equal(f.internal_field, [1, 2, 3])
|
102
104
|
assert f.class_ == "volVectorField"
|
103
105
|
|
104
106
|
|
@@ -166,9 +168,8 @@ def test_internal_field(cavity: FoamCase) -> None:
|
|
166
168
|
|
167
169
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
168
170
|
U = cavity[0]["U"].internal_field
|
169
|
-
assert isinstance(U,
|
170
|
-
|
171
|
-
assert u == pytest.approx(u_arr)
|
171
|
+
assert isinstance(U, np.ndarray)
|
172
|
+
assert U_arr == pytest.approx(U)
|
172
173
|
|
173
174
|
p_arr = np.arange(size) * 1e-6
|
174
175
|
U_arr = np.full((size, 3), [-1e-6, 1e-6, 0]) * np.arange(size)[:, np.newaxis]
|
@@ -178,9 +179,8 @@ def test_internal_field(cavity: FoamCase) -> None:
|
|
178
179
|
|
179
180
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
180
181
|
U = cavity[0]["U"].internal_field
|
181
|
-
assert isinstance(U,
|
182
|
-
|
183
|
-
assert u == pytest.approx(u_arr)
|
182
|
+
assert isinstance(U, np.ndarray)
|
183
|
+
assert U_arr == pytest.approx(U)
|
184
184
|
|
185
185
|
cavity.run(parallel=False)
|
186
186
|
|
@@ -200,27 +200,23 @@ def test_binary_field(cavity: FoamCase) -> None:
|
|
200
200
|
cavity.run(parallel=False)
|
201
201
|
|
202
202
|
p_bin = cavity[-1]["p"].internal_field
|
203
|
-
assert isinstance(p_bin,
|
203
|
+
assert isinstance(p_bin, np.ndarray)
|
204
204
|
U_bin = cavity[-1]["U"].internal_field
|
205
|
-
assert isinstance(U_bin,
|
206
|
-
assert
|
207
|
-
assert len(U_bin[0]) == 3
|
208
|
-
size = len(p_bin)
|
209
|
-
assert len(U_bin) == size
|
205
|
+
assert isinstance(U_bin, np.ndarray)
|
206
|
+
assert U_bin.shape == (len(p_bin), 3)
|
210
207
|
|
211
208
|
cavity.clean()
|
212
209
|
|
213
|
-
p_arr = np.arange(
|
214
|
-
U_arr = np.
|
210
|
+
p_arr = np.arange(len(p_bin)) * 1e-6
|
211
|
+
U_arr = np.full_like(U_bin, [-1e-6, 1e-6, 0]) * np.arange(len(U_bin))[:, np.newaxis]
|
215
212
|
|
216
213
|
cavity[0]["p"].internal_field = p_arr
|
217
214
|
cavity[0]["U"].internal_field = U_arr
|
218
215
|
|
219
216
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
220
217
|
U = cavity[0]["U"].internal_field
|
221
|
-
assert isinstance(U,
|
222
|
-
|
223
|
-
assert u == pytest.approx(u_arr)
|
218
|
+
assert isinstance(U, np.ndarray)
|
219
|
+
assert U_arr == pytest.approx(U)
|
224
220
|
|
225
221
|
cavity.run(parallel=False)
|
226
222
|
|
@@ -231,26 +227,22 @@ def test_compressed_field(cavity: FoamCase) -> None:
|
|
231
227
|
cavity.run(parallel=False)
|
232
228
|
|
233
229
|
p_bin = cavity[-1]["p"].internal_field
|
234
|
-
assert isinstance(p_bin,
|
230
|
+
assert isinstance(p_bin, np.ndarray)
|
235
231
|
U_bin = cavity[-1]["U"].internal_field
|
236
|
-
assert isinstance(U_bin,
|
237
|
-
assert
|
238
|
-
assert len(U_bin[0]) == 3
|
239
|
-
size = len(p_bin)
|
240
|
-
assert len(U_bin) == size
|
232
|
+
assert isinstance(U_bin, np.ndarray)
|
233
|
+
assert U_bin.shape == (len(p_bin), 3)
|
241
234
|
|
242
235
|
cavity.clean()
|
243
236
|
|
244
|
-
p_arr = np.arange(
|
245
|
-
U_arr = np.
|
237
|
+
p_arr = np.arange(len(p_bin)) * 1e-6
|
238
|
+
U_arr = np.full_like(U_bin, [-1e-6, 1e-6, 0]) * np.arange(len(U_bin))[:, np.newaxis]
|
246
239
|
|
247
240
|
cavity[0]["p"].internal_field = p_arr
|
248
241
|
cavity[0]["U"].internal_field = U_arr
|
249
242
|
|
250
243
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
251
244
|
U = cavity[0]["U"].internal_field
|
252
|
-
assert isinstance(U,
|
253
|
-
|
254
|
-
assert u == pytest.approx(u_arr)
|
245
|
+
assert isinstance(U, np.ndarray)
|
246
|
+
assert U_arr == pytest.approx(U)
|
255
247
|
|
256
248
|
cavity.run(parallel=False)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import numpy as np
|
1
2
|
from foamlib import FoamFile
|
2
3
|
from foamlib._files._parsing import Parsed
|
3
4
|
|
@@ -15,9 +16,15 @@ def test_parse_value() -> None:
|
|
15
16
|
assert Parsed(b"uniform 1.0")[()] == 1.0
|
16
17
|
assert Parsed(b"uniform 1.0e-3")[()] == 1.0e-3
|
17
18
|
assert Parsed(b"(1.0 2.0 3.0)")[()] == [1.0, 2.0, 3.0]
|
18
|
-
|
19
|
-
assert
|
20
|
-
assert
|
19
|
+
field = Parsed(b"uniform (1 2 3)")[()]
|
20
|
+
assert isinstance(field, np.ndarray)
|
21
|
+
assert np.array_equal(field, [1, 2, 3])
|
22
|
+
field = Parsed(b"nonuniform List<scalar> 2(1 2)")[()]
|
23
|
+
assert isinstance(field, np.ndarray)
|
24
|
+
assert np.array_equal(field, [1, 2])
|
25
|
+
field = Parsed(b"nonuniform List<scalar> 2{1}")[()]
|
26
|
+
assert isinstance(field, np.ndarray)
|
27
|
+
assert np.array_equal(field, [1, 1])
|
21
28
|
assert Parsed(b"3(1 2 3)")[()] == [1, 2, 3]
|
22
29
|
assert Parsed(b"2((1 2 3) (4 5 6))")[()] == [
|
23
30
|
[1, 2, 3],
|
@@ -27,24 +34,37 @@ def test_parse_value() -> None:
|
|
27
34
|
[1, 2, 3],
|
28
35
|
[1, 2, 3],
|
29
36
|
]
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
field = Parsed(b"nonuniform List<vector> 2((1 2 3) (4 5 6))")[()]
|
38
|
+
assert isinstance(field, np.ndarray)
|
39
|
+
assert np.array_equal(
|
40
|
+
field,
|
41
|
+
[
|
42
|
+
[1, 2, 3],
|
43
|
+
[4, 5, 6],
|
44
|
+
],
|
45
|
+
)
|
46
|
+
field = Parsed(b"nonuniform List<vector> 2{(1 2 3)}")[()]
|
47
|
+
assert isinstance(field, np.ndarray)
|
48
|
+
assert np.array_equal(
|
49
|
+
field,
|
50
|
+
[
|
51
|
+
[1, 2, 3],
|
52
|
+
[1, 2, 3],
|
53
|
+
],
|
54
|
+
)
|
55
|
+
field = Parsed(
|
39
56
|
b"nonuniform List<scalar> 2(\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@)"
|
40
|
-
)[()]
|
41
|
-
assert
|
57
|
+
)[()]
|
58
|
+
assert isinstance(field, np.ndarray)
|
59
|
+
assert np.array_equal(field, [1, 2])
|
60
|
+
field = Parsed(
|
42
61
|
b"nonuniform List<vector> 2(\x00\x00\x00\x00\x00\x00\xf0?\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x08@\x00\x00\x00\x00\x00\x00\x10@\x00\x00\x00\x00\x00\x00\x14@\x00\x00\x00\x00\x00\x00\x18@)"
|
43
|
-
)[()]
|
44
|
-
assert
|
45
|
-
|
46
|
-
|
47
|
-
|
62
|
+
)[()]
|
63
|
+
assert isinstance(field, np.ndarray)
|
64
|
+
assert np.array_equal(field, [[1, 2, 3], [4, 5, 6]])
|
65
|
+
field = Parsed(b"nonuniform List<scalar> 2(\x00\x00\x80?\x00\x00\x00@)")[()]
|
66
|
+
assert isinstance(field, np.ndarray)
|
67
|
+
assert np.array_equal(field, [1, 2])
|
48
68
|
assert Parsed(b"[1 1 -2 0 0 0 0]")[()] == FoamFile.DimensionSet(
|
49
69
|
mass=1, length=1, time=-2
|
50
70
|
)
|
foamlib-0.8.0/benchmark.png
DELETED
Binary file
|
@@ -1,23 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import sys
|
4
|
-
from typing import TYPE_CHECKING
|
5
|
-
|
6
|
-
if sys.version_info >= (3, 9):
|
7
|
-
from collections.abc import Sequence
|
8
|
-
else:
|
9
|
-
from typing import Sequence
|
10
|
-
|
11
|
-
if sys.version_info >= (3, 10):
|
12
|
-
from typing import TypeGuard
|
13
|
-
else:
|
14
|
-
from typing_extensions import TypeGuard
|
15
|
-
|
16
|
-
if TYPE_CHECKING:
|
17
|
-
from ._types import Entry
|
18
|
-
|
19
|
-
|
20
|
-
def is_sequence(
|
21
|
-
value: Entry,
|
22
|
-
) -> TypeGuard[Sequence[Entry]]:
|
23
|
-
return isinstance(value, Sequence) and not isinstance(value, str)
|
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
|
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
|
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
|