foamlib 0.8.1__tar.gz → 0.8.3__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.1 → foamlib-0.8.3}/.github/workflows/ci.yml +5 -5
- {foamlib-0.8.1 → foamlib-0.8.3}/.github/workflows/pypi-publish.yml +1 -1
- {foamlib-0.8.1 → foamlib-0.8.3}/PKG-INFO +7 -6
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/__init__.py +1 -1
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_async.py +1 -1
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_files/_files.py +5 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_files/_parsing.py +44 -80
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_files/_serialization.py +21 -8
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_files/_types.py +37 -3
- {foamlib-0.8.1 → foamlib-0.8.3}/pyproject.toml +2 -2
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_files/test_dumps.py +1 -1
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_files/test_files.py +7 -7
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_files/test_parsing.py +1 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.devcontainer.json +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.dockerignore +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.git-blame-ignore-revs +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.github/dependabot.yml +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.github/workflows/docker.yml +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.github/workflows/dockerhub-description.yml +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.gitignore +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/.readthedocs.yaml +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/Dockerfile +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/LICENSE.txt +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/README.md +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/benchmark.png +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/Makefile +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/cases.rst +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/conf.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/files.rst +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/index.rst +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/make.bat +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/docs/ruff.toml +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/__init__.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_base.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_run.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_slurm.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_subprocess.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_sync.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_cases/_util.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_files/__init__.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/_files/_io.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/foamlib/py.typed +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/logo.png +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/__init__.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/ruff.toml +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_cases/__init__.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_cases/test_cavity.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_cases/test_cavity_async.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_cases/test_flange.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_cases/test_flange_async.py +0 -0
- {foamlib-0.8.1 → foamlib-0.8.3}/tests/test_files/__init__.py +0 -0
@@ -16,9 +16,9 @@ jobs:
|
|
16
16
|
- name: Checkout
|
17
17
|
uses: actions/checkout@v4
|
18
18
|
- name: Lint with Ruff
|
19
|
-
uses: astral-sh/ruff-action@
|
19
|
+
uses: astral-sh/ruff-action@v3
|
20
20
|
- name: Format with Ruff
|
21
|
-
uses: astral-sh/ruff-action@
|
21
|
+
uses: astral-sh/ruff-action@v3
|
22
22
|
with:
|
23
23
|
args: 'format --check'
|
24
24
|
|
@@ -28,7 +28,7 @@ jobs:
|
|
28
28
|
- name: Checkout
|
29
29
|
uses: actions/checkout@v4
|
30
30
|
- name: Install uv
|
31
|
-
uses: astral-sh/setup-uv@
|
31
|
+
uses: astral-sh/setup-uv@v5
|
32
32
|
- name: Set up Python
|
33
33
|
uses: actions/setup-python@v5
|
34
34
|
with:
|
@@ -74,7 +74,7 @@ jobs:
|
|
74
74
|
with:
|
75
75
|
openfoam-version: ${{ matrix.openfoam-version }}
|
76
76
|
- name: Install uv
|
77
|
-
uses: astral-sh/setup-uv@
|
77
|
+
uses: astral-sh/setup-uv@v5
|
78
78
|
- name: Set up Python ${{ matrix.python-version }}
|
79
79
|
uses: actions/setup-python@v5
|
80
80
|
with:
|
@@ -103,7 +103,7 @@ jobs:
|
|
103
103
|
- name: Checkout
|
104
104
|
uses: actions/checkout@v4
|
105
105
|
- name: Install uv
|
106
|
-
uses: astral-sh/setup-uv@
|
106
|
+
uses: astral-sh/setup-uv@v5
|
107
107
|
- name: Check package build
|
108
108
|
run: |
|
109
109
|
uv build
|
@@ -1,11 +1,12 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.3
|
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
|
7
7
|
Project-URL: Documentation, https://foamlib.readthedocs.io
|
8
8
|
Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
|
9
|
+
License-File: LICENSE.txt
|
9
10
|
Classifier: Development Status :: 4 - Beta
|
10
11
|
Classifier: Framework :: AsyncIO
|
11
12
|
Classifier: Intended Audience :: Developers
|
@@ -27,11 +28,11 @@ Requires-Python: >=3.7
|
|
27
28
|
Requires-Dist: aioshutil<2,>=1
|
28
29
|
Requires-Dist: numpy<3,>=1
|
29
30
|
Requires-Dist: numpy<3,>=1.25.0; python_version >= '3.10'
|
30
|
-
Requires-Dist: pyparsing<4,>=3
|
31
|
+
Requires-Dist: pyparsing<4,>=3.1.2
|
31
32
|
Requires-Dist: typing-extensions<5,>=4; python_version < '3.11'
|
32
33
|
Provides-Extra: dev
|
33
34
|
Requires-Dist: mypy<2,>=1; extra == 'dev'
|
34
|
-
Requires-Dist: pytest-asyncio<0.
|
35
|
+
Requires-Dist: pytest-asyncio<0.26,>=0.21; extra == 'dev'
|
35
36
|
Requires-Dist: pytest-cov; extra == 'dev'
|
36
37
|
Requires-Dist: pytest<9,>=7; extra == 'dev'
|
37
38
|
Requires-Dist: ruff; extra == 'dev'
|
@@ -43,12 +44,12 @@ Requires-Dist: sphinx<9,>=5; extra == 'docs'
|
|
43
44
|
Provides-Extra: lint
|
44
45
|
Requires-Dist: ruff; extra == 'lint'
|
45
46
|
Provides-Extra: test
|
46
|
-
Requires-Dist: pytest-asyncio<0.
|
47
|
+
Requires-Dist: pytest-asyncio<0.26,>=0.21; extra == 'test'
|
47
48
|
Requires-Dist: pytest-cov; extra == 'test'
|
48
49
|
Requires-Dist: pytest<9,>=7; extra == 'test'
|
49
50
|
Provides-Extra: typing
|
50
51
|
Requires-Dist: mypy<2,>=1; extra == 'typing'
|
51
|
-
Requires-Dist: pytest-asyncio<0.
|
52
|
+
Requires-Dist: pytest-asyncio<0.26,>=0.21; extra == 'typing'
|
52
53
|
Requires-Dist: pytest-cov; extra == 'typing'
|
53
54
|
Requires-Dist: pytest<9,>=7; extra == 'typing'
|
54
55
|
Description-Content-Type: text/markdown
|
@@ -106,7 +106,7 @@ class AsyncFoamCase(FoamCaseRunBase):
|
|
106
106
|
async def _rmtree(
|
107
107
|
path: os.PathLike[str] | str, *, ignore_errors: bool = False
|
108
108
|
) -> None:
|
109
|
-
await aioshutil.rmtree(path, ignore_errors=ignore_errors)
|
109
|
+
await aioshutil.rmtree(path, ignore_errors=ignore_errors) # type: ignore [call-arg]
|
110
110
|
|
111
111
|
@staticmethod
|
112
112
|
async def _copytree(
|
@@ -260,6 +260,11 @@ class FoamFile(
|
|
260
260
|
elif shape[1] == 9:
|
261
261
|
self.class_ = "volTensorField"
|
262
262
|
|
263
|
+
if kind == Kind.ASCII_FIELD and self.class_.endswith("scalarField"):
|
264
|
+
kind = Kind.SCALAR_ASCII_FIELD
|
265
|
+
elif kind == Kind.BINARY_FIELD and self.class_.endswith("scalarField"):
|
266
|
+
kind = Kind.SCALAR_BINARY_FIELD
|
267
|
+
|
263
268
|
parsed = self._get_parsed(missing_ok=True)
|
264
269
|
|
265
270
|
start, end = parsed.entry_location(keywords, missing_ok=True)
|
@@ -2,7 +2,6 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import re
|
4
4
|
import sys
|
5
|
-
from enum import Enum, auto
|
6
5
|
from typing import Tuple, Union, cast
|
7
6
|
|
8
7
|
if sys.version_info >= (3, 9):
|
@@ -37,65 +36,31 @@ from pyparsing import (
|
|
37
36
|
printables,
|
38
37
|
)
|
39
38
|
|
40
|
-
from ._types import Data, Dimensioned, DimensionSet, File
|
41
|
-
|
42
|
-
|
43
|
-
class _Tensor(Enum):
|
44
|
-
SCALAR = auto()
|
45
|
-
VECTOR = auto()
|
46
|
-
SYMM_TENSOR = auto()
|
47
|
-
TENSOR = auto()
|
48
|
-
|
49
|
-
@property
|
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
|
60
|
-
|
61
|
-
@property
|
62
|
-
def size(self) -> int:
|
63
|
-
return {
|
64
|
-
_Tensor.SCALAR: 1,
|
65
|
-
_Tensor.VECTOR: 3,
|
66
|
-
_Tensor.SYMM_TENSOR: 6,
|
67
|
-
_Tensor.TENSOR: 9,
|
68
|
-
}[self]
|
69
|
-
|
70
|
-
def pattern(self, *, ignore: Regex | None = None) -> str:
|
71
|
-
float_pattern = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
|
72
|
-
|
73
|
-
if self == _Tensor.SCALAR:
|
74
|
-
return float_pattern
|
75
|
-
|
76
|
-
ignore_pattern = (
|
77
|
-
rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
|
78
|
-
)
|
39
|
+
from ._types import Data, Dimensioned, DimensionSet, File, TensorKind
|
79
40
|
|
80
|
-
return rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{self.size - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
|
81
41
|
|
82
|
-
|
83
|
-
|
84
|
-
return common.ieee_float
|
42
|
+
def _tensor(tensor_kind: TensorKind, *, ignore: Regex | None = None) -> Regex:
|
43
|
+
float_pattern = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
|
85
44
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
45
|
+
if tensor_kind == TensorKind.SCALAR:
|
46
|
+
ret = Regex(float_pattern)
|
47
|
+
ret.add_parse_action(lambda tks: [float(tks[0])])
|
48
|
+
return ret
|
49
|
+
|
50
|
+
ignore_pattern = rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
|
91
51
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
52
|
+
ret = Regex(
|
53
|
+
rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{tensor_kind.size - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
|
54
|
+
)
|
55
|
+
ret.add_parse_action(
|
56
|
+
lambda tks: np.fromstring(
|
57
|
+
re.sub(ignore.re, " ", tks[0][1:-1])
|
58
|
+
if ignore is not None
|
59
|
+
else tks[0][1:-1],
|
60
|
+
sep=" ",
|
61
|
+
)
|
62
|
+
)
|
63
|
+
return ret
|
99
64
|
|
100
65
|
|
101
66
|
def _list_of(entry: ParserElement) -> ParserElement:
|
@@ -118,43 +83,35 @@ def _list_of(entry: ParserElement) -> ParserElement:
|
|
118
83
|
|
119
84
|
|
120
85
|
def _parse_ascii_field(
|
121
|
-
s: str, tensor_kind:
|
86
|
+
s: str, tensor_kind: TensorKind, *, ignore: Regex | None
|
122
87
|
) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64]]:
|
123
88
|
if ignore is not None:
|
124
89
|
s = re.sub(ignore.re, " ", s)
|
125
90
|
s = s.replace("(", " ").replace(")", " ")
|
126
91
|
|
127
|
-
return np.fromstring(s,
|
92
|
+
return np.fromstring(s, sep=" ").reshape(-1, *tensor_kind.shape) # type: ignore [return-value]
|
128
93
|
|
129
94
|
|
130
95
|
def _unpack_binary_field(
|
131
|
-
b: bytes, tensor_kind:
|
96
|
+
b: bytes, tensor_kind: TensorKind, *, length: int
|
132
97
|
) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]:
|
133
98
|
float_size = len(b) / tensor_kind.size / length
|
134
99
|
assert float_size in (4, 8)
|
135
100
|
|
136
101
|
dtype = np.float32 if float_size == 4 else float
|
137
|
-
return np.frombuffer(b, dtype=dtype).reshape(-1, *tensor_kind.shape)
|
102
|
+
return np.frombuffer(b, dtype=dtype).reshape(-1, *tensor_kind.shape) # type: ignore [return-value]
|
138
103
|
|
139
104
|
|
140
105
|
def _tensor_list(
|
141
|
-
tensor_kind:
|
106
|
+
tensor_kind: TensorKind, *, ignore: Regex | None = None
|
142
107
|
) -> ParserElement:
|
143
|
-
|
144
|
-
return (
|
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)
|
149
|
-
)
|
150
|
-
|
151
|
-
tensor_pattern = tensor_kind.pattern(ignore=ignore)
|
108
|
+
tensor = _tensor(tensor_kind, ignore=ignore)
|
152
109
|
ignore_pattern = rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
|
153
110
|
|
154
111
|
list_ = Forward()
|
155
112
|
|
156
113
|
list_ <<= Regex(
|
157
|
-
rf"\((?:{ignore_pattern})?(?:{
|
114
|
+
rf"\((?:{ignore_pattern})?(?:{tensor.re.pattern}{ignore_pattern})*{tensor.re.pattern}(?:{ignore_pattern})?\)"
|
158
115
|
).add_parse_action(
|
159
116
|
lambda tks: [_parse_ascii_field(tks[0], tensor_kind, ignore=ignore)]
|
160
117
|
)
|
@@ -166,7 +123,7 @@ def _tensor_list(
|
|
166
123
|
|
167
124
|
list_ <<= (
|
168
125
|
Regex(
|
169
|
-
rf"\((?:{ignore_pattern})?(?:{
|
126
|
+
rf"\((?:{ignore_pattern})?(?:{tensor.re.pattern}{ignore_pattern}){{{length - 1}}}{tensor.re.pattern}(?:{ignore_pattern})?\)"
|
170
127
|
).add_parse_action(
|
171
128
|
lambda tks: [_parse_ascii_field(tks[0], tensor_kind, ignore=ignore)]
|
172
129
|
)
|
@@ -180,7 +137,7 @@ def _tensor_list(
|
|
180
137
|
]
|
181
138
|
)
|
182
139
|
| (
|
183
|
-
Literal("{").suppress() +
|
140
|
+
Literal("{").suppress() + tensor + Literal("}").suppress()
|
184
141
|
).add_parse_action(
|
185
142
|
lambda tks: [np.full((length, *tensor_kind.shape), tks[0], dtype=float)]
|
186
143
|
)
|
@@ -261,20 +218,27 @@ _DIMENSIONS = (
|
|
261
218
|
Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
|
262
219
|
).set_parse_action(lambda tks: DimensionSet(*tks))
|
263
220
|
_TENSOR = (
|
264
|
-
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
221
|
+
_tensor(TensorKind.SCALAR)
|
222
|
+
| _tensor(TensorKind.VECTOR)
|
223
|
+
| _tensor(TensorKind.SYMM_TENSOR)
|
224
|
+
| _tensor(TensorKind.TENSOR)
|
268
225
|
)
|
269
|
-
_IDENTIFIER =
|
226
|
+
_IDENTIFIER = Forward()
|
227
|
+
_IDENTIFIER <<= Combine(
|
270
228
|
Word(_IDENTCHARS, _IDENTBODYCHARS, exclude_chars="()")
|
271
|
-
+ Opt(Literal("(") +
|
229
|
+
+ Opt(Literal("(") + _IDENTIFIER + Literal(")"))
|
272
230
|
)
|
273
231
|
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
274
232
|
lambda tks: Dimensioned(*reversed(tks.as_list()))
|
275
233
|
)
|
276
234
|
_FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
|
277
|
-
Keyword("nonuniform", _IDENTBODYCHARS).suppress()
|
235
|
+
Keyword("nonuniform", _IDENTBODYCHARS).suppress()
|
236
|
+
+ (
|
237
|
+
_tensor_list(TensorKind.SCALAR, ignore=_COMMENT)
|
238
|
+
| _tensor_list(TensorKind.VECTOR, ignore=_COMMENT)
|
239
|
+
| _tensor_list(TensorKind.SYMM_TENSOR, ignore=_COMMENT)
|
240
|
+
| _tensor_list(TensorKind.TENSOR, ignore=_COMMENT)
|
241
|
+
)
|
278
242
|
)
|
279
243
|
_TOKEN = dbl_quoted_string | _IDENTIFIER
|
280
244
|
_DATA = Forward()
|
@@ -19,7 +19,9 @@ class Kind(Enum):
|
|
19
19
|
DEFAULT = auto()
|
20
20
|
SINGLE_ENTRY = auto()
|
21
21
|
ASCII_FIELD = auto()
|
22
|
+
SCALAR_ASCII_FIELD = auto()
|
22
23
|
BINARY_FIELD = auto()
|
24
|
+
SCALAR_BINARY_FIELD = auto()
|
23
25
|
DIMENSIONS = auto()
|
24
26
|
|
25
27
|
|
@@ -32,7 +34,12 @@ def normalize(data: Entry, *, kind: Kind = Kind.DEFAULT) -> Entry: ...
|
|
32
34
|
|
33
35
|
|
34
36
|
def normalize(data: Entry, *, kind: Kind = Kind.DEFAULT) -> Entry:
|
35
|
-
if kind in (
|
37
|
+
if kind in (
|
38
|
+
Kind.ASCII_FIELD,
|
39
|
+
Kind.SCALAR_ASCII_FIELD,
|
40
|
+
Kind.BINARY_FIELD,
|
41
|
+
Kind.SCALAR_BINARY_FIELD,
|
42
|
+
):
|
36
43
|
if is_sequence(data):
|
37
44
|
try:
|
38
45
|
arr = np.asarray(data)
|
@@ -43,7 +50,7 @@ def normalize(data: Entry, *, kind: Kind = Kind.DEFAULT) -> Entry:
|
|
43
50
|
arr = arr.astype(float)
|
44
51
|
|
45
52
|
if arr.ndim == 1 or (arr.ndim == 2 and arr.shape[1] in (3, 6, 9)):
|
46
|
-
return arr
|
53
|
+
return arr # type: ignore [return-value]
|
47
54
|
|
48
55
|
return data
|
49
56
|
|
@@ -112,11 +119,17 @@ def dumps(
|
|
112
119
|
if isinstance(data, DimensionSet):
|
113
120
|
return b"[" + b" ".join(dumps(v) for v in data) + b"]"
|
114
121
|
|
115
|
-
if kind in (
|
116
|
-
|
117
|
-
|
122
|
+
if kind in (
|
123
|
+
Kind.ASCII_FIELD,
|
124
|
+
Kind.SCALAR_ASCII_FIELD,
|
125
|
+
Kind.BINARY_FIELD,
|
126
|
+
Kind.SCALAR_BINARY_FIELD,
|
127
|
+
) and (isinstance(data, (int, float, np.ndarray))):
|
118
128
|
shape = np.shape(data)
|
119
|
-
if shape
|
129
|
+
if not shape or (
|
130
|
+
kind not in (Kind.SCALAR_ASCII_FIELD, Kind.SCALAR_BINARY_FIELD)
|
131
|
+
and shape in ((3,), (6,), (9,))
|
132
|
+
):
|
120
133
|
return b"uniform " + dumps(data, kind=Kind.SINGLE_ENTRY)
|
121
134
|
|
122
135
|
assert isinstance(data, np.ndarray)
|
@@ -137,10 +150,10 @@ def dumps(
|
|
137
150
|
else:
|
138
151
|
return dumps(data)
|
139
152
|
|
140
|
-
if kind
|
153
|
+
if kind in (Kind.BINARY_FIELD, Kind.SCALAR_BINARY_FIELD):
|
141
154
|
contents = b"(" + data.tobytes() + b")"
|
142
155
|
else:
|
143
|
-
assert kind
|
156
|
+
assert kind in (Kind.ASCII_FIELD, Kind.SCALAR_ASCII_FIELD)
|
144
157
|
contents = dumps(data, kind=Kind.SINGLE_ENTRY)
|
145
158
|
|
146
159
|
return b"nonuniform List<" + tensor_kind + b"> " + dumps(len(data)) + contents
|
@@ -2,7 +2,8 @@ from __future__ import annotations
|
|
2
2
|
|
3
3
|
import sys
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from
|
5
|
+
from enum import Enum
|
6
|
+
from typing import Dict, NamedTuple, Optional, Union
|
6
7
|
|
7
8
|
import numpy as np
|
8
9
|
|
@@ -33,10 +34,43 @@ class DimensionSet(NamedTuple):
|
|
33
34
|
Tensor = Union[
|
34
35
|
float,
|
35
36
|
Sequence[float],
|
36
|
-
"np.ndarray[tuple[()] |
|
37
|
+
"np.ndarray[tuple[()] | tuple[int], np.dtype[np.float64]]",
|
37
38
|
]
|
38
39
|
|
39
40
|
|
41
|
+
class TensorKind(Enum):
|
42
|
+
SCALAR = ()
|
43
|
+
VECTOR = (3,)
|
44
|
+
SYMM_TENSOR = (6,)
|
45
|
+
TENSOR = (9,)
|
46
|
+
|
47
|
+
@property
|
48
|
+
def shape(self) -> tuple[()] | tuple[int]:
|
49
|
+
shape: tuple[()] | tuple[int] = self.value
|
50
|
+
return shape
|
51
|
+
|
52
|
+
@property
|
53
|
+
def size(self) -> int:
|
54
|
+
return int(np.prod(self.shape))
|
55
|
+
|
56
|
+
def __str__(self) -> str:
|
57
|
+
return {
|
58
|
+
TensorKind.SCALAR: "scalar",
|
59
|
+
TensorKind.VECTOR: "vector",
|
60
|
+
TensorKind.SYMM_TENSOR: "symmTensor",
|
61
|
+
TensorKind.TENSOR: "tensor",
|
62
|
+
}[self]
|
63
|
+
|
64
|
+
@staticmethod
|
65
|
+
def from_shape(shape: tuple[int, ...]) -> TensorKind:
|
66
|
+
for kind in TensorKind:
|
67
|
+
if kind.shape == shape:
|
68
|
+
return kind
|
69
|
+
|
70
|
+
msg = f"No tensor kind for shape {shape!r}"
|
71
|
+
raise ValueError(msg)
|
72
|
+
|
73
|
+
|
40
74
|
@dataclass
|
41
75
|
class Dimensioned:
|
42
76
|
value: Tensor = 0
|
@@ -45,7 +79,7 @@ class Dimensioned:
|
|
45
79
|
|
46
80
|
def __post_init__(self) -> None:
|
47
81
|
if is_sequence(self.value):
|
48
|
-
self.value = np.asarray(self.value, dtype=float)
|
82
|
+
self.value = np.asarray(self.value, dtype=float) # type: ignore [assignment]
|
49
83
|
else:
|
50
84
|
assert isinstance(self.value, (int, float, np.ndarray))
|
51
85
|
self.value = float(self.value)
|
@@ -32,7 +32,7 @@ dependencies = [
|
|
32
32
|
"aioshutil>=1,<2",
|
33
33
|
"numpy>=1.25.0,<3; python_version>='3.10'",
|
34
34
|
"numpy>=1,<3",
|
35
|
-
"pyparsing>=3,<4",
|
35
|
+
"pyparsing>=3.1.2,<4",
|
36
36
|
"typing-extensions>=4,<5; python_version<'3.11'",
|
37
37
|
]
|
38
38
|
|
@@ -42,7 +42,7 @@ dynamic = ["version"]
|
|
42
42
|
lint = ["ruff"]
|
43
43
|
test = [
|
44
44
|
"pytest>=7,<9",
|
45
|
-
"pytest-asyncio>=0.21,<0.
|
45
|
+
"pytest-asyncio>=0.21,<0.26",
|
46
46
|
"pytest-cov",
|
47
47
|
]
|
48
48
|
typing = [
|
@@ -37,7 +37,7 @@ def test_serialize_data() -> None:
|
|
37
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@)"
|
38
38
|
)
|
39
39
|
assert (
|
40
|
-
dumps(np.array([1, 2], dtype=np.float32), kind=Kind.BINARY_FIELD)
|
40
|
+
dumps(np.array([1, 2], dtype=np.float32), kind=Kind.BINARY_FIELD) # type: ignore [arg-type]
|
41
41
|
== b"nonuniform List<scalar> 2(\x00\x00\x80?\x00\x00\x00@)"
|
42
42
|
)
|
43
43
|
assert (
|
@@ -163,19 +163,19 @@ def test_internal_field(cavity: FoamCase) -> None:
|
|
163
163
|
p_arr = np.zeros(size)
|
164
164
|
U_arr = np.zeros((size, 3))
|
165
165
|
|
166
|
-
cavity[0]["p"].internal_field = p_arr
|
167
|
-
cavity[0]["U"].internal_field = U_arr
|
166
|
+
cavity[0]["p"].internal_field = p_arr # type: ignore [assignment]
|
167
|
+
cavity[0]["U"].internal_field = U_arr # type: ignore [assignment]
|
168
168
|
|
169
169
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
170
170
|
U = cavity[0]["U"].internal_field
|
171
171
|
assert isinstance(U, np.ndarray)
|
172
172
|
assert U_arr == pytest.approx(U)
|
173
173
|
|
174
|
-
p_arr = np.arange(size) * 1e-6
|
174
|
+
p_arr = np.arange(size) * 1e-6 # type: ignore [assignment]
|
175
175
|
U_arr = np.full((size, 3), [-1e-6, 1e-6, 0]) * np.arange(size)[:, np.newaxis]
|
176
176
|
|
177
|
-
cavity[0]["p"].internal_field = p_arr
|
178
|
-
cavity[0]["U"].internal_field = U_arr
|
177
|
+
cavity[0]["p"].internal_field = p_arr # type: ignore [assignment]
|
178
|
+
cavity[0]["U"].internal_field = U_arr # type: ignore [assignment]
|
179
179
|
|
180
180
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
181
181
|
U = cavity[0]["U"].internal_field
|
@@ -210,7 +210,7 @@ def test_binary_field(cavity: FoamCase) -> None:
|
|
210
210
|
p_arr = np.arange(len(p_bin)) * 1e-6
|
211
211
|
U_arr = np.full_like(U_bin, [-1e-6, 1e-6, 0]) * np.arange(len(U_bin))[:, np.newaxis]
|
212
212
|
|
213
|
-
cavity[0]["p"].internal_field = p_arr
|
213
|
+
cavity[0]["p"].internal_field = p_arr # type: ignore [assignment]
|
214
214
|
cavity[0]["U"].internal_field = U_arr
|
215
215
|
|
216
216
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
@@ -237,7 +237,7 @@ def test_compressed_field(cavity: FoamCase) -> None:
|
|
237
237
|
p_arr = np.arange(len(p_bin)) * 1e-6
|
238
238
|
U_arr = np.full_like(U_bin, [-1e-6, 1e-6, 0]) * np.arange(len(U_bin))[:, np.newaxis]
|
239
239
|
|
240
|
-
cavity[0]["p"].internal_field = p_arr
|
240
|
+
cavity[0]["p"].internal_field = p_arr # type: ignore [assignment]
|
241
241
|
cavity[0]["U"].internal_field = U_arr
|
242
242
|
|
243
243
|
assert cavity[0]["p"].internal_field == pytest.approx(p_arr)
|
@@ -92,6 +92,7 @@ def test_parse_value() -> None:
|
|
92
92
|
assert Parsed(b"({a b; c d;} {e g;})")[()] == [{"a": "b", "c": "d"}, {"e": "g"}]
|
93
93
|
assert Parsed(b"(water oil mercury air)")[()] == ["water", "oil", "mercury", "air"]
|
94
94
|
assert Parsed(b"div(phi,U)")[()] == "div(phi,U)"
|
95
|
+
assert Parsed(b"div(nuEff*dev(T(grad(U))))")[()] == "div(nuEff*dev(T(grad(U))))"
|
95
96
|
assert Parsed(b"((air and water) { type constant; sigma 0.07; })")[()] == [
|
96
97
|
(["air", "and", "water"], {"type": "constant", "sigma": 0.07})
|
97
98
|
]
|
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
|