foamlib 0.7.3__py3-none-any.whl → 0.8.1__py3-none-any.whl

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/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.7.3"
3
+ __version__ = "0.8.1"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
@@ -16,9 +16,9 @@ __all__ = [
16
16
  "AsyncFoamCase",
17
17
  "AsyncSlurmFoamCase",
18
18
  "CalledProcessError",
19
- "FoamFile",
20
19
  "FoamCase",
21
20
  "FoamCaseBase",
22
21
  "FoamCaseRunBase",
23
22
  "FoamFieldFile",
23
+ "FoamFile",
24
24
  ]
@@ -7,9 +7,9 @@ from ._sync import FoamCase
7
7
 
8
8
  __all__ = [
9
9
  "AsyncFoamCase",
10
- "FoamCaseBase",
11
- "FoamCaseRunBase",
12
10
  "AsyncSlurmFoamCase",
13
11
  "CalledProcessError",
14
12
  "FoamCase",
13
+ "FoamCaseBase",
14
+ "FoamCaseRunBase",
15
15
  ]
foamlib/_cases/_base.py CHANGED
@@ -6,17 +6,10 @@ from pathlib import Path
6
6
  from typing import TYPE_CHECKING, overload
7
7
 
8
8
  if sys.version_info >= (3, 9):
9
- from collections.abc import (
10
- Iterator,
11
- Sequence,
12
- )
9
+ from collections.abc import Iterator, Sequence
13
10
  from collections.abc import Set as AbstractSet
14
11
  else:
15
- from typing import (
16
- AbstractSet,
17
- Iterator,
18
- Sequence,
19
- )
12
+ from typing import AbstractSet, Iterator, Sequence
20
13
 
21
14
  from .._files import FoamFieldFile, FoamFile
22
15
 
foamlib/_cases/_util.py CHANGED
@@ -2,14 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import functools
4
4
  import sys
5
- from typing import (
6
- TYPE_CHECKING,
7
- Any,
8
- AsyncContextManager,
9
- Callable,
10
- Generic,
11
- TypeVar,
12
- )
5
+ from typing import TYPE_CHECKING, Any, AsyncContextManager, Callable, Generic, TypeVar
13
6
 
14
7
  if TYPE_CHECKING:
15
8
  from types import TracebackType
@@ -1,6 +1,6 @@
1
1
  from ._files import FoamFieldFile, FoamFile
2
2
 
3
3
  __all__ = [
4
- "FoamFile",
5
4
  "FoamFieldFile",
5
+ "FoamFile",
6
6
  ]
foamlib/_files/_files.py CHANGED
@@ -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 (
@@ -25,15 +26,14 @@ from ._types import (
25
26
  Entry,
26
27
  Field,
27
28
  File,
28
- MutableData,
29
+ MutableEntry,
29
30
  )
30
- from ._util import is_sequence
31
31
 
32
32
 
33
33
  class FoamFile(
34
34
  MutableMapping[
35
35
  Optional[Union[str, Tuple[str, ...]]],
36
- MutableData,
36
+ MutableEntry,
37
37
  ],
38
38
  FoamFileIO,
39
39
  ):
@@ -49,7 +49,7 @@ class FoamFile(
49
49
  DimensionSet = DimensionSet
50
50
 
51
51
  class SubDict(
52
- MutableMapping[str, MutableData],
52
+ MutableMapping[str, MutableEntry],
53
53
  ):
54
54
  """An OpenFOAM dictionary within a file as a mutable mapping."""
55
55
 
@@ -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], kind=Kind.KEYWORD), str):
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
- if self.format == "binary":
232
- arch = self.get(("FoamFile", "arch"), default=None)
233
- assert arch is None or isinstance(arch, str)
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
- if isinstance(data, (int, float)):
255
- self.class_ = "volScalarField"
256
-
257
- elif is_sequence(data) and data:
258
- if isinstance(data[0], (int, float)):
259
- if len(data) == 3:
260
- self.class_ = "volVectorField"
261
- elif len(data) == 6:
262
- self.class_ = "volSymmTensorField"
263
- elif len(data) == 9:
264
- self.class_ = "volTensorField"
265
- elif (
266
- is_sequence(data[0])
267
- and data[0]
268
- and isinstance(data[0][0], (int, float))
269
- ):
270
- if len(data[0]) == 3:
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 len(data[0]) == 6:
258
+ elif shape[1] == 6:
273
259
  self.class_ = "volSymmTensorField"
274
- elif len(data[0]) == 9:
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], kind=Kind.KEYWORD)
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], kind=Kind.KEYWORD)
311
+ + dumps(keywords[-1])
326
312
  + b" "
327
313
  + dumps(data, kind=kind)
328
314
  + b";"
foamlib/_files/_io.py CHANGED
@@ -3,9 +3,7 @@ from __future__ import annotations
3
3
  import gzip
4
4
  import sys
5
5
  from pathlib import Path
6
- from typing import (
7
- TYPE_CHECKING,
8
- )
6
+ from typing import TYPE_CHECKING
9
7
 
10
8
  if sys.version_info >= (3, 11):
11
9
  from typing import Self
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- import array
4
3
  import re
5
4
  import sys
5
+ from enum import Enum, auto
6
6
  from typing import Tuple, Union, cast
7
7
 
8
8
  if sys.version_info >= (3, 9):
@@ -13,8 +13,9 @@ else:
13
13
  if sys.version_info >= (3, 10):
14
14
  from types import EllipsisType
15
15
  else:
16
- from typing import Any as EllipsisType
16
+ EllipsisType = type(...)
17
17
 
18
+ import numpy as np
18
19
  from pyparsing import (
19
20
  Combine,
20
21
  Dict,
@@ -39,6 +40,64 @@ from pyparsing import (
39
40
  from ._types import Data, Dimensioned, DimensionSet, File
40
41
 
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
+ )
79
+
80
+ return rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{self.size - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
81
+
82
+ def parser(self) -> ParserElement:
83
+ if self == _Tensor.SCALAR:
84
+ return common.ieee_float
85
+
86
+ return (
87
+ Literal("(").suppress()
88
+ + Group(common.ieee_float[self.size], aslist=True)
89
+ + Literal(")").suppress()
90
+ ).add_parse_action(lambda tks: np.array(tks[0], dtype=float))
91
+
92
+ def __str__(self) -> str:
93
+ return {
94
+ _Tensor.SCALAR: "scalar",
95
+ _Tensor.VECTOR: "vector",
96
+ _Tensor.SYMM_TENSOR: "symmTensor",
97
+ _Tensor.TENSOR: "tensor",
98
+ }[self]
99
+
100
+
42
101
  def _list_of(entry: ParserElement) -> ParserElement:
43
102
  return Opt(
44
103
  Literal("List") + Literal("<") + _IDENTIFIER + Literal(">")
@@ -59,56 +118,47 @@ def _list_of(entry: ParserElement) -> ParserElement:
59
118
 
60
119
 
61
120
  def _parse_ascii_field(
62
- s: str, *, elsize: int, ignore: Regex | None
63
- ) -> list[float] | list[list[float]]:
64
- values = [
65
- float(v)
66
- for v in (re.sub(ignore.re, " ", s) if ignore is not None else s)
67
- .replace("(", " ")
68
- .replace(")", " ")
69
- .split()
70
- ]
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(")", " ")
71
126
 
72
- if elsize == 1:
73
- return values
74
-
75
- return [values[i : i + elsize] for i in range(0, len(values), elsize)]
127
+ return np.fromstring(s, dtype=float, sep=" ").reshape(-1, *tensor_kind.shape)
76
128
 
77
129
 
78
130
  def _unpack_binary_field(
79
- b: bytes, *, elsize: int, length: int
80
- ) -> list[float] | list[list[float]]:
81
- float_size = len(b) / elsize / length
131
+ b: bytes, tensor_kind: _Tensor, *, length: int
132
+ ) -> np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]:
133
+ float_size = len(b) / tensor_kind.size / length
82
134
  assert float_size in (4, 8)
83
135
 
84
- arr = array.array("f" if float_size == 4 else "d", b)
85
- values = arr.tolist()
86
-
87
- if elsize == 1:
88
- return values
136
+ dtype = np.float32 if float_size == 4 else float
137
+ return np.frombuffer(b, dtype=dtype).reshape(-1, *tensor_kind.shape)
89
138
 
90
- return [values[i : i + elsize] for i in range(0, len(values), elsize)]
91
139
 
92
-
93
- def _counted_tensor_list(
94
- *, elsize: int = 1, ignore: Regex | None = None
140
+ def _tensor_list(
141
+ tensor_kind: _Tensor | None = None, *, ignore: Regex | None = None
95
142
  ) -> ParserElement:
96
- float_pattern = r"(?i:[+-]?(?:(?:\d+\.?\d*(?:e[+-]?\d+)?)|nan|inf(?:inity)?))"
97
- ignore_pattern = rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
98
-
99
- if elsize == 1:
100
- tensor_pattern = float_pattern
101
- tensor = common.ieee_float
102
- else:
103
- tensor_pattern = rf"\((?:{ignore_pattern})?(?:{float_pattern}{ignore_pattern}){{{elsize - 1}}}{float_pattern}(?:{ignore_pattern})?\)"
104
- tensor = (
105
- Literal("(").suppress()
106
- + Group(common.ieee_float[elsize], aslist=True)
107
- + Literal(")").suppress()
143
+ if tensor_kind is None:
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)
108
149
  )
109
150
 
151
+ tensor_pattern = tensor_kind.pattern(ignore=ignore)
152
+ ignore_pattern = rf"(?:\s|{ignore.re.pattern})+" if ignore is not None else r"\s+"
153
+
110
154
  list_ = Forward()
111
155
 
156
+ list_ <<= Regex(
157
+ rf"\((?:{ignore_pattern})?(?:{tensor_pattern}{ignore_pattern})*{tensor_pattern}(?:{ignore_pattern})?\)"
158
+ ).add_parse_action(
159
+ lambda tks: [_parse_ascii_field(tks[0], tensor_kind, ignore=ignore)]
160
+ )
161
+
112
162
  def count_parse_action(tks: ParseResults) -> None:
113
163
  nonlocal list_
114
164
  length = tks[0]
@@ -118,44 +168,66 @@ def _counted_tensor_list(
118
168
  Regex(
119
169
  rf"\((?:{ignore_pattern})?(?:{tensor_pattern}{ignore_pattern}){{{length - 1}}}{tensor_pattern}(?:{ignore_pattern})?\)"
120
170
  ).add_parse_action(
121
- lambda tks: [_parse_ascii_field(tks[0], elsize=elsize, ignore=ignore)]
171
+ lambda tks: [_parse_ascii_field(tks[0], tensor_kind, ignore=ignore)]
122
172
  )
123
173
  | Regex(
124
- rf"\((?s:.{{{length * elsize * 8}}}|.{{{length * elsize * 4}}})\)"
125
- ).set_parse_action(
174
+ rf"\((?s:.{{{length * tensor_kind.size * 8}}}|.{{{length * tensor_kind.size * 4}}})\)"
175
+ ).add_parse_action(
126
176
  lambda tks: [
127
177
  _unpack_binary_field(
128
- tks[0][1:-1].encode("latin-1"), elsize=elsize, length=length
178
+ tks[0][1:-1].encode("latin-1"), tensor_kind, length=length
129
179
  )
130
180
  ]
131
181
  )
132
182
  | (
133
- Literal("{").suppress() + tensor + Literal("}").suppress()
134
- ).set_parse_action(lambda tks: [[tks[0]] * length])
183
+ Literal("{").suppress() + tensor_kind.parser() + Literal("}").suppress()
184
+ ).add_parse_action(
185
+ lambda tks: [np.full((length, *tensor_kind.shape), tks[0], dtype=float)]
186
+ )
135
187
  )
136
188
 
137
189
  count = common.integer.copy().add_parse_action(count_parse_action)
138
190
 
139
- return count.suppress() + list_
191
+ return (
192
+ Opt(Literal("List") + Literal("<") + str(tensor_kind) + Literal(">")).suppress()
193
+ + Opt(count).suppress()
194
+ + list_
195
+ )
196
+
197
+
198
+ def _dict_of(
199
+ keyword: ParserElement, data: ParserElement, *, located: bool = False
200
+ ) -> ParserElement:
201
+ dict_ = Forward()
202
+
203
+ keyword_entry = keyword + (dict_ | (data + Literal(";").suppress()))
204
+
205
+ if located:
206
+ keyword_entry = Located(keyword_entry)
207
+
208
+ dict_ <<= (
209
+ Literal("{").suppress()
210
+ + Dict(Group(keyword_entry)[...], asdict=not located)
211
+ + Literal("}").suppress()
212
+ )
213
+
214
+ return dict_
140
215
 
141
216
 
142
217
  def _keyword_entry_of(
143
218
  keyword: ParserElement,
144
- data_entries: ParserElement,
219
+ data: ParserElement,
145
220
  *,
146
221
  located: bool = False,
147
222
  ) -> ParserElement:
148
- subdict = Forward()
149
-
150
223
  keyword_entry = keyword + (
151
- (Literal("{").suppress() + subdict + Literal("}").suppress())
152
- | (data_entries + Literal(";").suppress())
224
+ _dict_of(keyword, data, located=located) | (data + Literal(";").suppress())
153
225
  )
154
226
 
155
227
  if located:
156
228
  keyword_entry = Located(keyword_entry)
157
-
158
- subdict <<= Dict(Group(keyword_entry)[...], asdict=not located)
229
+ else:
230
+ keyword_entry = keyword_entry.copy().set_parse_action(lambda tks: tuple(tks))
159
231
 
160
232
  return keyword_entry
161
233
 
@@ -188,12 +260,11 @@ _SWITCH = (
188
260
  _DIMENSIONS = (
189
261
  Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
190
262
  ).set_parse_action(lambda tks: DimensionSet(*tks))
191
- _TENSOR = common.ieee_float | (
192
- Literal("(").suppress()
193
- + Group(
194
- common.ieee_float[3] | common.ieee_float[6] | common.ieee_float[9], aslist=True
195
- )
196
- + Literal(")").suppress()
263
+ _TENSOR = (
264
+ _Tensor.SCALAR.parser()
265
+ | _Tensor.VECTOR.parser()
266
+ | _Tensor.SYMM_TENSOR.parser()
267
+ | _Tensor.TENSOR.parser()
197
268
  )
198
269
  _IDENTIFIER = Combine(
199
270
  Word(_IDENTCHARS, _IDENTBODYCHARS, exclude_chars="()")
@@ -203,63 +274,37 @@ _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
203
274
  lambda tks: Dimensioned(*reversed(tks.as_list()))
204
275
  )
205
276
  _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
206
- Keyword("nonuniform", _IDENTBODYCHARS).suppress()
207
- + (
208
- Literal("List").suppress()
209
- + Literal("<").suppress()
210
- + (
211
- (
212
- Literal("scalar").suppress()
213
- + Literal(">").suppress()
214
- + _counted_tensor_list(elsize=1, ignore=_COMMENT)
215
- )
216
- | (
217
- Literal("vector").suppress()
218
- + Literal(">").suppress()
219
- + _counted_tensor_list(elsize=3, ignore=_COMMENT)
220
- )
221
- | (
222
- Literal("symmTensor").suppress()
223
- + Literal(">").suppress()
224
- + _counted_tensor_list(elsize=6, ignore=_COMMENT)
225
- )
226
- | (
227
- Literal("tensor").suppress()
228
- + Literal(">").suppress()
229
- + _counted_tensor_list(elsize=9, ignore=_COMMENT)
230
- )
231
- )
232
- )
277
+ Keyword("nonuniform", _IDENTBODYCHARS).suppress() + _tensor_list(ignore=_COMMENT)
233
278
  )
234
279
  _TOKEN = dbl_quoted_string | _IDENTIFIER
235
- DATA = Forward()
236
- KEYWORD = (
237
- _TOKEN
238
- | _list_of(_IDENTIFIER)
239
- .set_parse_action(lambda tks: "(" + " ".join(tks[0]) + ")")
240
- .ignore(_COMMENT)
241
- .parse_with_tabs()
242
- )
243
- _KEYWORD_ENTRY = Dict(Group(_keyword_entry_of(KEYWORD, DATA)), asdict=True)
280
+ _DATA = Forward()
281
+ _KEYWORD_ENTRY = _keyword_entry_of(_TOKEN | _list_of(_IDENTIFIER), _DATA)
282
+ _DICT = _dict_of(_TOKEN, _DATA)
244
283
  _DATA_ENTRY = Forward()
245
- _LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
284
+ _LIST_ENTRY = _DICT | _KEYWORD_ENTRY | _DATA_ENTRY
246
285
  _LIST = _list_of(_LIST_ENTRY)
247
286
  _NUMBER = common.signed_integer ^ common.ieee_float
248
287
  _DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH | _TOKEN
249
288
 
250
- DATA <<= (
289
+ _DATA <<= (
251
290
  _DATA_ENTRY[1, ...]
252
- .set_parse_action(lambda tks: tuple(tks) if len(tks) > 1 else [tks[0]])
291
+ .set_parse_action(lambda tks: [tuple(tks)] if len(tks) > 1 else [tks[0]])
253
292
  .ignore(_COMMENT)
254
293
  .parse_with_tabs()
255
294
  )
256
295
 
296
+
297
+ def parse_data(s: str) -> Data:
298
+ return cast(Data, _DATA.parse_string(s, parse_all=True)[0])
299
+
300
+
301
+ _LOCATED_DICTIONARY = Group(
302
+ _keyword_entry_of(_TOKEN, Opt(_DATA, default=""), located=True)
303
+ )[...]
304
+ _LOCATED_DATA = Group(Located(_DATA.copy().add_parse_action(lambda tks: ["", tks[0]])))
305
+
257
306
  _FILE = (
258
- Dict(
259
- Group(_keyword_entry_of(KEYWORD, Opt(DATA, default=""), located=True))[...]
260
- + Opt(Group(Located(DATA.copy().add_parse_action(lambda tks: ["", tks[0]]))))
261
- + Group(_keyword_entry_of(KEYWORD, Opt(DATA, default=""), located=True))[...]
262
- )
307
+ Dict(_LOCATED_DICTIONARY + Opt(_LOCATED_DATA) + _LOCATED_DICTIONARY)
263
308
  .ignore(_COMMENT)
264
309
  .ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
265
310
  .parse_with_tabs()
@@ -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 cast, overload
5
+ from typing import overload
8
6
 
9
7
  if sys.version_info >= (3, 9):
10
- from collections.abc import Mapping, Sequence
8
+ from collections.abc import Mapping
11
9
  else:
12
- from typing import Mapping, Sequence
10
+ from typing import Mapping
13
11
 
14
- from ._parsing import DATA, KEYWORD
15
- from ._types import Data, Dimensioned, DimensionSet, Entry
16
- from ._util import is_sequence
12
+ import numpy as np
17
13
 
18
- try:
19
- import numpy as np
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
- DOUBLE_PRECISION_BINARY_FIELD = auto()
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 numpy and isinstance(data, np.ndarray):
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,31 +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
- if is_sequence(data) and (kind == Kind.SINGLE_ENTRY or not isinstance(data, tuple)):
63
- if len(data) == 1 and isinstance(data[0], Mapping) and len(data[0]) > 1:
64
- return [normalize({k: v}) for k, v in data[0].items()]
71
+ if isinstance(data, tuple) and kind == Kind.SINGLE_ENTRY and len(data) == 2:
72
+ k, v = data
73
+ return (normalize(k), normalize(v))
65
74
 
75
+ if is_sequence(data) and (kind == Kind.SINGLE_ENTRY or not isinstance(data, tuple)):
66
76
  return [normalize(d, kind=Kind.SINGLE_ENTRY) for d in data]
67
77
 
68
- if isinstance(data, Dimensioned):
69
- value = normalize(data.value, kind=Kind.SINGLE_ENTRY)
70
- assert isinstance(value, (int, float, list))
71
- return Dimensioned(value, data.dimensions, data.name)
72
-
73
78
  if isinstance(data, str):
74
- if kind == Kind.KEYWORD:
75
- data = KEYWORD.parse_string(data, parse_all=True)[0]
76
- assert isinstance(data, str)
77
- return data
78
-
79
- return cast(Data, DATA.parse_string(data, parse_all=True)[0])
79
+ return parse_data(data)
80
80
 
81
81
  if isinstance(
82
82
  data,
83
- (int, float, bool, tuple, DimensionSet),
83
+ (int, float, bool, tuple, DimensionSet, Dimensioned),
84
84
  ):
85
85
  return data
86
86
 
@@ -96,73 +96,51 @@ def dumps(
96
96
  data = normalize(data, kind=kind)
97
97
 
98
98
  if isinstance(data, Mapping):
99
- entries = []
100
- for k, v in data.items():
101
- value = normalize(v)
102
- if isinstance(value, Mapping):
103
- entries.append(
104
- dumps(k, kind=Kind.KEYWORD) + b" {" + dumps(value) + b"}"
105
- )
106
- elif not value:
107
- entries.append(dumps(k, kind=Kind.KEYWORD) + b";")
108
- else:
109
- entries.append(dumps(k, kind=Kind.KEYWORD) + b" " + dumps(value) + b";")
99
+ return (
100
+ b"{"
101
+ + b" ".join(dumps((k, v), kind=Kind.SINGLE_ENTRY) for k, v in data.items())
102
+ + b"}"
103
+ )
110
104
 
111
- return b" ".join(entries)
105
+ if isinstance(data, tuple) and kind == Kind.SINGLE_ENTRY and len(data) == 2:
106
+ k, v = data
107
+ ret = dumps(k) + b" " + dumps(v)
108
+ if not isinstance(v, Mapping):
109
+ ret += b";"
110
+ return ret
112
111
 
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
- Kind.ASCII_FIELD,
118
- Kind.DOUBLE_PRECISION_BINARY_FIELD,
119
- Kind.SINGLE_PRECISION_BINARY_FIELD,
120
- ) and (
121
- isinstance(data, (int, float))
122
- or is_sequence(data)
123
- and data
124
- and isinstance(data[0], (int, float))
125
- and len(data) in (3, 6, 9)
115
+ if kind in (Kind.ASCII_FIELD, Kind.BINARY_FIELD) and (
116
+ isinstance(data, (int, float, np.ndarray))
126
117
  ):
127
- return b"uniform " + dumps(data, kind=Kind.SINGLE_ENTRY)
128
-
129
- if kind in (
130
- Kind.ASCII_FIELD,
131
- Kind.DOUBLE_PRECISION_BINARY_FIELD,
132
- Kind.SINGLE_PRECISION_BINARY_FIELD,
133
- ) and is_sequence(data):
134
- 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:
135
125
  tensor_kind = b"scalar"
136
- elif is_sequence(data[0]) and data[0] and isinstance(data[0][0], (int, float)):
137
- if len(data[0]) == 3:
126
+
127
+ elif ndim == 2:
128
+ if shape[1] == 3:
138
129
  tensor_kind = b"vector"
139
- elif len(data[0]) == 6:
130
+ elif shape[1] == 6:
140
131
  tensor_kind = b"symmTensor"
141
- elif len(data[0]) == 9:
132
+ elif shape[1] == 9:
142
133
  tensor_kind = b"tensor"
143
134
  else:
144
135
  return dumps(data)
136
+
145
137
  else:
146
138
  return dumps(data)
147
139
 
148
- if kind in (
149
- Kind.DOUBLE_PRECISION_BINARY_FIELD,
150
- Kind.SINGLE_PRECISION_BINARY_FIELD,
151
- ):
152
- typecode = "f" if kind == Kind.SINGLE_PRECISION_BINARY_FIELD else "d"
153
- if tensor_kind == b"scalar":
154
- data = cast(Sequence[float], data)
155
- contents = b"(" + array.array(typecode, data).tobytes() + b")"
156
- else:
157
- data = cast(Sequence[Sequence[float]], data)
158
- contents = (
159
- b"("
160
- + array.array(
161
- typecode, itertools.chain.from_iterable(data)
162
- ).tobytes()
163
- + b")"
164
- )
140
+ if kind == Kind.BINARY_FIELD:
141
+ contents = b"(" + data.tobytes() + b")"
165
142
  else:
143
+ assert kind == Kind.ASCII_FIELD
166
144
  contents = dumps(data, kind=Kind.SINGLE_ENTRY)
167
145
 
168
146
  return b"nonuniform List<" + tensor_kind + b"> " + dumps(len(data)) + contents
@@ -185,7 +163,7 @@ def dumps(
185
163
  if isinstance(data, tuple):
186
164
  return b" ".join(dumps(v) for v in data)
187
165
 
188
- if is_sequence(data):
166
+ if is_sequence(data) and not isinstance(data, tuple):
189
167
  return b"(" + b" ".join(dumps(v, kind=Kind.SINGLE_ENTRY) for v in data) + b")"
190
168
 
191
169
  if data is True:
foamlib/_files/_types.py CHANGED
@@ -2,16 +2,20 @@ from __future__ import annotations
2
2
 
3
3
  import sys
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Dict, NamedTuple, Optional, Tuple, Union
5
+ from typing import Dict, NamedTuple, Optional, Tuple, Union
6
6
 
7
- if TYPE_CHECKING:
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,8 +33,7 @@ class DimensionSet(NamedTuple):
29
33
  Tensor = Union[
30
34
  float,
31
35
  Sequence[float],
32
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
33
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
36
+ "np.ndarray[tuple[()] | Tuple[int], np.dtype[np.float64]]",
34
37
  ]
35
38
 
36
39
 
@@ -41,15 +44,30 @@ class Dimensioned:
41
44
  name: str | None = None
42
45
 
43
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
+
44
53
  if not isinstance(self.dimensions, DimensionSet):
45
54
  self.dimensions = DimensionSet(*self.dimensions)
46
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
+
47
66
 
48
67
  Field = Union[
49
68
  Tensor,
50
69
  Sequence[Tensor],
51
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
52
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
70
+ "np.ndarray[tuple[int] | tuple[int, int], np.dtype[np.float64 | np.float32]]",
53
71
  ]
54
72
 
55
73
  Data = Union[
@@ -60,7 +78,6 @@ Data = Union[
60
78
  Dimensioned,
61
79
  DimensionSet,
62
80
  Sequence["Entry"],
63
- Tensor,
64
81
  Field,
65
82
  ]
66
83
 
@@ -72,9 +89,21 @@ Entry = Union[
72
89
  A value that can be stored in an OpenFOAM file.
73
90
  """
74
91
 
75
- MutableData = Union[
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
+
104
+ MutableEntry = Union[
76
105
  Data,
77
- MutableMapping[str, "MutableData"],
106
+ MutableMapping[str, "MutableEntry"],
78
107
  ]
79
108
 
80
109
  Dict_ = Dict[str, Union["Entry", "Dict_"]]
@@ -1,15 +1,15 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: foamlib
3
- Version: 0.7.3
3
+ Version: 0.8.1
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
- Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
5
  Project-URL: Homepage, https://github.com/gerlero/foamlib
7
6
  Project-URL: Repository, https://github.com/gerlero/foamlib
8
7
  Project-URL: Documentation, https://foamlib.readthedocs.io
8
+ Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
9
9
  Classifier: Development Status :: 4 - Beta
10
10
  Classifier: Framework :: AsyncIO
11
- Classifier: Intended Audience :: Science/Research
12
11
  Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
13
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
14
14
  Classifier: Operating System :: POSIX
15
15
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -24,34 +24,34 @@ Classifier: Topic :: Scientific/Engineering
24
24
  Classifier: Topic :: Software Development
25
25
  Classifier: Typing :: Typed
26
26
  Requires-Python: >=3.7
27
- Description-Content-Type: text/markdown
28
- License-File: LICENSE.txt
29
- Requires-Dist: aioshutil <2,>=1
30
- Requires-Dist: pyparsing <4,>=3
31
- Requires-Dist: typing-extensions <5,>=4 ; python_version < "3.11"
27
+ Requires-Dist: aioshutil<2,>=1
28
+ Requires-Dist: numpy<3,>=1
29
+ Requires-Dist: numpy<3,>=1.25.0; python_version >= '3.10'
30
+ Requires-Dist: pyparsing<4,>=3
31
+ Requires-Dist: typing-extensions<5,>=4; python_version < '3.11'
32
32
  Provides-Extra: dev
33
- Requires-Dist: foamlib[numpy] ; extra == 'dev'
34
- Requires-Dist: foamlib[lint] ; extra == 'dev'
35
- Requires-Dist: foamlib[test] ; extra == 'dev'
36
- Requires-Dist: foamlib[typing] ; extra == 'dev'
37
- Requires-Dist: foamlib[docs] ; extra == 'dev'
33
+ Requires-Dist: mypy<2,>=1; extra == 'dev'
34
+ Requires-Dist: pytest-asyncio<0.25,>=0.21; extra == 'dev'
35
+ Requires-Dist: pytest-cov; extra == 'dev'
36
+ Requires-Dist: pytest<9,>=7; extra == 'dev'
37
+ Requires-Dist: ruff; extra == 'dev'
38
+ Requires-Dist: sphinx-rtd-theme; extra == 'dev'
39
+ Requires-Dist: sphinx<9,>=5; extra == 'dev'
38
40
  Provides-Extra: docs
39
- Requires-Dist: foamlib[numpy] ; extra == 'docs'
40
- Requires-Dist: sphinx <9,>=5 ; extra == 'docs'
41
- Requires-Dist: sphinx-rtd-theme ; extra == 'docs'
41
+ Requires-Dist: sphinx-rtd-theme; extra == 'docs'
42
+ Requires-Dist: sphinx<9,>=5; extra == 'docs'
42
43
  Provides-Extra: lint
43
- Requires-Dist: ruff ; extra == 'lint'
44
- Provides-Extra: numpy
45
- Requires-Dist: numpy <3,>=1 ; extra == 'numpy'
46
- Requires-Dist: numpy <3,>=1.25.0 ; (python_version >= "3.10") and extra == 'numpy'
44
+ Requires-Dist: ruff; extra == 'lint'
47
45
  Provides-Extra: test
48
- Requires-Dist: foamlib[numpy] ; extra == 'test'
49
- Requires-Dist: pytest <9,>=7 ; extra == 'test'
50
- Requires-Dist: pytest-asyncio <0.25,>=0.21 ; extra == 'test'
51
- Requires-Dist: pytest-cov ; extra == 'test'
46
+ Requires-Dist: pytest-asyncio<0.25,>=0.21; extra == 'test'
47
+ Requires-Dist: pytest-cov; extra == 'test'
48
+ Requires-Dist: pytest<9,>=7; extra == 'test'
52
49
  Provides-Extra: typing
53
- Requires-Dist: foamlib[test] ; extra == 'typing'
54
- Requires-Dist: mypy <2,>=1 ; extra == 'typing'
50
+ Requires-Dist: mypy<2,>=1; extra == 'typing'
51
+ Requires-Dist: pytest-asyncio<0.25,>=0.21; extra == 'typing'
52
+ Requires-Dist: pytest-cov; extra == 'typing'
53
+ Requires-Dist: pytest<9,>=7; extra == 'typing'
54
+ Description-Content-Type: text/markdown
55
55
 
56
56
  [<img alt="foamlib" src="https://github.com/gerlero/foamlib/raw/main/logo.png" height="65">](https://github.com/gerlero/foamlib)
57
57
 
@@ -0,0 +1,20 @@
1
+ foamlib/__init__.py,sha256=TATCOFe7Qz20bMXgF1iX2nZmLzME7Wtg3gIocDfQTZk,452
2
+ foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ foamlib/_cases/__init__.py,sha256=_A1TTHuQfS9FH2_33lSEyLtOJZGFHZBco1tWJCVOHks,358
4
+ foamlib/_cases/_async.py,sha256=i6g4EBHqvI-1PkdrxsRto2ynW7sxsOga2bSYk1XVG1U,7795
5
+ foamlib/_cases/_base.py,sha256=37oBbM3NM-hpG7dKewZvyJNtqSAogMurcbmX-wLIgMU,6727
6
+ foamlib/_cases/_run.py,sha256=lveqKZium_qK_eTxYE8jOjwx0eiIoolCBbi56-zLw1o,14420
7
+ foamlib/_cases/_slurm.py,sha256=kj4wqgr3foMyAoUkoHOZODRBmVqH1B9KqAIEEjM8ZBg,2328
8
+ foamlib/_cases/_subprocess.py,sha256=6BlBRxknj2-BFcGkx7oVcuL63_utSaY1Axmsc1qV9j8,3887
9
+ foamlib/_cases/_sync.py,sha256=2BJXB7Nzldb4OgPukqupgYqdceUGkI2mYhhtGPWEBrc,5901
10
+ foamlib/_cases/_util.py,sha256=tK4SM5WT3eEgGsFLnidIySbom1qowBAua9z13gipKJk,1518
11
+ foamlib/_files/__init__.py,sha256=q1vkjXnjnSZvo45jPAICpWeF2LZv5V6xfzAR6S8fS5A,96
12
+ foamlib/_files/_files.py,sha256=hVzsAhUFukF7sGLUNp1wFUkzIMpRfrpKCKObLj8MKhA,15070
13
+ foamlib/_files/_io.py,sha256=BGbbm6HKxL2ka0YMCmHqZQZ1R4PPQlkvWWb4FHMAS8k,2217
14
+ foamlib/_files/_parsing.py,sha256=iKtreUueJ3NrE8a2cIpUf4zF7z3o4pJtKLufx0mnb3A,13986
15
+ foamlib/_files/_serialization.py,sha256=Vuq9Ityb55a7kheGCqeuMHyzddb4mXozglMsc90_3Cc,4746
16
+ foamlib/_files/_types.py,sha256=3l6MCwyK1wAv3B5xJsyF9Q4Ndgt-BF8zCd1N7_ZBxPM,2554
17
+ foamlib-0.8.1.dist-info/METADATA,sha256=izsNijMcEBsVrrgyWoLzx80Gi6LoJ5PAIM4zNVngxwk,7938
18
+ foamlib-0.8.1.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
19
+ foamlib-0.8.1.dist-info/licenses/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
20
+ foamlib-0.8.1.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: hatchling 1.26.3
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
foamlib/_files/_util.py DELETED
@@ -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)
@@ -1,22 +0,0 @@
1
- foamlib/__init__.py,sha256=GdEjgMru_5M1AG0ltkVKqNek6vw5aI4j_6QNApGRqsg,452
2
- foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- foamlib/_cases/__init__.py,sha256=wTUHcUgU1CBgpu0cUMtksQ5VKG6B8CFu9xc3dWwsQuo,358
4
- foamlib/_cases/_async.py,sha256=i6g4EBHqvI-1PkdrxsRto2ynW7sxsOga2bSYk1XVG1U,7795
5
- foamlib/_cases/_base.py,sha256=FKfZxP7HspWfSswQ6yZ5bGJRdZUlupQxj2tDqngXPmc,6785
6
- foamlib/_cases/_run.py,sha256=lveqKZium_qK_eTxYE8jOjwx0eiIoolCBbi56-zLw1o,14420
7
- foamlib/_cases/_slurm.py,sha256=kj4wqgr3foMyAoUkoHOZODRBmVqH1B9KqAIEEjM8ZBg,2328
8
- foamlib/_cases/_subprocess.py,sha256=6BlBRxknj2-BFcGkx7oVcuL63_utSaY1Axmsc1qV9j8,3887
9
- foamlib/_cases/_sync.py,sha256=2BJXB7Nzldb4OgPukqupgYqdceUGkI2mYhhtGPWEBrc,5901
10
- foamlib/_cases/_util.py,sha256=lhVca3ERY0zwYjDam6W2QMROt0yX5vAF-9_DS5RuMbM,1547
11
- foamlib/_files/__init__.py,sha256=GDkYkF3F-ADhkCRT3j9dQQHPP5LyJJYb8TaBbZTQ6fo,96
12
- foamlib/_files/_files.py,sha256=_qzXSsKRVXNrfySewonJJqL9I4OA8RTZuYyuWTGR4Ys,15829
13
- foamlib/_files/_io.py,sha256=IQLqoqnA1TpHf21NbUho2wsYWevyqC6MKo-wfpaObUU,2226
14
- foamlib/_files/_parsing.py,sha256=sY_Cyep9R7HfeTOKnKoAh9ZVXTnrJw3HGIgFFwUPGms,12541
15
- foamlib/_files/_serialization.py,sha256=GOhWIMHNf5MaLJUjbiRffNGQn1xVGPbyh_Mm50iz5e8,5847
16
- foamlib/_files/_types.py,sha256=mOOTXVrrD6MZGH64TmK1KX7WfUVBS8JLOuHTeeDerDQ,1729
17
- foamlib/_files/_util.py,sha256=lkoSJHXjd6MvDxx39ZF75mhPq-_QX9AjrruVcQ7I9WI,496
18
- foamlib-0.7.3.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
19
- foamlib-0.7.3.dist-info/METADATA,sha256=4yvR9-0fYVRRfbROkoO3HcmmtZVYI4QAqIC_NPqKE0A,7957
20
- foamlib-0.7.3.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
21
- foamlib-0.7.3.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
22
- foamlib-0.7.3.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- foamlib