foamlib 0.8.11__tar.gz → 0.9.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.
Files changed (63) hide show
  1. {foamlib-0.8.11 → foamlib-0.9.1}/PKG-INFO +1 -1
  2. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/__init__.py +1 -1
  3. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_files/_parsing.py +206 -110
  4. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_files/_types.py +0 -34
  5. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_files.py +5 -6
  6. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_advanced.py +22 -14
  7. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_basic.py +41 -18
  8. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_decompose_par.py +2 -1
  9. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_intermediate.py +125 -100
  10. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_poly_mesh.py +12 -11
  11. {foamlib-0.8.11 → foamlib-0.9.1}/.devcontainer.json +0 -0
  12. {foamlib-0.8.11 → foamlib-0.9.1}/.dockerignore +0 -0
  13. {foamlib-0.8.11 → foamlib-0.9.1}/.git-blame-ignore-revs +0 -0
  14. {foamlib-0.8.11 → foamlib-0.9.1}/.github/dependabot.yml +0 -0
  15. {foamlib-0.8.11 → foamlib-0.9.1}/.github/workflows/ci.yml +0 -0
  16. {foamlib-0.8.11 → foamlib-0.9.1}/.github/workflows/docker.yml +0 -0
  17. {foamlib-0.8.11 → foamlib-0.9.1}/.github/workflows/dockerhub-description.yml +0 -0
  18. {foamlib-0.8.11 → foamlib-0.9.1}/.github/workflows/pypi-publish.yml +0 -0
  19. {foamlib-0.8.11 → foamlib-0.9.1}/.gitignore +0 -0
  20. {foamlib-0.8.11 → foamlib-0.9.1}/.readthedocs.yaml +0 -0
  21. {foamlib-0.8.11 → foamlib-0.9.1}/CONTRIBUTING.md +0 -0
  22. {foamlib-0.8.11 → foamlib-0.9.1}/Dockerfile +0 -0
  23. {foamlib-0.8.11 → foamlib-0.9.1}/LICENSE.txt +0 -0
  24. {foamlib-0.8.11 → foamlib-0.9.1}/README.md +0 -0
  25. {foamlib-0.8.11 → foamlib-0.9.1}/benchmark/benchmark.png +0 -0
  26. {foamlib-0.8.11 → foamlib-0.9.1}/benchmark/benchmark.py +0 -0
  27. {foamlib-0.8.11 → foamlib-0.9.1}/benchmark/requirements.txt +0 -0
  28. {foamlib-0.8.11 → foamlib-0.9.1}/benchmark/ruff.toml +0 -0
  29. {foamlib-0.8.11 → foamlib-0.9.1}/docs/Makefile +0 -0
  30. {foamlib-0.8.11 → foamlib-0.9.1}/docs/cases.rst +0 -0
  31. {foamlib-0.8.11 → foamlib-0.9.1}/docs/conf.py +0 -0
  32. {foamlib-0.8.11 → foamlib-0.9.1}/docs/files.rst +0 -0
  33. {foamlib-0.8.11 → foamlib-0.9.1}/docs/index.rst +0 -0
  34. {foamlib-0.8.11 → foamlib-0.9.1}/docs/make.bat +0 -0
  35. {foamlib-0.8.11 → foamlib-0.9.1}/docs/ruff.toml +0 -0
  36. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/__init__.py +0 -0
  37. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_async.py +0 -0
  38. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_base.py +0 -0
  39. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_run.py +0 -0
  40. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_slurm.py +0 -0
  41. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_subprocess.py +0 -0
  42. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_sync.py +0 -0
  43. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_cases/_util.py +0 -0
  44. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_files/__init__.py +0 -0
  45. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_files/_files.py +0 -0
  46. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_files/_io.py +0 -0
  47. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/_files/_serialization.py +0 -0
  48. {foamlib-0.8.11 → foamlib-0.9.1}/foamlib/py.typed +0 -0
  49. {foamlib-0.8.11 → foamlib-0.9.1}/logo.png +0 -0
  50. {foamlib-0.8.11 → foamlib-0.9.1}/pyproject.toml +0 -0
  51. {foamlib-0.8.11 → foamlib-0.9.1}/tests/__init__.py +0 -0
  52. {foamlib-0.8.11 → foamlib-0.9.1}/tests/ruff.toml +0 -0
  53. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_cases/__init__.py +0 -0
  54. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_cases/test_cavity.py +0 -0
  55. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_cases/test_cavity_async.py +0 -0
  56. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_cases/test_flange.py +0 -0
  57. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_cases/test_flange_async.py +0 -0
  58. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_example.py +0 -0
  59. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/__init__.py +0 -0
  60. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_dumps.py +0 -0
  61. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/__init__.py +0 -0
  62. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_fields.py +0 -0
  63. {foamlib-0.8.11 → foamlib-0.9.1}/tests/test_files/test_parsing/test_fv_schemes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foamlib
3
- Version: 0.8.11
3
+ Version: 0.9.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
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.8.11"
3
+ __version__ = "0.9.1"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import re
4
4
  import sys
5
- from typing import Tuple, Union, cast
5
+ from typing import TYPE_CHECKING, Tuple, Union, cast
6
6
 
7
7
  if sys.version_info >= (3, 9):
8
8
  from collections.abc import Iterator, Mapping, MutableMapping, Sequence
@@ -25,6 +25,7 @@ from pyparsing import (
25
25
  LineEnd,
26
26
  Literal,
27
27
  Located,
28
+ NoMatch,
28
29
  Opt,
29
30
  ParserElement,
30
31
  ParseResults,
@@ -37,131 +38,172 @@ from pyparsing import (
37
38
  printables,
38
39
  )
39
40
 
40
- from ._types import Data, Dimensioned, DimensionSet, File, TensorKind
41
+ from ._types import Data, Dimensioned, DimensionSet, File
41
42
 
43
+ if TYPE_CHECKING:
44
+ from numpy.typing import DTypeLike
42
45
 
43
- def _tensor(tensor_kind: TensorKind, *, ignore: Regex | None = None) -> Regex:
44
- float_pattern = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
45
46
 
46
- if tensor_kind == TensorKind.SCALAR:
47
- ret = Regex(float_pattern)
48
- ret.add_parse_action(lambda tks: [float(tks[0])])
49
- return ret
50
-
51
- ignore_pattern = rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
47
+ def _ascii_numeric_list(
48
+ dtype: DTypeLike,
49
+ *,
50
+ nested: int | None = None,
51
+ ignore: Regex | None = None,
52
+ empty_ok: bool = False,
53
+ ) -> ParserElement:
54
+ dtype = np.dtype(dtype)
55
+
56
+ if np.issubdtype(dtype, np.floating):
57
+ element = common.ieee_float
58
+ element_pattern = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
59
+ elif np.issubdtype(dtype, np.integer):
60
+ element = common.integer
61
+ element_pattern = r"(?:-?\d+)"
62
+ else:
63
+ msg = f"Unsupported dtype: {dtype}"
64
+ raise TypeError(msg)
52
65
 
53
- ret = Regex(
54
- rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{tensor_kind.size - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
66
+ spacing_pattern = (
67
+ rf"(?:(?:\s|{ignore.re.pattern})+)" if ignore is not None else r"(?:\s+)"
55
68
  )
56
- ret.add_parse_action(
57
- lambda tks: np.fromstring(
58
- re.sub(ignore.re, " ", tks[0][1:-1])
59
- if ignore is not None
60
- else tks[0][1:-1],
61
- sep=" ",
69
+
70
+ if nested is not None:
71
+ element = (
72
+ Literal("(").suppress() + Group(element[nested]) + Literal(")").suppress()
62
73
  )
63
- )
64
- return ret
74
+ element_pattern = rf"(?:{spacing_pattern}?\({element_pattern}?(?:{element_pattern}{spacing_pattern}){{{nested - 1}}}{element_pattern}{spacing_pattern}?\))"
65
75
 
76
+ list_ = Forward()
66
77
 
67
- def _list_of(entry: ParserElement) -> ParserElement:
68
- return Opt(
69
- Literal("List") + Literal("<") + _IDENTIFIER + Literal(">")
70
- ).suppress() + (
71
- (
72
- counted_array(entry, common.integer + Literal("(").suppress())
73
- + Literal(")").suppress()
74
- ).set_parse_action(lambda tks: [tks.as_list()])
75
- | (
76
- Literal("(").suppress()
77
- + Group((entry)[...], aslist=True)
78
- + Literal(")").suppress()
78
+ def process_count(tks: ParseResults) -> None:
79
+ nonlocal list_
80
+
81
+ if not tks:
82
+ count = None
83
+ else:
84
+ (count,) = tks
85
+ assert isinstance(count, int)
86
+
87
+ if count is None:
88
+ if not empty_ok:
89
+ list_pattern = rf"\({spacing_pattern}?(?:{element_pattern}{spacing_pattern})*{element_pattern}{spacing_pattern}?\)"
90
+ else:
91
+ list_pattern = rf"\({spacing_pattern}?(?:{element_pattern}{spacing_pattern})*{element_pattern}?{spacing_pattern}?\)"
92
+
93
+ elif count == 0:
94
+ if not empty_ok:
95
+ list_ <<= NoMatch()
96
+ else:
97
+ list_ <<= (Literal("(") + Literal(")")).add_parse_action(
98
+ lambda: np.empty((0, nested) if nested else 0, dtype=dtype)
99
+ )
100
+ return
101
+
102
+ else:
103
+ list_pattern = rf"\({spacing_pattern}?(?:{element_pattern}{spacing_pattern}){{{count - 1}}}{element_pattern}{spacing_pattern}?\)"
104
+
105
+ list_ <<= Regex(list_pattern).add_parse_action(
106
+ lambda tks: to_array(tks, dtype=dtype)
79
107
  )
80
- | (
81
- common.integer + Literal("{").suppress() + entry + Literal("}").suppress()
82
- ).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
83
- )
84
108
 
109
+ def to_array(
110
+ tks: ParseResults, *, dtype: DTypeLike
111
+ ) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.integer | np.floating]]:
112
+ (s,) = tks
113
+ assert s.startswith("(")
114
+ assert s.endswith(")")
115
+ s = s[1:-1]
116
+ if ignore is not None:
117
+ s = re.sub(ignore.re, " ", s)
118
+ if nested is not None:
119
+ s = s.replace("(", " ").replace(")", " ")
120
+
121
+ ret: np.ndarray[
122
+ tuple[int] | tuple[int, int], np.dtype[np.integer | np.floating]
123
+ ] = np.fromstring(s, sep=" ", dtype=dtype) # type: ignore[assignment]
124
+
125
+ if nested is not None:
126
+ ret = ret.reshape(-1, nested)
85
127
 
86
- def _parse_ascii_field(
87
- s: str, tensor_kind: TensorKind, *, ignore: Regex | None
88
- ) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64]]:
89
- if ignore is not None:
90
- s = re.sub(ignore.re, " ", s)
91
- s = s.replace("(", " ").replace(")", " ")
128
+ return ret
129
+
130
+ def to_full_array(
131
+ tks: ParseResults, *, dtype: type
132
+ ) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.integer | np.floating]]:
133
+ count, lst = tks
134
+ assert isinstance(count, int)
92
135
 
93
- return np.fromstring(s, sep=" ").reshape(-1, *tensor_kind.shape) # type: ignore [return-value]
136
+ if nested is None:
137
+ return np.full(count, lst, dtype=dtype)
94
138
 
139
+ return np.full((count, nested), lst, dtype=dtype) # type: ignore[return-value]
95
140
 
96
- def _unpack_binary_field(
97
- b: bytes, tensor_kind: TensorKind, *, length: int
98
- ) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]:
99
- float_size = len(b) / tensor_kind.size / length
100
- assert float_size in (4, 8)
141
+ ret = ((Opt(common.integer).add_parse_action(process_count)).suppress() + list_) | (
142
+ common.integer + Literal("{").suppress() + element + Literal("}").suppress()
143
+ ).add_parse_action(lambda tks: to_full_array(tks, dtype=float))
144
+
145
+ if ignore is not None:
146
+ ret.ignore(ignore)
101
147
 
102
- dtype = np.float32 if float_size == 4 else float
103
- return np.frombuffer(b, dtype=dtype).reshape(-1, *tensor_kind.shape) # type: ignore [return-value]
148
+ return ret
104
149
 
105
150
 
106
- def _tensor_list(
107
- tensor_kind: TensorKind, *, ignore: Regex | None = None
151
+ def _binary_numeric_list(
152
+ dtype: DTypeLike, *, nested: int | None = None, empty_ok: bool = False
108
153
  ) -> ParserElement:
109
- tensor = _tensor(tensor_kind, ignore=ignore)
110
- ignore_pattern = rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
154
+ dtype = np.dtype(dtype)
111
155
 
112
- list_ = Forward()
156
+ elsize = nested if nested is not None else 1
113
157
 
114
- list_ <<= Regex(
115
- rf"\((?:{ignore_pattern})?(?:{tensor.re.pattern}{ignore_pattern})*{tensor.re.pattern}(?:{ignore_pattern})?\)"
116
- ).add_parse_action(
117
- lambda tks: [_parse_ascii_field(tks[0], tensor_kind, ignore=ignore)]
118
- ) | (
119
- (Literal("(") + Literal(")"))
120
- .suppress()
121
- .add_parse_action(lambda: np.array([]).reshape(0, *tensor_kind.shape))
122
- )
158
+ list_ = Forward()
123
159
 
124
- def count_parse_action(tks: ParseResults) -> None:
160
+ def process_count(tks: ParseResults) -> None:
125
161
  nonlocal list_
126
- length = tks[0]
127
- assert isinstance(length, int)
128
-
129
- if not length:
130
- list_ <<= (
131
- ((Literal("(") + Literal(")")) | (Literal("{") + Literal("}")))
132
- .suppress()
133
- .add_parse_action(lambda: np.array([]).reshape(0, *tensor_kind.shape))
134
- )
162
+ (size,) = tks
163
+ assert isinstance(size, int)
164
+
165
+ if size == 0 and not empty_ok:
166
+ list_ <<= NoMatch()
135
167
  return
136
168
 
137
- list_ <<= (
138
- Regex(
139
- rf"\((?:{ignore_pattern})?(?:{tensor.re.pattern}{ignore_pattern}){{{length - 1}}}{tensor.re.pattern}(?:{ignore_pattern})?\)"
140
- ).add_parse_action(
141
- lambda tks: [_parse_ascii_field(tks[0], tensor_kind, ignore=ignore)]
142
- )
143
- | Regex(
144
- rf"\((?s:({'.' * 8 * tensor_kind.size}|{'.' * 4 * tensor_kind.size}){{{length}}})\)"
145
- ).add_parse_action(
146
- lambda tks: [
147
- _unpack_binary_field(
148
- tks[0][1:-1].encode("latin-1"), tensor_kind, length=length
149
- )
150
- ]
151
- )
152
- | (
153
- Literal("{").suppress() + tensor + Literal("}").suppress()
154
- ).add_parse_action(
155
- lambda tks: [np.full((length, *tensor_kind.shape), tks[0], dtype=float)]
156
- )
157
- )
169
+ list_ <<= Regex(rf"\((?s:{'.' * dtype.itemsize * elsize}){{{size}}}\)")
158
170
 
159
- count = common.integer.copy().add_parse_action(count_parse_action)
171
+ def to_array(
172
+ tks: ParseResults,
173
+ ) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.integer | np.floating]]:
174
+ size, s = tks
175
+ assert isinstance(size, int)
176
+ assert isinstance(s, str)
177
+ assert s[0] == "("
178
+ assert s[-1] == ")"
179
+ s = s[1:-1]
180
+
181
+ ret = np.frombuffer(s.encode("latin-1"), dtype=dtype)
182
+
183
+ if nested is not None:
184
+ ret = ret.reshape(-1, nested)
185
+
186
+ return ret # type: ignore[return-value]
160
187
 
161
188
  return (
162
- Opt(Literal("List") + Literal("<") + str(tensor_kind) + Literal(">")).suppress()
163
- + Opt(count).suppress()
164
- + list_
189
+ common.integer.copy().add_parse_action(process_count) + list_
190
+ ).add_parse_action(to_array)
191
+
192
+
193
+ def _list_of(entry: ParserElement) -> ParserElement:
194
+ return (
195
+ (
196
+ counted_array(entry, common.integer + Literal("(").suppress())
197
+ + Literal(")").suppress()
198
+ ).set_parse_action(lambda tks: [tks.as_list()])
199
+ | (
200
+ Literal("(").suppress()
201
+ + Group((entry)[...], aslist=True)
202
+ + Literal(")").suppress()
203
+ )
204
+ | (
205
+ common.integer + Literal("{").suppress() + entry + Literal("}").suppress()
206
+ ).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
165
207
  )
166
208
 
167
209
 
@@ -250,12 +292,11 @@ _SWITCH = (
250
292
  _DIMENSIONS = (
251
293
  Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
252
294
  ).set_parse_action(lambda tks: DimensionSet(*tks))
253
- _TENSOR = (
254
- _tensor(TensorKind.SCALAR)
255
- | _tensor(TensorKind.VECTOR)
256
- | _tensor(TensorKind.SYMM_TENSOR)
257
- | _tensor(TensorKind.TENSOR)
258
- )
295
+ _TENSOR = common.ieee_float | (
296
+ Literal("(").suppress()
297
+ + Group(common.ieee_float[3] | common.ieee_float[6] | common.ieee_float[9])
298
+ + Literal(")").suppress()
299
+ ).add_parse_action(lambda tks: np.array(tks[0], dtype=float))
259
300
  _PARENTHESIZED = Forward()
260
301
  _IDENTIFIER = Combine(Word(_IDENTCHARS, _IDENTBODYCHARS) + Opt(_PARENTHESIZED))
261
302
  _PARENTHESIZED <<= Combine(
@@ -270,12 +311,55 @@ _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
270
311
  _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
271
312
  Keyword("nonuniform", _IDENTBODYCHARS).suppress()
272
313
  + (
273
- _tensor_list(TensorKind.SCALAR, ignore=_COMMENT)
274
- | _tensor_list(TensorKind.VECTOR, ignore=_COMMENT)
275
- | _tensor_list(TensorKind.SYMM_TENSOR, ignore=_COMMENT)
276
- | _tensor_list(TensorKind.TENSOR, ignore=_COMMENT)
314
+ (
315
+ Opt(
316
+ Literal("List") + Literal("<") + Literal("scalar") + Literal(">")
317
+ ).suppress()
318
+ + (
319
+ _ascii_numeric_list(dtype=float, ignore=_COMMENT, empty_ok=True)
320
+ | _binary_numeric_list(dtype=np.float64, empty_ok=True)
321
+ | _binary_numeric_list(dtype=np.float32, empty_ok=True)
322
+ )
323
+ )
324
+ | (
325
+ Opt(
326
+ Literal("List") + Literal("<") + Literal("vector") + Literal(">")
327
+ ).suppress()
328
+ + (
329
+ _ascii_numeric_list(
330
+ dtype=float, nested=3, ignore=_COMMENT, empty_ok=True
331
+ )
332
+ | _binary_numeric_list(np.float64, nested=3, empty_ok=True)
333
+ | _binary_numeric_list(np.float32, nested=3, empty_ok=True)
334
+ )
335
+ )
336
+ | (
337
+ Opt(
338
+ Literal("List") + Literal("<") + Literal("symmTensor") + Literal(">")
339
+ ).suppress()
340
+ + (
341
+ _ascii_numeric_list(
342
+ dtype=float, nested=6, ignore=_COMMENT, empty_ok=True
343
+ )
344
+ | _binary_numeric_list(np.float64, nested=6, empty_ok=True)
345
+ | _binary_numeric_list(np.float32, nested=6, empty_ok=True)
346
+ )
347
+ )
348
+ | (
349
+ Opt(
350
+ Literal("List") + Literal("<") + Literal("tensor") + Literal(">")
351
+ ).suppress()
352
+ + (
353
+ _ascii_numeric_list(
354
+ dtype=float, nested=9, ignore=_COMMENT, empty_ok=True
355
+ )
356
+ | _binary_numeric_list(np.float64, nested=9, empty_ok=True)
357
+ | _binary_numeric_list(np.float32, nested=9, empty_ok=True)
358
+ )
359
+ )
277
360
  )
278
361
  )
362
+
279
363
  _DIRECTIVE = Word("#", _IDENTBODYCHARS)
280
364
  _TOKEN = dbl_quoted_string | _DIRECTIVE | _IDENTIFIER
281
365
  _DATA = Forward()
@@ -319,7 +403,19 @@ _LOCATED_DICTIONARY = Group(
319
403
  located=True,
320
404
  )
321
405
  )[...]
322
- _LOCATED_DATA = Group(Located(_DATA.copy().add_parse_action(lambda tks: ["", tks[0]])))
406
+ _LOCATED_DATA = Group(
407
+ Located(
408
+ (
409
+ _ascii_numeric_list(dtype=int, ignore=_COMMENT)
410
+ | _binary_numeric_list(dtype=np.int64)
411
+ | _binary_numeric_list(dtype=np.int32)
412
+ | _ascii_numeric_list(dtype=float, nested=3, ignore=_COMMENT)
413
+ | _binary_numeric_list(dtype=np.float64, nested=3)
414
+ | _binary_numeric_list(dtype=np.float32, nested=3)
415
+ | _DATA
416
+ ).add_parse_action(lambda tks: ["", tks[0]])
417
+ )
418
+ )
323
419
 
324
420
  _FILE = (
325
421
  Dict(_LOCATED_DICTIONARY + Opt(_LOCATED_DATA) + _LOCATED_DICTIONARY)
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import sys
4
- from enum import Enum
5
4
  from typing import Dict, NamedTuple, Optional, Union
6
5
 
7
6
  import numpy as np
@@ -42,39 +41,6 @@ TensorLike = Union[
42
41
  ]
43
42
 
44
43
 
45
- class TensorKind(Enum):
46
- SCALAR = ()
47
- VECTOR = (3,)
48
- SYMM_TENSOR = (6,)
49
- TENSOR = (9,)
50
-
51
- @property
52
- def shape(self) -> tuple[()] | tuple[int]:
53
- shape: tuple[()] | tuple[int] = self.value
54
- return shape
55
-
56
- @property
57
- def size(self) -> int:
58
- return int(np.prod(self.shape))
59
-
60
- def __str__(self) -> str:
61
- return {
62
- TensorKind.SCALAR: "scalar",
63
- TensorKind.VECTOR: "vector",
64
- TensorKind.SYMM_TENSOR: "symmTensor",
65
- TensorKind.TENSOR: "tensor",
66
- }[self]
67
-
68
- @staticmethod
69
- def from_shape(shape: tuple[int, ...]) -> TensorKind:
70
- for kind in TensorKind:
71
- if kind.shape == shape:
72
- return kind
73
-
74
- msg = f"No tensor kind for shape {shape!r}"
75
- raise ValueError(msg)
76
-
77
-
78
44
  class Dimensioned:
79
45
  def __init__(
80
46
  self,
@@ -3,9 +3,9 @@ import sys
3
3
  from pathlib import Path
4
4
 
5
5
  if sys.version_info >= (3, 9):
6
- from collections.abc import Generator, Sequence
6
+ from collections.abc import Generator
7
7
  else:
8
- from typing import Generator, Sequence
8
+ from typing import Generator
9
9
 
10
10
  import numpy as np
11
11
  import pytest
@@ -153,16 +153,15 @@ def test_mesh(cavity: FoamCase) -> None:
153
153
 
154
154
  points = file[None]
155
155
 
156
- assert isinstance(points, Sequence)
157
- assert isinstance(points[0], Sequence)
158
- assert len(points[0]) == 3
156
+ assert isinstance(points, np.ndarray)
157
+ assert points.ndim == 2
158
+ assert points.shape[-1] == 3
159
159
 
160
160
 
161
161
  def test_internal_field(cavity: FoamCase) -> None:
162
162
  blocks = cavity.block_mesh_dict["blocks"]
163
163
  assert isinstance(blocks, list)
164
164
  sizes = blocks[2]
165
- assert isinstance(sizes, list)
166
165
  size = np.prod(sizes)
167
166
 
168
167
  p_arr = np.zeros(size)
@@ -1,5 +1,6 @@
1
1
  # Based on https://foss.heptapod.net/fluiddyn/fluidsimfoam/-/blob/branch/default/tests/test_parser_advanced.py
2
2
 
3
+ import numpy as np
3
4
  import pytest
4
5
  from foamlib._files._parsing import Parsed
5
6
 
@@ -276,9 +277,10 @@ def test_assignment_strange_name() -> None:
276
277
  """
277
278
  )
278
279
  assert parsed[("equations", '"(U|e|k).*"')] == 0.7
279
- assert parsed[("equations", '"(U|e|k|epsilon).*"')] == (
280
- "table",
281
- [[0, 0.4], [0.5, 0.7]],
280
+ assert isinstance(parsed[("equations", '"(U|e|k|epsilon).*"')], tuple)
281
+ assert parsed[("equations", '"(U|e|k|epsilon).*"')][0] == "table"
282
+ assert np.array_equal(
283
+ parsed[("equations", '"(U|e|k|epsilon).*"')][1], [[0, 0.4], [0.5, 0.7]]
282
284
  )
283
285
 
284
286
 
@@ -331,17 +333,23 @@ def test_list_u() -> None:
331
333
  )
332
334
  """
333
335
  )
334
- assert parsed[()] == [
335
- [4.50773, 1.79963, 0.0],
336
- [6.06208, 2.40831, 0.0],
337
- [6.874, 2.72079, 0.0],
338
- [7.42929, 2.931, 0.0],
339
- [7.85095, 3.08805, 0.0],
340
- [8.19202, 3.21306, 0.0],
341
- [17.5, 1.92559e-09, 0.0],
342
- [17.5, 6.81045e-12, 0.0],
343
- [17.5, 6.81045e-12, 0.0],
344
- ]
336
+ data = parsed[()]
337
+ assert isinstance(data, np.ndarray)
338
+ assert data.shape == (9, 3)
339
+ assert np.array_equal(
340
+ data,
341
+ [
342
+ [4.507730000e00, 1.799630000e00, 0.000000000e00],
343
+ [6.062080000e00, 2.408310000e00, 0.000000000e00],
344
+ [6.874000000e00, 2.720790000e00, 0.000000000e00],
345
+ [7.429290000e00, 2.931000000e00, 0.000000000e00],
346
+ [7.850950000e00, 3.088050000e00, 0.000000000e00],
347
+ [8.192020000e00, 3.213060000e00, 0.000000000e00],
348
+ [1.750000000e01, 1.925590000e-09, 0.000000000e00],
349
+ [1.750000000e01, 6.810450000e-12, 0.000000000e00],
350
+ [1.750000000e01, 6.810450000e-12, 0.000000000e00],
351
+ ],
352
+ )
345
353
 
346
354
 
347
355
  def test_list_as_write_cell_centers() -> None:
@@ -1,6 +1,7 @@
1
1
  import numpy as np
2
2
  from foamlib import FoamFile
3
3
  from foamlib._files._parsing import Parsed
4
+ from foamlib._files._types import is_sequence
4
5
 
5
6
 
6
7
  def test_parse_value() -> None:
@@ -16,7 +17,12 @@ def test_parse_value() -> None:
16
17
  assert Parsed(b"uniform 1")[()] == 1
17
18
  assert Parsed(b"uniform 1.0")[()] == 1.0
18
19
  assert Parsed(b"uniform 1.0e-3")[()] == 1.0e-3
19
- assert Parsed(b"(1.0 2.0 3.0)")[()] == [1.0, 2.0, 3.0]
20
+ assert Parsed(b"(word word)")[()] == ["word", "word"]
21
+ lst = Parsed(b"(1 2 3)")[()]
22
+ assert np.array_equal(lst, [1, 2, 3]) # type: ignore[arg-type]
23
+ lst = Parsed(b"(1.0 2 3)")[()]
24
+ assert np.array_equal(lst, [1.0, 2.0, 3.0]) # type: ignore[arg-type]
25
+ assert Parsed(b"()")[()] == []
20
26
  field = Parsed(b"uniform (1 2 3)")[()]
21
27
  assert isinstance(field, np.ndarray)
22
28
  assert np.array_equal(field, [1, 2, 3])
@@ -32,15 +38,28 @@ def test_parse_value() -> None:
32
38
  field = Parsed(b"nonuniform List<tensor> ()")[()]
33
39
  assert isinstance(field, np.ndarray)
34
40
  assert field.shape == (0, 9)
35
- assert Parsed(b"3(1 2 3)")[()] == [1, 2, 3]
36
- assert Parsed(b"2((1 2 3) (4 5 6))")[()] == [
37
- [1, 2, 3],
38
- [4, 5, 6],
39
- ]
40
- assert Parsed(b"2{(1 2 3)}")[()] == [
41
- [1, 2, 3],
42
- [1, 2, 3],
43
- ]
41
+ lst = Parsed(b"3(1 2 3)")[()]
42
+ assert isinstance(lst, np.ndarray)
43
+ assert np.array_equal(lst, [1, 2, 3])
44
+ lst = Parsed(b"2((1 2 3) (4 5 6))")[()]
45
+ assert isinstance(lst, np.ndarray)
46
+ assert np.array_equal(
47
+ lst,
48
+ [
49
+ [1, 2, 3],
50
+ [4, 5, 6],
51
+ ],
52
+ )
53
+ lst = Parsed(b"2{(1 2 3)}")[()]
54
+ assert isinstance(lst, np.ndarray)
55
+ assert np.array_equal(
56
+ lst,
57
+ [
58
+ [1, 2, 3],
59
+ [1, 2, 3],
60
+ ],
61
+ )
62
+ assert Parsed(b"0()")[()] == []
44
63
  field = Parsed(b"nonuniform List<vector> 2((1 2 3) (4 5 6))")[()]
45
64
  assert isinstance(field, np.ndarray)
46
65
  assert np.array_equal(
@@ -83,19 +102,23 @@ def test_parse_value() -> None:
83
102
  assert Parsed(b"[1 1 -2 0 0 0 0] 9.81")[()] == FoamFile.Dimensioned(
84
103
  dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2), value=9.81
85
104
  )
86
- assert Parsed(b"hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)")[()] == (
87
- "hex",
88
- [0, 1, 2, 3, 4, 5, 6, 7],
89
- [1, 1, 1],
90
- "simpleGrading",
91
- [1, 1, 1],
92
- )
105
+ tpl = Parsed(b"hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)")[()]
106
+ assert isinstance(tpl, tuple)
107
+ assert len(tpl) == 5
108
+ assert tpl[0] == "hex"
109
+ assert is_sequence(tpl[1])
110
+ assert np.array_equal(tpl[1], [0, 1, 2, 3, 4, 5, 6, 7]) # type: ignore[arg-type]
111
+ assert is_sequence(tpl[2])
112
+ assert np.array_equal(tpl[2], [1, 1, 1]) # type: ignore[arg-type]
113
+ assert tpl[3] == "simpleGrading"
114
+ assert is_sequence(tpl[4])
115
+ assert np.array_equal(tpl[4], [1, 1, 1]) # type: ignore[arg-type]
93
116
  assert Parsed(b"(a b; c d;)")[()] == [("a", "b"), ("c", "d")]
94
117
  assert Parsed(b"(a {b c;} d {e g;})")[()] == [
95
118
  ("a", {"b": "c"}),
96
119
  ("d", {"e": "g"}),
97
120
  ]
98
- assert Parsed(b"(a (0 1 2); b {})")[()] == [("a", [0, 1, 2]), ("b", {})]
121
+ assert Parsed(b"(a (b c d); e {})")[()] == [("a", ["b", "c", "d"]), ("e", {})]
99
122
  assert Parsed(b"({a b; c d;} {e g;})")[()] == [{"a": "b", "c": "d"}, {"e": "g"}]
100
123
  assert Parsed(b"(water oil mercury air)")[()] == ["water", "oil", "mercury", "air"]
101
124
  assert Parsed(b"div(phi,U)")[()] == "div(phi,U)"
@@ -3,6 +3,7 @@
3
3
  from pathlib import Path
4
4
  from textwrap import dedent
5
5
 
6
+ import numpy as np
6
7
  from foamlib import FoamFile
7
8
 
8
9
 
@@ -43,7 +44,7 @@ def test_simple(tmp_path: Path) -> None:
43
44
 
44
45
  assert decompose_par_dict["numberOfSubdomains"] == 8
45
46
  assert decompose_par_dict["method"] == "simple"
46
- assert decompose_par_dict["coeffs", "n"] == [4, 2, 1]
47
+ assert np.array_equal(decompose_par_dict["coeffs", "n"], [4, 2, 1]) # type: ignore[arg-type]
47
48
  assert decompose_par_dict["coeffs", "order"] == "xyz"
48
49
  assert decompose_par_dict["coeffs", "delta"] == 0.001
49
50
 
@@ -1,5 +1,6 @@
1
1
  # Based on https://foss.heptapod.net/fluiddyn/fluidsimfoam/-/blob/branch/default/tests/test_parser.py
2
2
 
3
+ import numpy as np
3
4
  import pytest
4
5
  from foamlib import FoamFile
5
6
  from foamlib._files._parsing import Parsed
@@ -61,20 +62,18 @@ def test_strange_names() -> None:
61
62
 
62
63
 
63
64
  def test_list_simple() -> None:
64
- assert Parsed(b"""
65
+ faces = Parsed(b"""
65
66
  faces
66
67
  (
67
68
  (1 5 4 0)
68
69
  (2 3 4 5)
69
70
  );
70
- """)[("faces",)] == [
71
- [1, 5, 4, 0],
72
- [2, 3, 4, 5],
73
- ]
71
+ """)[("faces",)]
72
+ assert np.array_equal(faces, [[1, 5, 4, 0], [2, 3, 4, 5]]) # type: ignore[arg-type]
74
73
 
75
74
 
76
75
  def test_list_assignment() -> None:
77
- assert Parsed(b"""
76
+ faces = Parsed(b"""
78
77
  faces
79
78
  (
80
79
  1
@@ -82,7 +81,8 @@ def test_list_assignment() -> None:
82
81
  4
83
82
  0
84
83
  );
85
- """)[("faces",)] == [1, 5, 4, 0]
84
+ """)[("faces",)]
85
+ assert np.array_equal(faces, [1, 5, 4, 0]) # type: ignore[arg-type]
86
86
 
87
87
 
88
88
  def test_dict_simple() -> None:
@@ -135,11 +135,11 @@ def test_dict_with_list() -> None:
135
135
  pRefValue 0;
136
136
  }
137
137
  """)
138
- assert parsed[("PISO", "pRefPoint")] == [0, 0, 0]
138
+ assert np.array_equal(parsed[("PISO", "pRefPoint")], [0, 0, 0]) # type: ignore[arg-type]
139
139
 
140
140
 
141
141
  def test_list_with_dict() -> None:
142
- assert Parsed(b"""
142
+ boundary = Parsed(b"""
143
143
  boundary
144
144
  (
145
145
  upperBoundary
@@ -152,33 +152,32 @@ def test_list_with_dict() -> None:
152
152
  );
153
153
  }
154
154
  );
155
- """)[("boundary",)] == [
156
- (
157
- "upperBoundary",
158
- {
159
- "type": "cyclic",
160
- "neighbourPatch": "lowerBoundary",
161
- "faces": [
162
- [3, 7, 6, 2],
163
- ],
164
- },
165
- ),
166
- ]
155
+ """)[("boundary",)]
156
+ assert isinstance(boundary, list)
157
+ assert len(boundary) == 1
158
+
159
+ upper_boundary = boundary[0]
160
+ assert upper_boundary[0] == "upperBoundary"
161
+ assert upper_boundary[1]["type"] == "cyclic"
162
+ assert upper_boundary[1]["neighbourPatch"] == "lowerBoundary"
163
+ assert np.array_equal(upper_boundary[1]["faces"], [[3, 7, 6, 2]])
167
164
 
168
165
 
169
166
  def test_list_with_str() -> None:
170
- assert Parsed(b"""
167
+ blocks = Parsed(b"""
171
168
  blocks
172
169
  (
173
170
  hex (0 1 2 3 4 5 6 7) (40 40 40) simpleGrading (1 1 1)
174
171
  );
175
- """)[("blocks",)] == [
176
- "hex",
177
- [0, 1, 2, 3, 4, 5, 6, 7],
178
- [40, 40, 40],
179
- "simpleGrading",
180
- [1, 1, 1],
181
- ]
172
+ """)[("blocks",)]
173
+ assert isinstance(blocks, list)
174
+ assert len(blocks) == 5
175
+
176
+ assert blocks[0] == "hex"
177
+ assert np.array_equal(blocks[1], [0, 1, 2, 3, 4, 5, 6, 7])
178
+ assert np.array_equal(blocks[2], [40, 40, 40])
179
+ assert blocks[3] == "simpleGrading"
180
+ assert np.array_equal(blocks[4], [1, 1, 1])
182
181
 
183
182
 
184
183
  def test_file_simple() -> None:
@@ -230,10 +229,8 @@ def test_file() -> None:
230
229
  """)
231
230
  assert parsed[("a",)] == 1
232
231
  assert parsed[("b",)] == 2
233
- assert parsed[("faces",)] == [
234
- [1, 5, 4, 0],
235
- [2, 3, 4, 5],
236
- ]
232
+ faces = parsed[("faces",)]
233
+ assert np.array_equal(faces, [[1, 5, 4, 0], [2, 3, 4, 5]]) # type: ignore[arg-type]
237
234
  assert parsed[("my_dict", "a")] == 1
238
235
 
239
236
 
@@ -455,12 +452,14 @@ def test_for_blockmesh() -> None:
455
452
  );
456
453
  """)
457
454
  assert parsed[("negHalfWidth",)] == ("#neg", "$halfWidth")
458
- assert parsed[("blocks",)] == [
459
- "hex",
460
- [4, 6, 14, 12, 0, 2, 10, 8],
461
- [1, "$upstreamCells", "$cylinderBoxCells"],
462
- "$expandBlock",
463
- ]
455
+ blocks = parsed[("blocks",)]
456
+ assert isinstance(blocks, list)
457
+ assert len(blocks) == 4
458
+
459
+ assert blocks[0] == "hex"
460
+ assert np.array_equal(blocks[1], [4, 6, 14, 12, 0, 2, 10, 8])
461
+ assert blocks[2] == [1, "$upstreamCells", "$cylinderBoxCells"]
462
+ assert blocks[3] == "$expandBlock"
464
463
 
465
464
 
466
465
  def test_for_u() -> None:
@@ -483,30 +482,32 @@ def test_blocks() -> None:
483
482
  hex (16 17 18 19 20 21 22 23) (96 1 72) simpleGrading (1 1 1)
484
483
  );
485
484
  """)
486
- assert parsed[("blocks",)] == [
487
- "hex",
488
- [0, 1, 2, 3, 4, 5, 6, 7],
489
- "inletChannel",
490
- [40, 1, 64],
491
- "simpleGrading",
492
- [1, 1, 1],
493
- "hex",
494
- [4, 5, 6, 7, 8, 9, 10, 11, 12],
495
- "inletChannel",
496
- [40, 1, 16],
497
- "simpleGrading",
498
- [1, 1, 1],
499
- "hex",
500
- [12, 13, 14, 15, 16, 17, 18, 19],
501
- [96, 1, 8],
502
- "simpleGrading",
503
- [1, 1, 1],
504
- "hex",
505
- [16, 17, 18, 19, 20, 21, 22, 23],
506
- [96, 1, 72],
507
- "simpleGrading",
508
- [1, 1, 1],
509
- ]
485
+ blocks = parsed[("blocks",)]
486
+ assert isinstance(blocks, list)
487
+ assert len(blocks) == 22
488
+
489
+ assert blocks[0] == "hex"
490
+ assert np.array_equal(blocks[1], [0, 1, 2, 3, 4, 5, 6, 7])
491
+ assert blocks[2] == "inletChannel"
492
+ assert np.array_equal(blocks[3], [40, 1, 64])
493
+ assert blocks[4] == "simpleGrading"
494
+ assert np.array_equal(blocks[5], [1, 1, 1])
495
+ assert blocks[6] == "hex"
496
+ assert np.array_equal(blocks[7], [4, 5, 6, 7, 8, 9, 10, 11, 12])
497
+ assert blocks[8] == "inletChannel"
498
+ assert np.array_equal(blocks[9], [40, 1, 16])
499
+ assert blocks[10] == "simpleGrading"
500
+ assert np.array_equal(blocks[11], [1, 1, 1])
501
+ assert blocks[12] == "hex"
502
+ assert np.array_equal(blocks[13], [12, 13, 14, 15, 16, 17, 18, 19])
503
+ assert np.array_equal(blocks[14], [96, 1, 8])
504
+ assert blocks[15] == "simpleGrading"
505
+ assert np.array_equal(blocks[16], [1, 1, 1])
506
+ assert blocks[17] == "hex"
507
+ assert np.array_equal(blocks[18], [16, 17, 18, 19, 20, 21, 22, 23])
508
+ assert np.array_equal(blocks[19], [96, 1, 72])
509
+ assert blocks[20] == "simpleGrading"
510
+ assert np.array_equal(blocks[21], [1, 1, 1])
510
511
 
511
512
 
512
513
  @pytest.mark.xfail(reason="Not currently supported")
@@ -586,10 +587,15 @@ def test_list_edges() -> None:
586
587
  spline 6 5 ((0.6 0.0124 0.05) (0.7 0.0395 0.05) (0.8 0.0724 0.05) (0.9 0.132 0.05) (1 0.172 0.05) (1.1 0.132 0.05) (1.2 0.0724 0.05) (1.3 0.0395 0.05) (1.4 0.0124 0.05))
587
588
  );
588
589
  """)
589
- assert parsed[("edges",)] == [
590
- "spline",
591
- 1,
592
- 2,
590
+ edges = parsed[("edges",)]
591
+ assert isinstance(edges, list)
592
+ assert len(edges) == 8
593
+
594
+ assert edges[0] == "spline"
595
+ assert edges[1] == 1
596
+ assert edges[2] == 2
597
+ assert np.array_equal(
598
+ edges[3],
593
599
  [
594
600
  [0.6, 0.0124, 0.0],
595
601
  [0.7, 0.0395, 0.0],
@@ -601,9 +607,12 @@ def test_list_edges() -> None:
601
607
  [1.3, 0.0395, 0.0],
602
608
  [1.4, 0.0124, 0.0],
603
609
  ],
604
- "spline",
605
- 6,
606
- 5,
610
+ )
611
+ assert edges[4] == "spline"
612
+ assert edges[5] == 6
613
+ assert edges[6] == 5
614
+ assert np.array_equal(
615
+ edges[7],
607
616
  [
608
617
  [0.6, 0.0124, 0.05],
609
618
  [0.7, 0.0395, 0.05],
@@ -615,7 +624,7 @@ def test_list_edges() -> None:
615
624
  [1.3, 0.0395, 0.05],
616
625
  [1.4, 0.0124, 0.05],
617
626
  ],
618
- ]
627
+ )
619
628
 
620
629
 
621
630
  def test_list_edges_arcs() -> None:
@@ -626,18 +635,19 @@ def test_list_edges_arcs() -> None:
626
635
  arc 5 10 origin (0 0 0)
627
636
  );
628
637
  """)
629
- assert parsed[("edges",)] == [
630
- "arc",
631
- 0,
632
- 5,
633
- "origin",
634
- [0, 0, 0],
635
- "arc",
636
- 5,
637
- 10,
638
- "origin",
639
- [0, 0, 0],
640
- ]
638
+ edges = parsed[("edges",)]
639
+ assert isinstance(edges, list)
640
+ assert len(edges) == 10
641
+ assert edges[0] == "arc"
642
+ assert edges[1] == 0
643
+ assert edges[2] == 5
644
+ assert edges[3] == "origin"
645
+ assert np.array_equal(edges[4], [0, 0, 0])
646
+ assert edges[5] == "arc"
647
+ assert edges[6] == 5
648
+ assert edges[7] == 10
649
+ assert edges[8] == "origin"
650
+ assert np.array_equal(edges[9], [0, 0, 0])
641
651
 
642
652
 
643
653
  def test_list_blocks() -> None:
@@ -649,23 +659,38 @@ def test_list_blocks() -> None:
649
659
  hex (2 3 11 10 5 4 12 13) (225 100 1) simpleGrading (1 ((0.1 0.25 41.9) (0.9 0.75 1)) 1)
650
660
  );
651
661
  """)
652
- assert parsed[("blocks",)] == [
653
- "hex",
654
- [0, 1, 9, 8, 7, 6, 14, 15],
655
- [50, 100, 1],
656
- "simpleGrading",
657
- [1, [[0.1, 0.25, 41.9], [0.9, 0.75, 1]], 1],
658
- "hex",
659
- [1, 2, 10, 9, 6, 5, 13, 14],
660
- [50, 100, 1],
661
- "simpleGrading",
662
- [1, [[0.1, 0.25, 41.9], [0.9, 0.75, 1]], 1],
663
- "hex",
664
- [2, 3, 11, 10, 5, 4, 12, 13],
665
- [225, 100, 1],
666
- "simpleGrading",
667
- [1, [[0.1, 0.25, 41.9], [0.9, 0.75, 1]], 1],
668
- ]
662
+ blocks = parsed[("blocks",)]
663
+
664
+ assert isinstance(blocks, list)
665
+ assert len(blocks) == 15
666
+
667
+ assert blocks[0] == "hex"
668
+ assert np.array_equal(blocks[1], [0, 1, 9, 8, 7, 6, 14, 15])
669
+ assert np.array_equal(blocks[2], [50, 100, 1])
670
+ assert blocks[3] == "simpleGrading"
671
+ assert isinstance(blocks[4], list)
672
+ assert len(blocks[4]) == 3
673
+ assert blocks[4][0] == 1
674
+ assert np.array_equal(blocks[4][1], [[0.1, 0.25, 41.9], [0.9, 0.75, 1]])
675
+ assert blocks[4][2] == 1
676
+ assert blocks[5] == "hex"
677
+ assert np.array_equal(blocks[6], [1, 2, 10, 9, 6, 5, 13, 14])
678
+ assert np.array_equal(blocks[7], [50, 100, 1])
679
+ assert blocks[8] == "simpleGrading"
680
+ assert isinstance(blocks[9], list)
681
+ assert len(blocks[9]) == 3
682
+ assert blocks[9][0] == 1
683
+ assert np.array_equal(blocks[9][1], [[0.1, 0.25, 41.9], [0.9, 0.75, 1]])
684
+ assert blocks[9][2] == 1
685
+ assert blocks[10] == "hex"
686
+ assert np.array_equal(blocks[11], [2, 3, 11, 10, 5, 4, 12, 13])
687
+ assert np.array_equal(blocks[12], [225, 100, 1])
688
+ assert blocks[13] == "simpleGrading"
689
+ assert isinstance(blocks[14], list)
690
+ assert len(blocks[14]) == 3
691
+ assert blocks[14][0] == 1
692
+ assert np.array_equal(blocks[14][1], [[0.1, 0.25, 41.9], [0.9, 0.75, 1]])
693
+ assert blocks[14][2] == 1
669
694
 
670
695
 
671
696
  @pytest.mark.xfail(reason="Not currently supported")
@@ -2,6 +2,7 @@
2
2
 
3
3
  from pathlib import Path
4
4
 
5
+ import numpy as np
5
6
  from foamlib import FoamFile
6
7
 
7
8
  contents = r"""
@@ -49,18 +50,18 @@ def test_get_cells_coords(tmp_path: Path) -> None:
49
50
  file = FoamFile(path)
50
51
 
51
52
  points = file[None]
52
- assert isinstance(points, list)
53
+ assert isinstance(points, np.ndarray)
53
54
 
54
- assert points[0] == [0, 0, 0]
55
- assert points[1] == [0.15707963268, 0, 0]
56
- assert points[2] == [0.314159265359, 0, 0]
57
- assert points[3] == [0.471238898038, 0, 0]
58
- assert points[4] == [0.628318530718, 0, 0]
59
- assert points[5] == [0, 0, 0]
60
- assert points[6] == [0.15707963268, 0, 0]
61
- assert points[7] == [0.314159265359, 0, 0]
62
- assert points[8] == [0.471238898038, 0, 0]
63
- assert points[9] == [0.628318530718, 0, 0]
55
+ assert np.array_equal(points[0], [0, 0, 0])
56
+ assert np.array_equal(points[1], [0.15707963268, 0, 0])
57
+ assert np.array_equal(points[2], [0.314159265359, 0, 0])
58
+ assert np.array_equal(points[3], [0.471238898038, 0, 0])
59
+ assert np.array_equal(points[4], [0.628318530718, 0, 0])
60
+ assert np.array_equal(points[5], [0, 0, 0])
61
+ assert np.array_equal(points[6], [0.15707963268, 0, 0])
62
+ assert np.array_equal(points[7], [0.314159265359, 0, 0])
63
+ assert np.array_equal(points[8], [0.471238898038, 0, 0])
64
+ assert np.array_equal(points[9], [0.628318530718, 0, 0])
64
65
 
65
66
  assert len(points) == 10
66
67
 
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