foamlib 0.9.2__tar.gz → 0.9.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.
Files changed (66) hide show
  1. {foamlib-0.9.2 → foamlib-0.9.3}/PKG-INFO +1 -1
  2. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/__init__.py +1 -1
  3. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_parsing.py +3 -1
  4. foamlib-0.9.3/foamlib/_files/_types.py +252 -0
  5. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_files.py +5 -6
  6. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_basic.py +10 -8
  7. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_loads.py +12 -8
  8. foamlib-0.9.3/tests/test_files/test_types.py +62 -0
  9. foamlib-0.9.2/foamlib/_files/_types.py +0 -136
  10. {foamlib-0.9.2 → foamlib-0.9.3}/.devcontainer.json +0 -0
  11. {foamlib-0.9.2 → foamlib-0.9.3}/.dockerignore +0 -0
  12. {foamlib-0.9.2 → foamlib-0.9.3}/.git-blame-ignore-revs +0 -0
  13. {foamlib-0.9.2 → foamlib-0.9.3}/.github/dependabot.yml +0 -0
  14. {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/ci.yml +0 -0
  15. {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/docker.yml +0 -0
  16. {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/dockerhub-description.yml +0 -0
  17. {foamlib-0.9.2 → foamlib-0.9.3}/.github/workflows/pypi-publish.yml +0 -0
  18. {foamlib-0.9.2 → foamlib-0.9.3}/.gitignore +0 -0
  19. {foamlib-0.9.2 → foamlib-0.9.3}/.readthedocs.yaml +0 -0
  20. {foamlib-0.9.2 → foamlib-0.9.3}/CONTRIBUTING.md +0 -0
  21. {foamlib-0.9.2 → foamlib-0.9.3}/Dockerfile +0 -0
  22. {foamlib-0.9.2 → foamlib-0.9.3}/LICENSE.txt +0 -0
  23. {foamlib-0.9.2 → foamlib-0.9.3}/README.md +0 -0
  24. {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/benchmark.png +0 -0
  25. {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/benchmark.py +0 -0
  26. {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/requirements.txt +0 -0
  27. {foamlib-0.9.2 → foamlib-0.9.3}/benchmark/ruff.toml +0 -0
  28. {foamlib-0.9.2 → foamlib-0.9.3}/docs/Makefile +0 -0
  29. {foamlib-0.9.2 → foamlib-0.9.3}/docs/cases.rst +0 -0
  30. {foamlib-0.9.2 → foamlib-0.9.3}/docs/conf.py +0 -0
  31. {foamlib-0.9.2 → foamlib-0.9.3}/docs/files.rst +0 -0
  32. {foamlib-0.9.2 → foamlib-0.9.3}/docs/index.rst +0 -0
  33. {foamlib-0.9.2 → foamlib-0.9.3}/docs/make.bat +0 -0
  34. {foamlib-0.9.2 → foamlib-0.9.3}/docs/ruff.toml +0 -0
  35. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/__init__.py +0 -0
  36. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_async.py +0 -0
  37. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_base.py +0 -0
  38. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_run.py +0 -0
  39. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_slurm.py +0 -0
  40. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_subprocess.py +0 -0
  41. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_sync.py +0 -0
  42. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_cases/_util.py +0 -0
  43. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/__init__.py +0 -0
  44. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_files.py +0 -0
  45. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_io.py +0 -0
  46. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/_files/_serialization.py +0 -0
  47. {foamlib-0.9.2 → foamlib-0.9.3}/foamlib/py.typed +0 -0
  48. {foamlib-0.9.2 → foamlib-0.9.3}/logo.png +0 -0
  49. {foamlib-0.9.2 → foamlib-0.9.3}/pyproject.toml +0 -0
  50. {foamlib-0.9.2 → foamlib-0.9.3}/tests/__init__.py +0 -0
  51. {foamlib-0.9.2 → foamlib-0.9.3}/tests/ruff.toml +0 -0
  52. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/__init__.py +0 -0
  53. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_cavity.py +0 -0
  54. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_cavity_async.py +0 -0
  55. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_flange.py +0 -0
  56. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_cases/test_flange_async.py +0 -0
  57. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_example.py +0 -0
  58. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/__init__.py +0 -0
  59. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_dumps.py +0 -0
  60. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/__init__.py +0 -0
  61. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_advanced.py +0 -0
  62. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_decompose_par.py +0 -0
  63. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_fields.py +0 -0
  64. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_fv_schemes.py +0 -0
  65. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_intermediate.py +0 -0
  66. {foamlib-0.9.2 → foamlib-0.9.3}/tests/test_files/test_parsing/test_poly_mesh.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: foamlib
3
- Version: 0.9.2
3
+ Version: 0.9.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
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.9.2"
3
+ __version__ = "0.9.3"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
@@ -363,7 +363,9 @@ _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
363
363
  _DIRECTIVE = Word("#", _IDENTBODYCHARS)
364
364
  _TOKEN = dbl_quoted_string | _DIRECTIVE | _IDENTIFIER
365
365
  _DATA = Forward()
366
- _KEYWORD_ENTRY = _keyword_entry_of(_TOKEN | _list_of(_IDENTIFIER), _DATA)
366
+ _KEYWORD_ENTRY = _keyword_entry_of(
367
+ _TOKEN | _list_of(_IDENTIFIER), Opt(_DATA, default="")
368
+ )
367
369
  _DICT = _dict_of(_TOKEN, _DATA)
368
370
  _DATA_ENTRY = Forward()
369
371
  _LIST_ENTRY = _DICT | _KEYWORD_ENTRY | _DATA_ENTRY
@@ -0,0 +1,252 @@
1
+ from __future__ import annotations
2
+
3
+ import sys
4
+ from typing import Any, Dict, NamedTuple, Optional, Union
5
+
6
+ import numpy as np
7
+
8
+ if sys.version_info >= (3, 9):
9
+ from collections.abc import Mapping, MutableMapping, Sequence
10
+ else:
11
+ from typing import Mapping, MutableMapping, Sequence
12
+
13
+ if sys.version_info >= (3, 10):
14
+ from typing import TypeGuard
15
+ else:
16
+ from typing_extensions import TypeGuard
17
+
18
+
19
+ class DimensionSet(NamedTuple):
20
+ mass: float = 0
21
+ length: float = 0
22
+ time: float = 0
23
+ temperature: float = 0
24
+ moles: float = 0
25
+ current: float = 0
26
+ luminous_intensity: float = 0
27
+
28
+ def __repr__(self) -> str:
29
+ return f"{type(self).__name__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
30
+
31
+ def __add__(self, other: DimensionSet) -> DimensionSet: # type: ignore[override]
32
+ if not isinstance(other, DimensionSet):
33
+ return NotImplemented
34
+
35
+ if self != other:
36
+ msg = f"Cannot add DimensionSet with different dimensions: {self} + {other}"
37
+ raise ValueError(msg)
38
+
39
+ return self
40
+
41
+ def __sub__(self, other: DimensionSet) -> DimensionSet:
42
+ if not isinstance(other, DimensionSet):
43
+ return NotImplemented
44
+
45
+ if self != other:
46
+ msg = f"Cannot subtract DimensionSet with different dimensions: {self} - {other}"
47
+ raise ValueError(msg)
48
+
49
+ return self
50
+
51
+ def __mul__(self, other: DimensionSet) -> DimensionSet: # type: ignore[override]
52
+ if not isinstance(other, DimensionSet):
53
+ return NotImplemented
54
+
55
+ return DimensionSet(*(a + b for a, b in zip(self, other)))
56
+
57
+ def __truediv__(self, other: DimensionSet) -> DimensionSet:
58
+ if not isinstance(other, DimensionSet):
59
+ return NotImplemented
60
+
61
+ return DimensionSet(*(a - b for a, b in zip(self, other)))
62
+
63
+ def __pow__(self, exponent: float) -> DimensionSet:
64
+ if not isinstance(exponent, (int, float)):
65
+ return NotImplemented
66
+
67
+ return DimensionSet(*(a * exponent for a in self))
68
+
69
+ def __bool__(self) -> bool:
70
+ return any(v != 0 for v in self)
71
+
72
+
73
+ Tensor = Union[
74
+ float,
75
+ "np.ndarray[tuple[int], np.dtype[np.float64]]",
76
+ ]
77
+
78
+ TensorLike = Union[
79
+ Sequence[float],
80
+ "np.ndarray[tuple[()], np.dtype[np.float64]]",
81
+ Tensor,
82
+ ]
83
+
84
+
85
+ class Dimensioned:
86
+ def __init__(
87
+ self,
88
+ value: TensorLike,
89
+ dimensions: DimensionSet | Sequence[float],
90
+ name: str | None = None,
91
+ ) -> None:
92
+ if is_sequence(value):
93
+ self.value: Tensor = np.array(value, dtype=float) # type: ignore [assignment]
94
+ else:
95
+ assert isinstance(value, (int, float, np.ndarray))
96
+ self.value = float(value)
97
+
98
+ if not isinstance(dimensions, DimensionSet):
99
+ self.dimensions = DimensionSet(*dimensions)
100
+ else:
101
+ self.dimensions = dimensions
102
+
103
+ self.name = name
104
+
105
+ def __repr__(self) -> str:
106
+ if self.name is not None:
107
+ return (
108
+ f"{type(self).__name__}({self.value}, {self.dimensions}, {self.name})"
109
+ )
110
+ return f"{type(self).__name__}({self.value}, {self.dimensions})"
111
+
112
+ def __add__(self, other: Dimensioned | Tensor) -> Dimensioned:
113
+ if not isinstance(other, Dimensioned):
114
+ other = Dimensioned(other, DimensionSet())
115
+
116
+ return Dimensioned(
117
+ self.value + other.value, # type: ignore [arg-type]
118
+ self.dimensions + other.dimensions,
119
+ f"{self.name} + {other.name}"
120
+ if self.name is not None and other.name is not None
121
+ else None,
122
+ )
123
+
124
+ def __sub__(self, other: Dimensioned | Tensor) -> Dimensioned:
125
+ if not isinstance(other, Dimensioned):
126
+ other = Dimensioned(other, DimensionSet())
127
+
128
+ return Dimensioned(
129
+ self.value - other.value, # type: ignore [arg-type]
130
+ self.dimensions - other.dimensions,
131
+ f"{self.name} - {other.name}"
132
+ if self.name is not None and other.name is not None
133
+ else None,
134
+ )
135
+
136
+ def __mul__(self, other: Dimensioned | Tensor) -> Dimensioned:
137
+ if not isinstance(other, Dimensioned):
138
+ other = Dimensioned(other, DimensionSet())
139
+
140
+ return Dimensioned(
141
+ self.value * other.value, # type: ignore [arg-type]
142
+ self.dimensions * other.dimensions,
143
+ f"{self.name} * {other.name}"
144
+ if self.name is not None and other.name is not None
145
+ else None,
146
+ )
147
+
148
+ def __truediv__(self, other: Dimensioned | Tensor) -> Dimensioned:
149
+ if not isinstance(other, Dimensioned):
150
+ other = Dimensioned(other, DimensionSet())
151
+
152
+ return Dimensioned(
153
+ self.value / other.value, # type: ignore [arg-type]
154
+ self.dimensions / other.dimensions,
155
+ f"{self.name} / {other.name}"
156
+ if self.name is not None and other.name is not None
157
+ else None,
158
+ )
159
+
160
+ def __pow__(self, exponent: float) -> Dimensioned:
161
+ if not isinstance(exponent, (int, float)):
162
+ return NotImplemented
163
+
164
+ return Dimensioned(
165
+ self.value**exponent, # type: ignore [arg-type]
166
+ self.dimensions**exponent,
167
+ f"{self.name} ** {exponent}" if self.name is not None else None,
168
+ )
169
+
170
+ def __float__(self) -> float:
171
+ if self.dimensions:
172
+ msg = f"Cannot convert non-dimensionless Dimensioned object to float: {self.dimensions}"
173
+ raise ValueError(msg)
174
+ return float(self.value)
175
+
176
+ def __int__(self) -> int:
177
+ if self.dimensions:
178
+ msg = f"Cannot convert non-dimensionless Dimensioned object to int: {self.dimensions}"
179
+ raise ValueError(msg)
180
+ return int(self.value)
181
+
182
+ def __array__(
183
+ self, dtype: Any = None, *, copy: Any = None
184
+ ) -> np.ndarray[tuple[()] | tuple[int], np.dtype[np.float64]]:
185
+ if self.dimensions:
186
+ msg = f"Cannot convert non-dimensionless Dimensioned object to array: {self.dimensions}"
187
+ raise ValueError(msg)
188
+ return np.array(self.value, dtype=dtype, copy=copy)
189
+
190
+
191
+ Field = Union[
192
+ float,
193
+ "np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]",
194
+ ]
195
+
196
+ FieldLike = Union[
197
+ TensorLike,
198
+ Sequence[TensorLike],
199
+ Sequence[Sequence[TensorLike]],
200
+ Field,
201
+ ]
202
+
203
+
204
+ Data = Union[
205
+ str,
206
+ int,
207
+ float,
208
+ bool,
209
+ Dimensioned,
210
+ DimensionSet,
211
+ Sequence["Entry"],
212
+ Field,
213
+ ]
214
+
215
+ Entry = Union[
216
+ Data,
217
+ Mapping[str, "Entry"],
218
+ ]
219
+ """
220
+ A value that can be stored in an OpenFOAM file.
221
+ """
222
+
223
+ DataLike = Union[
224
+ FieldLike,
225
+ Sequence["EntryLike"],
226
+ Data,
227
+ ]
228
+
229
+ EntryLike = Union[
230
+ DataLike,
231
+ Mapping[str, "EntryLike"],
232
+ ]
233
+
234
+
235
+ def is_sequence(
236
+ value: EntryLike,
237
+ ) -> TypeGuard[
238
+ Sequence[EntryLike]
239
+ | np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]
240
+ ]:
241
+ return (isinstance(value, Sequence) and not isinstance(value, str)) or (
242
+ isinstance(value, np.ndarray) and value.ndim > 0
243
+ )
244
+
245
+
246
+ MutableEntry = Union[
247
+ Data,
248
+ MutableMapping[str, "MutableEntry"],
249
+ ]
250
+
251
+ Dict_ = Dict[str, Union["Entry", "Dict_"]]
252
+ File = Dict[Optional[str], Union["Entry", "Dict_"]]
@@ -78,13 +78,12 @@ def test_write_read(tmp_path: Path) -> None:
78
78
  assert sd["nestedList"] == [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
79
79
 
80
80
  sd["g"] = FoamFile.Dimensioned(
81
- name="g", dimensions=[1, 1, -2, 0, 0, 0, 0], value=[0, 0, -9.81]
82
- )
83
- assert sd["g"] == FoamFile.Dimensioned(
84
- name="g",
85
- dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2),
86
- value=[0, 0, -9.81],
81
+ name="g", dimensions=[0, 1, -2, 0, 0, 0, 0], value=[0, 0, -9.81]
87
82
  )
83
+ assert isinstance(sd["g"], FoamFile.Dimensioned)
84
+ assert sd["g"].name == "g"
85
+ assert sd["g"].dimensions == FoamFile.DimensionSet(length=1, time=-2)
86
+ assert np.array_equal(sd["g"].value, [0, 0, -9.81])
88
87
 
89
88
  sd["n"] = 1
90
89
  sd["y"] = 2
@@ -95,14 +95,16 @@ def test_parse_value() -> None:
95
95
  assert Parsed(b"[1 1 -2 0 0 0 0]")[()] == FoamFile.DimensionSet(
96
96
  mass=1, length=1, time=-2
97
97
  )
98
- assert Parsed(b"g [1 1 -2 0 0 0 0] (0 0 -9.81)")[()] == FoamFile.Dimensioned(
99
- name="g",
100
- dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2),
101
- value=[0, 0, -9.81],
102
- )
103
- assert Parsed(b"[1 1 -2 0 0 0 0] 9.81")[()] == FoamFile.Dimensioned(
104
- dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2), value=9.81
105
- )
98
+ dimensioned = Parsed(b"g [1 1 -2 0 0 0 0] (0 0 -9.81)")[()]
99
+ assert isinstance(dimensioned, FoamFile.Dimensioned)
100
+ assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
101
+ assert np.array_equal(dimensioned.value, [0, 0, -9.81])
102
+ assert dimensioned.name == "g"
103
+ dimensioned = Parsed(b"[1 1 -2 0 0 0 0] 9.81")[()]
104
+ assert isinstance(dimensioned, FoamFile.Dimensioned)
105
+ assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
106
+ assert dimensioned.value == 9.81
107
+ assert dimensioned.name is None
106
108
  tpl = Parsed(b"hex (0 1 2 3 4 5 6 7) (1 1 1) simpleGrading (1 1 1)")[()]
107
109
  assert isinstance(tpl, tuple)
108
110
  assert len(tpl) == 5
@@ -18,6 +18,7 @@ def test_loads() -> None:
18
18
  assert FoamFile.loads("word word") == ("word", "word")
19
19
  assert FoamFile.loads('"a string"') == '"a string"'
20
20
  assert FoamFile.loads("(word word)") == ["word", "word"]
21
+ assert FoamFile.loads("keyword value;") == {"keyword": "value"}
21
22
  assert FoamFile.loads("uniform 1") == 1
22
23
  assert FoamFile.loads("uniform 1.0") == 1.0
23
24
  assert FoamFile.loads("uniform 1.0e-3") == 1.0e-3
@@ -57,13 +58,16 @@ def test_loads() -> None:
57
58
  assert FoamFile.loads("[1 1 -2 0 0 0 0]") == FoamFile.DimensionSet(
58
59
  mass=1, length=1, time=-2
59
60
  )
60
- assert FoamFile.loads("g [1 1 -2 0 0 0 0] (0 0 -9.81)") == FoamFile.Dimensioned(
61
- name="g",
62
- dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2),
63
- value=[0, 0, -9.81],
64
- )
65
- assert FoamFile.loads("[1 1 -2 0 0 0 0] 9.81") == FoamFile.Dimensioned(
66
- dimensions=FoamFile.DimensionSet(mass=1, length=1, time=-2), value=9.81
67
- )
61
+ dimensioned = FoamFile.loads("g [1 1 -2 0 0 0 0] (0 0 -9.81)")
62
+ assert isinstance(dimensioned, FoamFile.Dimensioned)
63
+ assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
64
+ assert np.array_equal(dimensioned.value, [0, 0, -9.81])
65
+ assert dimensioned.name == "g"
66
+ dimensioned = FoamFile.loads("[1 1 -2 0 0 0 0] 9.81")
67
+ assert isinstance(dimensioned, FoamFile.Dimensioned)
68
+ assert dimensioned.dimensions == FoamFile.DimensionSet(mass=1, length=1, time=-2)
69
+ assert dimensioned.value == 9.81
70
+ assert dimensioned.name is None
68
71
  assert FoamFile.loads("a {b c; d e;}") == {"a": {"b": "c", "d": "e"}}
69
72
  assert FoamFile.loads("(a b; c d;)") == [("a", "b"), ("c", "d")]
73
+ assert FoamFile.loads("keyword;") == {"keyword": ""}
@@ -0,0 +1,62 @@
1
+ import numpy as np
2
+ import pytest
3
+ from foamlib import FoamFile
4
+
5
+
6
+ def test_dimension_set() -> None:
7
+ dims = FoamFile.DimensionSet(length=1, time=-2)
8
+ assert dims.length == 1
9
+ assert dims.time == -2
10
+ assert dims.mass == 0
11
+
12
+ assert dims + dims == dims
13
+ assert dims - dims == dims
14
+ assert dims * dims == FoamFile.DimensionSet(length=2, time=-4)
15
+ assert dims / dims == FoamFile.DimensionSet()
16
+ assert dims**2 == FoamFile.DimensionSet(length=2, time=-4)
17
+
18
+ assert dims
19
+ assert not FoamFile.DimensionSet()
20
+
21
+
22
+ def test_dimensioned() -> None:
23
+ dimensioned = FoamFile.Dimensioned(
24
+ 9.81, FoamFile.DimensionSet(length=1, time=-2), "g"
25
+ )
26
+ assert dimensioned.value == 9.81
27
+ assert dimensioned.dimensions == FoamFile.DimensionSet(length=1, time=-2)
28
+ assert dimensioned.name == "g"
29
+
30
+ result = dimensioned + dimensioned
31
+ assert result.value == 9.81 * 2
32
+ assert result.dimensions == FoamFile.DimensionSet(length=1, time=-2)
33
+ assert result.name == "g + g"
34
+
35
+ result = dimensioned - dimensioned
36
+ assert result.value == 0.0
37
+ assert result.dimensions == FoamFile.DimensionSet(length=1, time=-2)
38
+ assert result.name == "g - g"
39
+
40
+ result = dimensioned * dimensioned
41
+ assert result.value == 9.81**2
42
+ assert result.dimensions == FoamFile.DimensionSet(length=2, time=-4)
43
+ assert result.name == "g * g"
44
+
45
+ result = dimensioned / dimensioned
46
+ assert result.value == 1.0
47
+ assert result.dimensions == FoamFile.DimensionSet()
48
+ assert result.name == "g / g"
49
+
50
+ result = dimensioned**2
51
+ assert result.value == 9.81**2
52
+ assert result.dimensions == FoamFile.DimensionSet(length=2, time=-4)
53
+ assert result.name == "g ** 2"
54
+
55
+ with pytest.raises(ValueError, match="dimension"):
56
+ dimensioned + 1
57
+
58
+ with pytest.raises(ValueError, match="dimension"):
59
+ float(dimensioned)
60
+
61
+ with pytest.raises(ValueError, match="dimension"):
62
+ np.array(dimensioned)
@@ -1,136 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import sys
4
- from typing import Dict, NamedTuple, Optional, Union
5
-
6
- import numpy as np
7
-
8
- if sys.version_info >= (3, 9):
9
- from collections.abc import Mapping, MutableMapping, Sequence
10
- else:
11
- from typing import Mapping, MutableMapping, Sequence
12
-
13
- if sys.version_info >= (3, 10):
14
- from typing import TypeGuard
15
- else:
16
- from typing_extensions import TypeGuard
17
-
18
-
19
- class DimensionSet(NamedTuple):
20
- mass: float = 0
21
- length: float = 0
22
- time: float = 0
23
- temperature: float = 0
24
- moles: float = 0
25
- current: float = 0
26
- luminous_intensity: float = 0
27
-
28
- def __repr__(self) -> str:
29
- return f"{type(self).__name__}({', '.join(f'{n}={v}' for n, v in zip(self._fields, self) if v != 0)})"
30
-
31
-
32
- Tensor = Union[
33
- float,
34
- "np.ndarray[tuple[int], np.dtype[np.float64]]",
35
- ]
36
-
37
- TensorLike = Union[
38
- Sequence[float],
39
- "np.ndarray[tuple[()], np.dtype[np.float64]]",
40
- Tensor,
41
- ]
42
-
43
-
44
- class Dimensioned:
45
- def __init__(
46
- self,
47
- value: TensorLike,
48
- dimensions: DimensionSet | Sequence[float],
49
- name: str | None = None,
50
- ) -> None:
51
- if is_sequence(value):
52
- self.value: Tensor = np.array(value, dtype=float) # type: ignore [assignment]
53
- else:
54
- assert isinstance(value, (int, float, np.ndarray))
55
- self.value = float(value)
56
-
57
- if not isinstance(dimensions, DimensionSet):
58
- self.dimensions = DimensionSet(*dimensions)
59
- else:
60
- self.dimensions = dimensions
61
-
62
- self.name = name
63
-
64
- def __eq__(self, other: object) -> bool:
65
- if not isinstance(other, Dimensioned):
66
- return NotImplemented
67
-
68
- return (
69
- self.dimensions == other.dimensions
70
- and np.array_equal(self.value, other.value)
71
- and self.name == other.name
72
- )
73
-
74
-
75
- Field = Union[
76
- float,
77
- "np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]",
78
- ]
79
-
80
- FieldLike = Union[
81
- TensorLike,
82
- Sequence[TensorLike],
83
- Sequence[Sequence[TensorLike]],
84
- Field,
85
- ]
86
-
87
-
88
- Data = Union[
89
- str,
90
- int,
91
- float,
92
- bool,
93
- Dimensioned,
94
- DimensionSet,
95
- Sequence["Entry"],
96
- Field,
97
- ]
98
-
99
- Entry = Union[
100
- Data,
101
- Mapping[str, "Entry"],
102
- ]
103
- """
104
- A value that can be stored in an OpenFOAM file.
105
- """
106
-
107
- DataLike = Union[
108
- FieldLike,
109
- Sequence["EntryLike"],
110
- Data,
111
- ]
112
-
113
- EntryLike = Union[
114
- DataLike,
115
- Mapping[str, "EntryLike"],
116
- ]
117
-
118
-
119
- def is_sequence(
120
- value: EntryLike,
121
- ) -> TypeGuard[
122
- Sequence[EntryLike]
123
- | np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]
124
- ]:
125
- return (isinstance(value, Sequence) and not isinstance(value, str)) or (
126
- isinstance(value, np.ndarray) and value.ndim > 0
127
- )
128
-
129
-
130
- MutableEntry = Union[
131
- Data,
132
- MutableMapping[str, "MutableEntry"],
133
- ]
134
-
135
- Dict_ = Dict[str, Union["Entry", "Dict_"]]
136
- File = Dict[Optional[str], Union["Entry", "Dict_"]]
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