foamlib 0.3.3__tar.gz → 0.3.5__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
5
  Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
6
  Project-URL: Homepage, https://github.com/gerlero/foamlib
@@ -58,11 +58,11 @@ Requires-Dist: numpy<2,>=1; extra == "docs"
58
58
 
59
59
  **foamlib** provides a simple, modern and ergonomic Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
60
60
 
61
- It offers the following classes (among a few others):
61
+ It offers the following classes:
62
62
 
63
+ * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile)): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s, using `foamlib`'s own parser. Supports both ASCII and binary field formats.
63
64
  * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
64
65
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
65
- * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s.
66
66
 
67
67
  ## Get started
68
68
 
@@ -130,6 +130,16 @@ async def run_case():
130
130
  asyncio.run(run_case())
131
131
  ```
132
132
 
133
+ ### Parse a field using the [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile) class directly
134
+
135
+ ```python
136
+ from foamlib import FoamFieldFile
137
+
138
+ U = FoamFieldFile(Path(my_pitz) / "0/U")
139
+
140
+ print(U.internal_field)
141
+ ```
142
+
133
143
  ## Documentation
134
144
 
135
145
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -11,11 +11,11 @@
11
11
 
12
12
  **foamlib** provides a simple, modern and ergonomic Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
13
13
 
14
- It offers the following classes (among a few others):
14
+ It offers the following classes:
15
15
 
16
+ * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile)): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s, using `foamlib`'s own parser. Supports both ASCII and binary field formats.
16
17
  * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
17
18
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
18
- * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s.
19
19
 
20
20
  ## Get started
21
21
 
@@ -83,6 +83,16 @@ async def run_case():
83
83
  asyncio.run(run_case())
84
84
  ```
85
85
 
86
+ ### Parse a field using the [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile) class directly
87
+
88
+ ```python
89
+ from foamlib import FoamFieldFile
90
+
91
+ U = FoamFieldFile(Path(my_pitz) / "0/U")
92
+
93
+ print(U.internal_field)
94
+ ```
95
+
86
96
  ## Documentation
87
97
 
88
98
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.3.3"
3
+ __version__ = "0.3.5"
4
4
 
5
5
  from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
6
6
  from ._files import FoamDict, FoamFieldFile, FoamFile
@@ -1,6 +1,5 @@
1
1
  import asyncio
2
2
  import multiprocessing
3
- import os
4
3
  import shutil
5
4
  import sys
6
5
  from contextlib import asynccontextmanager
@@ -17,13 +16,12 @@ if sys.version_info >= (3, 9):
17
16
  Callable,
18
17
  Collection,
19
18
  Iterator,
20
- Mapping,
21
19
  Sequence,
22
20
  Set,
23
21
  )
24
22
  else:
25
23
  from typing import AbstractSet as Set
26
- from typing import AsyncGenerator, Callable, Collection, Iterator, Mapping, Sequence
24
+ from typing import AsyncGenerator, Callable, Collection, Iterator, Sequence
27
25
 
28
26
  import aioshutil
29
27
 
@@ -87,7 +85,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
87
85
  return str(self.path)
88
86
 
89
87
  def __repr__(self) -> str:
90
- return f"{type(self).__name__}({self.path})"
88
+ return f"{type(self).__qualname__}('{self.path}')"
91
89
 
92
90
  def __str__(self) -> str:
93
91
  return str(self.path)
@@ -203,12 +201,6 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
203
201
  else:
204
202
  return None
205
203
 
206
- def _env(self) -> Mapping[str, str]:
207
- """Return the environment variables for this case."""
208
- env = os.environ.copy()
209
- env["PWD"] = str(self.path)
210
- return env
211
-
212
204
  def _parallel_cmd(
213
205
  self, cmd: Union[Sequence[Union[str, Path]], str, Path]
214
206
  ) -> Union[Sequence[Union[str, Path]], str]:
@@ -298,7 +290,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
298
290
  return str(self.path)
299
291
 
300
292
  def __repr__(self) -> str:
301
- return f"{type(self).__name__}({self.path})"
293
+ return f"{type(self).__qualname__}('{self.path}')"
302
294
 
303
295
  def __str__(self) -> str:
304
296
  return str(self.path)
@@ -359,7 +351,6 @@ class FoamCase(FoamCaseBase):
359
351
  cmd,
360
352
  check=check,
361
353
  cwd=self.path,
362
- env=self._env(),
363
354
  )
364
355
  else:
365
356
  script_path = self._run_script(parallel=parallel) if script else None
@@ -522,7 +513,6 @@ class AsyncFoamCase(FoamCaseBase):
522
513
  cmd,
523
514
  check=check,
524
515
  cwd=self.path,
525
- env=self._env(),
526
516
  )
527
517
  else:
528
518
  script_path = self._run_script(parallel=parallel) if script else None
@@ -1,6 +1,5 @@
1
1
  from ._base import FoamDict
2
- from ._fields import FoamFieldFile
3
- from ._files import FoamFile
2
+ from ._files import FoamFieldFile, FoamFile
4
3
 
5
4
  __all__ = [
6
5
  "FoamFile",
@@ -0,0 +1,340 @@
1
+ import sys
2
+ from typing import Any, Tuple, Union, cast
3
+
4
+ if sys.version_info >= (3, 9):
5
+ from collections.abc import Iterator, Mapping, MutableMapping, Sequence
6
+ else:
7
+ from typing import Iterator, Mapping, MutableMapping, Sequence
8
+
9
+ from ._base import FoamDict
10
+ from ._io import FoamFileIO
11
+ from ._serialization import Kind, dumpb
12
+
13
+ try:
14
+ import numpy as np
15
+ except ModuleNotFoundError:
16
+ pass
17
+
18
+
19
+ class FoamFile(
20
+ FoamDict,
21
+ MutableMapping[
22
+ Union[str, Tuple[str, ...]], Union["FoamFile.Data", "FoamFile.SubDict"]
23
+ ],
24
+ FoamFileIO,
25
+ ):
26
+ """
27
+ An OpenFOAM dictionary file.
28
+
29
+ Use as a mutable mapping (i.e., like a dict) to access and modify entries.
30
+
31
+ Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
32
+ """
33
+
34
+ class SubDict(
35
+ FoamDict,
36
+ MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
37
+ ):
38
+ """An OpenFOAM dictionary within a file as a mutable mapping."""
39
+
40
+ def __init__(self, _file: "FoamFile", _keywords: Tuple[str, ...]) -> None:
41
+ self._file = _file
42
+ self._keywords = _keywords
43
+
44
+ def __getitem__(
45
+ self, keyword: str
46
+ ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
47
+ return self._file[(*self._keywords, keyword)]
48
+
49
+ def __setitem__(
50
+ self,
51
+ keyword: str,
52
+ data: "FoamFile._SetData",
53
+ ) -> None:
54
+ self._file[(*self._keywords, keyword)] = data
55
+
56
+ def __delitem__(self, keyword: str) -> None:
57
+ del self._file[(*self._keywords, keyword)]
58
+
59
+ def __iter__(self) -> Iterator[str]:
60
+ return self._file._iter(self._keywords)
61
+
62
+ def __contains__(self, keyword: object) -> bool:
63
+ return (*self._keywords, keyword) in self._file
64
+
65
+ def __len__(self) -> int:
66
+ return len(list(iter(self)))
67
+
68
+ def update(self, *args: Any, **kwargs: Any) -> None:
69
+ with self._file:
70
+ super().update(*args, **kwargs)
71
+
72
+ def clear(self) -> None:
73
+ with self._file:
74
+ super().clear()
75
+
76
+ def __repr__(self) -> str:
77
+ return f"{type(self).__qualname__}('{self._file}', {self._keywords})"
78
+
79
+ def as_dict(self) -> FoamDict._Dict:
80
+ """Return a nested dict representation of the dictionary."""
81
+ ret = self._file.as_dict()
82
+
83
+ for k in self._keywords:
84
+ assert isinstance(ret, dict)
85
+ v = ret[k]
86
+ assert isinstance(v, dict)
87
+ ret = v
88
+
89
+ return ret
90
+
91
+ def __getitem__(
92
+ self, keywords: Union[str, Tuple[str, ...]]
93
+ ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
94
+ if not isinstance(keywords, tuple):
95
+ keywords = (keywords,)
96
+
97
+ _, parsed = self._read()
98
+
99
+ value = parsed[keywords]
100
+
101
+ if value is ...:
102
+ return FoamFile.SubDict(self, keywords)
103
+ else:
104
+ return value # type: ignore [return-value]
105
+
106
+ @property
107
+ def _binary(self) -> bool:
108
+ return self.get(("FoamFile", "format"), None) == "binary"
109
+
110
+ def __setitem__(
111
+ self,
112
+ keywords: Union[str, Tuple[str, ...]],
113
+ data: "FoamFile._SetData",
114
+ *,
115
+ assume_field: bool = False,
116
+ assume_dimensions: bool = False,
117
+ ) -> None:
118
+ if not isinstance(keywords, tuple):
119
+ keywords = (keywords,)
120
+
121
+ kind = Kind.DEFAULT
122
+ if keywords == ("internalField",) or (
123
+ len(keywords) == 3
124
+ and keywords[0] == "boundaryField"
125
+ and keywords[2] == "value"
126
+ ):
127
+ kind = Kind.BINARY_FIELD if self._binary else Kind.FIELD
128
+ elif keywords == ("dimensions",):
129
+ kind = Kind.DIMENSIONS
130
+
131
+ contents, parsed = self._read()
132
+
133
+ if isinstance(data, Mapping):
134
+ with self:
135
+ if isinstance(data, FoamDict):
136
+ data = data.as_dict()
137
+
138
+ start, end = parsed.entry_location(keywords, missing_ok=True)
139
+
140
+ self._write(
141
+ contents[:start]
142
+ + b"\n"
143
+ + dumpb({keywords[-1]: {}})
144
+ + b"\n"
145
+ + contents[end:]
146
+ )
147
+
148
+ for k, v in data.items():
149
+ self[(*keywords, k)] = v
150
+ else:
151
+ start, end = parsed.entry_location(keywords, missing_ok=True)
152
+
153
+ self._write(
154
+ contents[:start]
155
+ + b"\n"
156
+ + dumpb({keywords[-1]: data}, kind=kind)
157
+ + b"\n"
158
+ + contents[end:]
159
+ )
160
+
161
+ def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
162
+ if not isinstance(keywords, tuple):
163
+ keywords = (keywords,)
164
+
165
+ contents, parsed = self._read()
166
+
167
+ start, end = parsed.entry_location(keywords)
168
+
169
+ self._write(contents[:start] + contents[end:])
170
+
171
+ def _iter(self, keywords: Union[str, Tuple[str, ...]] = ()) -> Iterator[str]:
172
+ if not isinstance(keywords, tuple):
173
+ keywords = (keywords,)
174
+
175
+ _, parsed = self._read()
176
+
177
+ yield from (k[-1] for k in parsed if k[:-1] == keywords)
178
+
179
+ def __iter__(self) -> Iterator[str]:
180
+ return self._iter()
181
+
182
+ def __contains__(self, keywords: object) -> bool:
183
+ if not isinstance(keywords, tuple):
184
+ keywords = (keywords,)
185
+ _, parsed = self._read()
186
+ return keywords in parsed
187
+
188
+ def __len__(self) -> int:
189
+ return len(list(iter(self)))
190
+
191
+ def update(self, *args: Any, **kwargs: Any) -> None:
192
+ with self:
193
+ super().update(*args, **kwargs)
194
+
195
+ def clear(self) -> None:
196
+ with self:
197
+ super().clear()
198
+
199
+ def __fspath__(self) -> str:
200
+ return str(self.path)
201
+
202
+ def as_dict(self) -> FoamDict._Dict:
203
+ """Return a nested dict representation of the file."""
204
+ _, parsed = self._read()
205
+ return parsed.as_dict()
206
+
207
+
208
+ class FoamFieldFile(FoamFile):
209
+ """An OpenFOAM dictionary file representing a field as a mutable mapping."""
210
+
211
+ class BoundariesSubDict(FoamFile.SubDict):
212
+ def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundarySubDict":
213
+ value = super().__getitem__(keyword)
214
+ if not isinstance(value, FoamFieldFile.BoundarySubDict):
215
+ assert not isinstance(value, FoamFile.SubDict)
216
+ raise TypeError(f"boundary {keyword} is not a dictionary")
217
+ return value
218
+
219
+ class BoundarySubDict(FoamFile.SubDict):
220
+ """An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
221
+
222
+ @property
223
+ def type(self) -> str:
224
+ """Alias of `self["type"]`."""
225
+ ret = self["type"]
226
+ if not isinstance(ret, str):
227
+ raise TypeError("type is not a string")
228
+ return ret
229
+
230
+ @type.setter
231
+ def type(self, data: str) -> None:
232
+ self["type"] = data
233
+
234
+ @property
235
+ def value(
236
+ self,
237
+ ) -> Union[
238
+ int,
239
+ float,
240
+ Sequence[Union[int, float, Sequence[Union[int, float]]]],
241
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
242
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
243
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
244
+ ]:
245
+ """Alias of `self["value"]`."""
246
+ ret = self["value"]
247
+ if not isinstance(ret, (int, float, Sequence)):
248
+ raise TypeError("value is not a field")
249
+ return cast(
250
+ Union[
251
+ int, float, Sequence[Union[int, float, Sequence[Union[int, float]]]]
252
+ ],
253
+ ret,
254
+ )
255
+
256
+ @value.setter
257
+ def value(
258
+ self,
259
+ value: Union[
260
+ int,
261
+ float,
262
+ Sequence[Union[int, float, Sequence[Union[int, float]]]],
263
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
264
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
265
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
266
+ ],
267
+ ) -> None:
268
+ self["value"] = value
269
+
270
+ @value.deleter
271
+ def value(self) -> None:
272
+ del self["value"]
273
+
274
+ def __getitem__(
275
+ self, keywords: Union[str, Tuple[str, ...]]
276
+ ) -> Union[FoamFile.Data, FoamFile.SubDict]:
277
+ if not isinstance(keywords, tuple):
278
+ keywords = (keywords,)
279
+
280
+ ret = super().__getitem__(keywords)
281
+ if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.SubDict):
282
+ if len(keywords) == 1:
283
+ ret = FoamFieldFile.BoundariesSubDict(self, keywords)
284
+ elif len(keywords) == 2:
285
+ ret = FoamFieldFile.BoundarySubDict(self, keywords)
286
+ return ret
287
+
288
+ @property
289
+ def dimensions(self) -> Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]:
290
+ """Alias of `self["dimensions"]`."""
291
+ ret = self["dimensions"]
292
+ if not isinstance(ret, FoamFile.DimensionSet):
293
+ raise TypeError("dimensions is not a DimensionSet")
294
+ return ret
295
+
296
+ @dimensions.setter
297
+ def dimensions(
298
+ self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
299
+ ) -> None:
300
+ self["dimensions"] = value
301
+
302
+ @property
303
+ def internal_field(
304
+ self,
305
+ ) -> Union[
306
+ int,
307
+ float,
308
+ Sequence[Union[int, float, Sequence[Union[int, float]]]],
309
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
310
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
311
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
312
+ ]:
313
+ """Alias of `self["internalField"]`."""
314
+ ret = self["internalField"]
315
+ if not isinstance(ret, (int, float, Sequence)):
316
+ raise TypeError("internalField is not a field")
317
+ return cast(Union[int, float, Sequence[Union[int, float]]], ret)
318
+
319
+ @internal_field.setter
320
+ def internal_field(
321
+ self,
322
+ value: Union[
323
+ int,
324
+ float,
325
+ Sequence[Union[int, float, Sequence[Union[int, float]]]],
326
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
327
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
328
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
329
+ ],
330
+ ) -> None:
331
+ self["internalField"] = value
332
+
333
+ @property
334
+ def boundary_field(self) -> "FoamFieldFile.BoundariesSubDict":
335
+ """Alias of `self["boundaryField"]`."""
336
+ ret = self["boundaryField"]
337
+ if not isinstance(ret, FoamFieldFile.BoundariesSubDict):
338
+ assert not isinstance(ret, FoamFile.SubDict)
339
+ raise TypeError("boundaryField is not a dictionary")
340
+ return ret
@@ -25,7 +25,7 @@ class FoamFileIO:
25
25
  elif not self.path.is_file():
26
26
  raise FileNotFoundError(self.path)
27
27
 
28
- self.__contents: Optional[str] = None
28
+ self.__contents: Optional[bytes] = None
29
29
  self.__parsed: Optional[Parsed] = None
30
30
  self.__defer_io = 0
31
31
  self.__dirty = False
@@ -48,9 +48,9 @@ class FoamFileIO:
48
48
  self._write(self.__contents)
49
49
  assert not self.__dirty
50
50
 
51
- def _read(self) -> Tuple[str, Parsed]:
51
+ def _read(self) -> Tuple[bytes, Parsed]:
52
52
  if not self.__defer_io:
53
- contents = self.path.read_text()
53
+ contents = self.path.read_bytes()
54
54
  if contents != self.__contents:
55
55
  self.__contents = contents
56
56
  self.__parsed = None
@@ -63,11 +63,14 @@ class FoamFileIO:
63
63
 
64
64
  return self.__contents, deepcopy(self.__parsed)
65
65
 
66
- def _write(self, contents: str) -> None:
66
+ def _write(self, contents: bytes) -> None:
67
67
  self.__contents = contents
68
68
  self.__parsed = None
69
69
  if not self.__defer_io:
70
- self.path.write_text(contents)
70
+ self.path.write_bytes(contents)
71
71
  self.__dirty = False
72
72
  else:
73
73
  self.__dirty = True
74
+
75
+ def __repr__(self) -> str:
76
+ return f"{type(self).__qualname__}('{self.path}')"
@@ -1,3 +1,4 @@
1
+ import array
1
2
  import sys
2
3
  from typing import Tuple, Union
3
4
 
@@ -12,6 +13,7 @@ else:
12
13
  from typing import Any as EllipsisType
13
14
 
14
15
  from pyparsing import (
16
+ CharsNotIn,
15
17
  Dict,
16
18
  Forward,
17
19
  Group,
@@ -74,6 +76,56 @@ def _dictionary_of(
74
76
  return Dict(Group(keyword_entry)[len], asdict=not located)
75
77
 
76
78
 
79
+ _binary_contents = Forward()
80
+
81
+
82
+ def _binary_field_parse_action(tks: ParseResults) -> None:
83
+ global _binary_contents
84
+
85
+ kind, count = tks
86
+ if kind == "scalar":
87
+ elsize = 1
88
+ elif kind == "vector":
89
+ elsize = 3
90
+ elif kind == "symmTensor":
91
+ elsize = 6
92
+ elif kind == "tensor":
93
+ elsize = 9
94
+
95
+ def unpack(
96
+ tks: ParseResults,
97
+ ) -> Sequence[Union[Sequence[float], Sequence[Sequence[float]]]]:
98
+ bytes_ = tks[0].encode("latin-1")
99
+
100
+ arr = array.array("d", bytes_)
101
+
102
+ if elsize != 1:
103
+ all = [arr[i : i + elsize].tolist() for i in range(0, len(arr), elsize)]
104
+ else:
105
+ all = arr.tolist()
106
+
107
+ return [all]
108
+
109
+ _binary_contents <<= CharsNotIn(exact=count * elsize * 8).set_parse_action(unpack)
110
+
111
+ tks.clear() # type: ignore [no-untyped-call]
112
+
113
+
114
+ _BINARY_FIELD = (
115
+ (
116
+ Keyword("nonuniform").suppress()
117
+ + Literal("List").suppress()
118
+ + Literal("<").suppress()
119
+ + common.identifier
120
+ + Literal(">").suppress()
121
+ + common.integer
122
+ + Literal("(").suppress()
123
+ ).set_parse_action(_binary_field_parse_action, call_during_try=True)
124
+ + _binary_contents
125
+ + Literal(")").suppress()
126
+ )
127
+
128
+
77
129
  _SWITCH = (
78
130
  Keyword("yes") | Keyword("true") | Keyword("on") | Keyword("y") | Keyword("t")
79
131
  ).set_parse_action(lambda: True) | (
@@ -87,8 +139,10 @@ _IDENTIFIER = Word(identchars + "$", printables, exclude_chars=";")
87
139
  _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
88
140
  lambda tks: FoamDict.Dimensioned(*reversed(tks.as_list()))
89
141
  )
90
- _FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
91
- Keyword("nonuniform").suppress() + _list_of(_TENSOR)
142
+ _FIELD = (
143
+ (Keyword("uniform").suppress() + _TENSOR)
144
+ | (Keyword("nonuniform").suppress() + _list_of(_TENSOR))
145
+ | _BINARY_FIELD
92
146
  )
93
147
  _TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
94
148
  _DATA = Forward()
@@ -109,16 +163,19 @@ _FILE = (
109
163
  .ignore(c_style_comment)
110
164
  .ignore(cpp_style_comment)
111
165
  .ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
166
+ .parse_with_tabs()
112
167
  )
113
168
 
114
169
 
115
170
  class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
116
- def __init__(self, contents: str) -> None:
171
+ def __init__(self, contents: bytes) -> None:
117
172
  self._parsed: MutableMapping[
118
173
  Tuple[str, ...],
119
174
  Tuple[int, Union[FoamDict.Data, EllipsisType], int],
120
175
  ] = {}
121
- for parse_result in _FILE.parse_string(contents, parse_all=True):
176
+ for parse_result in _FILE.parse_string(
177
+ contents.decode("latin-1"), parse_all=True
178
+ ):
122
179
  self._parsed.update(self._flatten_result(parse_result))
123
180
 
124
181
  @staticmethod
@@ -0,0 +1,117 @@
1
+ import array
2
+ import itertools
3
+ import sys
4
+ from enum import Enum, auto
5
+
6
+ if sys.version_info >= (3, 9):
7
+ from collections.abc import Mapping
8
+ else:
9
+ from typing import Mapping
10
+
11
+ from .._util import is_sequence
12
+ from ._base import FoamDict
13
+
14
+ try:
15
+ import numpy as np
16
+
17
+ numpy = True
18
+ except ModuleNotFoundError:
19
+ numpy = False
20
+
21
+
22
+ class Kind(Enum):
23
+ DEFAULT = auto()
24
+ LIST_ENTRY = auto()
25
+ FIELD = auto()
26
+ BINARY_FIELD = auto()
27
+ DIMENSIONS = auto()
28
+
29
+
30
+ def dumpb(
31
+ data: FoamDict._SetData,
32
+ *,
33
+ kind: Kind = Kind.DEFAULT,
34
+ ) -> bytes:
35
+ if numpy and isinstance(data, np.ndarray):
36
+ return dumpb(data.tolist(), kind=kind)
37
+
38
+ elif isinstance(data, Mapping):
39
+ entries = []
40
+ for k, v in data.items():
41
+ b = dumpb(v, kind=kind)
42
+ if isinstance(v, Mapping):
43
+ entries.append(dumpb(k) + b"\n" + b"{\n" + b + b"\n}")
44
+ elif b:
45
+ entries.append(dumpb(k) + b" " + b + b";")
46
+ else:
47
+ entries.append(dumpb(k) + b";")
48
+
49
+ return b"\n".join(entries)
50
+
51
+ elif isinstance(data, FoamDict.DimensionSet) or (
52
+ kind == Kind.DIMENSIONS and is_sequence(data) and len(data) == 7
53
+ ):
54
+ return b"[" + b" ".join(dumpb(v) for v in data) + b"]"
55
+
56
+ elif (kind == Kind.FIELD or kind == Kind.BINARY_FIELD) and (
57
+ isinstance(data, (int, float))
58
+ or is_sequence(data)
59
+ and data
60
+ and isinstance(data[0], (int, float))
61
+ and len(data) in (3, 6, 9)
62
+ ):
63
+ return b"uniform " + dumpb(data)
64
+
65
+ elif (kind == Kind.FIELD or kind == Kind.BINARY_FIELD) and is_sequence(data):
66
+ if isinstance(data[0], (int, float)):
67
+ tensor_kind = b"scalar"
68
+ elif len(data[0]) == 3:
69
+ tensor_kind = b"vector"
70
+ elif len(data[0]) == 6:
71
+ tensor_kind = b"symmTensor"
72
+ elif len(data[0]) == 9:
73
+ tensor_kind = b"tensor"
74
+ else:
75
+ return dumpb(data)
76
+
77
+ if kind == Kind.BINARY_FIELD:
78
+ if tensor_kind == b"scalar":
79
+ contents = b"(" + array.array("d", data).tobytes() + b")"
80
+ else:
81
+ contents = (
82
+ b"("
83
+ + array.array("d", itertools.chain.from_iterable(data)).tobytes()
84
+ + b")"
85
+ )
86
+ else:
87
+ contents = dumpb(data)
88
+
89
+ return b"nonuniform List<" + tensor_kind + b"> " + dumpb(len(data)) + contents
90
+
91
+ elif kind != Kind.LIST_ENTRY and isinstance(data, tuple):
92
+ return b" ".join(dumpb(v) for v in data)
93
+
94
+ elif isinstance(data, FoamDict.Dimensioned):
95
+ if data.name is not None:
96
+ return (
97
+ dumpb(data.name)
98
+ + b" "
99
+ + dumpb(data.dimensions, kind=Kind.DIMENSIONS)
100
+ + b" "
101
+ + dumpb(data.value)
102
+ )
103
+ else:
104
+ return (
105
+ dumpb(data.dimensions, kind=Kind.DIMENSIONS) + b" " + dumpb(data.value)
106
+ )
107
+
108
+ elif is_sequence(data):
109
+ return b"(" + b" ".join(dumpb(v, kind=Kind.LIST_ENTRY) for v in data) + b")"
110
+
111
+ elif data is True:
112
+ return b"yes"
113
+ elif data is False:
114
+ return b"no"
115
+
116
+ else:
117
+ return str(data).encode("latin-1")
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import os
2
3
  import subprocess
3
4
  import sys
4
5
  from pathlib import Path
@@ -47,12 +48,20 @@ def _check(
47
48
  warn(f"Command {cmd} printed to stderr.\n{stderr}", CalledProcessWarning)
48
49
 
49
50
 
51
+ def _env(cwd: Optional[Union[str, Path]] = None) -> Optional[Mapping[str, str]]:
52
+ if cwd is not None:
53
+ env = os.environ.copy()
54
+ env["PWD"] = str(cwd)
55
+ return env
56
+ else:
57
+ return None
58
+
59
+
50
60
  def run_process(
51
61
  cmd: Union[Sequence[Union[str, Path]], str, Path],
52
62
  *,
53
63
  check: bool = True,
54
64
  cwd: Union[None, str, Path] = None,
55
- env: Union[None, Mapping[str, str]] = None,
56
65
  ) -> None:
57
66
  shell = not is_sequence(cmd)
58
67
 
@@ -65,8 +74,8 @@ def run_process(
65
74
  proc = subprocess.run(
66
75
  cmd,
67
76
  cwd=cwd,
68
- env=env,
69
- stdout=subprocess.DEVNULL,
77
+ env=_env(cwd),
78
+ stdout=None,
70
79
  stderr=subprocess.PIPE if check else subprocess.DEVNULL,
71
80
  text=True,
72
81
  shell=shell,
@@ -81,13 +90,12 @@ async def run_process_async(
81
90
  *,
82
91
  check: bool = True,
83
92
  cwd: Union[None, str, Path] = None,
84
- env: Union[None, Mapping[str, str]] = None,
85
93
  ) -> None:
86
94
  if not is_sequence(cmd):
87
95
  proc = await asyncio.create_subprocess_shell(
88
96
  str(cmd),
89
97
  cwd=cwd,
90
- env=env,
98
+ env=_env(cwd),
91
99
  stdout=asyncio.subprocess.DEVNULL,
92
100
  stderr=asyncio.subprocess.PIPE if check else asyncio.subprocess.DEVNULL,
93
101
  )
@@ -98,7 +106,7 @@ async def run_process_async(
98
106
  proc = await asyncio.create_subprocess_exec(
99
107
  *cmd,
100
108
  cwd=cwd,
101
- env=env,
109
+ env=_env(cwd),
102
110
  stdout=asyncio.subprocess.DEVNULL,
103
111
  stderr=asyncio.subprocess.PIPE if check else asyncio.subprocess.DEVNULL,
104
112
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.3.3
3
+ Version: 0.3.5
4
4
  Summary: A Python interface for interacting with OpenFOAM
5
5
  Author-email: "Gabriel S. Gerlero" <ggerlero@cimec.unl.edu.ar>
6
6
  Project-URL: Homepage, https://github.com/gerlero/foamlib
@@ -58,11 +58,11 @@ Requires-Dist: numpy<2,>=1; extra == "docs"
58
58
 
59
59
  **foamlib** provides a simple, modern and ergonomic Python interface for interacting with [OpenFOAM](https://www.openfoam.com).
60
60
 
61
- It offers the following classes (among a few others):
61
+ It offers the following classes:
62
62
 
63
+ * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile)): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s, using `foamlib`'s own parser. Supports both ASCII and binary field formats.
63
64
  * [`FoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
64
65
  * [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
65
- * [`FoamFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFile): read-write access to OpenFOAM configuration and field files as if they were Python `dict`s.
66
66
 
67
67
  ## Get started
68
68
 
@@ -130,6 +130,16 @@ async def run_case():
130
130
  asyncio.run(run_case())
131
131
  ```
132
132
 
133
+ ### Parse a field using the [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/#foamlib.FoamFieldFile) class directly
134
+
135
+ ```python
136
+ from foamlib import FoamFieldFile
137
+
138
+ U = FoamFieldFile(Path(my_pitz) / "0/U")
139
+
140
+ print(U.internal_field)
141
+ ```
142
+
133
143
  ## Documentation
134
144
 
135
145
  For more information, check out the [documentation](https://foamlib.readthedocs.io/).
@@ -12,7 +12,6 @@ foamlib.egg-info/requires.txt
12
12
  foamlib.egg-info/top_level.txt
13
13
  foamlib/_files/__init__.py
14
14
  foamlib/_files/_base.py
15
- foamlib/_files/_fields.py
16
15
  foamlib/_files/_files.py
17
16
  foamlib/_files/_io.py
18
17
  foamlib/_files/_parsing.py
@@ -1,165 +0,0 @@
1
- import sys
2
- from typing import Any, Tuple, Union, cast
3
-
4
- if sys.version_info >= (3, 9):
5
- from collections.abc import Sequence
6
- else:
7
- from typing import Sequence
8
-
9
- from ._files import FoamFile
10
-
11
- try:
12
- import numpy as np
13
- except ModuleNotFoundError:
14
- pass
15
-
16
-
17
- class FoamFieldFile(FoamFile):
18
- """An OpenFOAM dictionary file representing a field as a mutable mapping."""
19
-
20
- class BoundariesSubDict(FoamFile.SubDict):
21
- def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundarySubDict":
22
- value = super().__getitem__(keyword)
23
- if not isinstance(value, FoamFieldFile.BoundarySubDict):
24
- assert not isinstance(value, FoamFile.SubDict)
25
- raise TypeError(f"boundary {keyword} is not a dictionary")
26
- return value
27
-
28
- class BoundarySubDict(FoamFile.SubDict):
29
- """An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
30
-
31
- def __setitem__(
32
- self,
33
- keyword: str,
34
- data: FoamFile._SetData,
35
- ) -> None:
36
- if keyword == "value":
37
- self._setitem(keyword, data, assume_field=True)
38
- else:
39
- self._setitem(keyword, data)
40
-
41
- @property
42
- def type(self) -> str:
43
- """Alias of `self["type"]`."""
44
- ret = self["type"]
45
- if not isinstance(ret, str):
46
- raise TypeError("type is not a string")
47
- return ret
48
-
49
- @type.setter
50
- def type(self, data: str) -> None:
51
- self["type"] = data
52
-
53
- @property
54
- def value(
55
- self,
56
- ) -> Union[
57
- int,
58
- float,
59
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
60
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
61
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
62
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
63
- ]:
64
- """Alias of `self["value"]`."""
65
- ret = self["value"]
66
- if not isinstance(ret, (int, float, Sequence)):
67
- raise TypeError("value is not a field")
68
- return cast(Union[int, float, Sequence[Union[int, float]]], ret)
69
-
70
- @value.setter
71
- def value(
72
- self,
73
- value: Union[
74
- int,
75
- float,
76
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
77
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
78
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
79
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
80
- ],
81
- ) -> None:
82
- self["value"] = value
83
-
84
- @value.deleter
85
- def value(self) -> None:
86
- del self["value"]
87
-
88
- def __getitem__(
89
- self, keywords: Union[str, Tuple[str, ...]]
90
- ) -> Union[FoamFile.Data, FoamFile.SubDict]:
91
- if not isinstance(keywords, tuple):
92
- keywords = (keywords,)
93
-
94
- ret = super().__getitem__(keywords)
95
- if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.SubDict):
96
- if len(keywords) == 1:
97
- ret = FoamFieldFile.BoundariesSubDict(self, keywords)
98
- elif len(keywords) == 2:
99
- ret = FoamFieldFile.BoundarySubDict(self, keywords)
100
- return ret
101
-
102
- def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
103
- if not isinstance(keywords, tuple):
104
- keywords = (keywords,)
105
-
106
- if keywords == ("internalField",):
107
- self._setitem(keywords, value, assume_field=True)
108
- elif keywords == ("dimensions",):
109
- self._setitem(keywords, value, assume_dimensions=True)
110
- else:
111
- self._setitem(keywords, value)
112
-
113
- @property
114
- def dimensions(self) -> FoamFile.DimensionSet:
115
- """Alias of `self["dimensions"]`."""
116
- ret = self["dimensions"]
117
- if not isinstance(ret, FoamFile.DimensionSet):
118
- raise TypeError("dimensions is not a DimensionSet")
119
- return ret
120
-
121
- @dimensions.setter
122
- def dimensions(
123
- self, value: Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]
124
- ) -> None:
125
- self["dimensions"] = value
126
-
127
- @property
128
- def internal_field(
129
- self,
130
- ) -> Union[
131
- int,
132
- float,
133
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
134
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
135
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
136
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
137
- ]:
138
- """Alias of `self["internalField"]`."""
139
- ret = self["internalField"]
140
- if not isinstance(ret, (int, float, Sequence)):
141
- raise TypeError("internalField is not a field")
142
- return cast(Union[int, float, Sequence[Union[int, float]]], ret)
143
-
144
- @internal_field.setter
145
- def internal_field(
146
- self,
147
- value: Union[
148
- int,
149
- float,
150
- Sequence[Union[int, float, Sequence[Union[int, float]]]],
151
- "np.ndarray[Tuple[()], np.dtype[np.generic]]",
152
- "np.ndarray[Tuple[int], np.dtype[np.generic]]",
153
- "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
154
- ],
155
- ) -> None:
156
- self["internalField"] = value
157
-
158
- @property
159
- def boundary_field(self) -> "FoamFieldFile.BoundariesSubDict":
160
- """Alias of `self["boundaryField"]`."""
161
- ret = self["boundaryField"]
162
- if not isinstance(ret, FoamFieldFile.BoundariesSubDict):
163
- assert not isinstance(ret, FoamFile.SubDict)
164
- raise TypeError("boundaryField is not a dictionary")
165
- return ret
@@ -1,203 +0,0 @@
1
- import sys
2
- from typing import (
3
- Any,
4
- Tuple,
5
- Union,
6
- )
7
-
8
- if sys.version_info >= (3, 9):
9
- from collections.abc import Iterator, Mapping, MutableMapping
10
- else:
11
- from typing import Iterator, Mapping, MutableMapping
12
-
13
- from ._base import FoamDict
14
- from ._io import FoamFileIO
15
- from ._serialization import dumps
16
-
17
-
18
- class FoamFile(
19
- FoamDict,
20
- MutableMapping[
21
- Union[str, Tuple[str, ...]], Union["FoamFile.Data", "FoamFile.SubDict"]
22
- ],
23
- FoamFileIO,
24
- ):
25
- """
26
- An OpenFOAM dictionary file.
27
-
28
- Use as a mutable mapping (i.e., like a dict) to access and modify entries.
29
-
30
- Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
31
- """
32
-
33
- class SubDict(
34
- FoamDict,
35
- MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
36
- ):
37
- """An OpenFOAM dictionary within a file as a mutable mapping."""
38
-
39
- def __init__(self, _file: "FoamFile", _keywords: Tuple[str, ...]) -> None:
40
- self._file = _file
41
- self._keywords = _keywords
42
-
43
- def __getitem__(
44
- self, keyword: str
45
- ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
46
- return self._file[(*self._keywords, keyword)]
47
-
48
- def _setitem(
49
- self,
50
- keyword: str,
51
- data: Any,
52
- *,
53
- assume_field: bool = False,
54
- assume_dimensions: bool = False,
55
- ) -> None:
56
- self._file._setitem(
57
- (*self._keywords, keyword),
58
- data,
59
- assume_field=assume_field,
60
- assume_dimensions=assume_dimensions,
61
- )
62
-
63
- def __setitem__(self, keyword: str, value: "FoamFile._SetData") -> None:
64
- self._setitem(keyword, value)
65
-
66
- def __delitem__(self, keyword: str) -> None:
67
- del self._file[(*self._keywords, keyword)]
68
-
69
- def __iter__(self) -> Iterator[str]:
70
- return self._file._iter(self._keywords)
71
-
72
- def __contains__(self, keyword: object) -> bool:
73
- return (*self._keywords, keyword) in self._file
74
-
75
- def __len__(self) -> int:
76
- return len(list(iter(self)))
77
-
78
- def update(self, *args: Any, **kwargs: Any) -> None:
79
- with self._file:
80
- super().update(*args, **kwargs)
81
-
82
- def clear(self) -> None:
83
- with self._file:
84
- super().clear()
85
-
86
- def __repr__(self) -> str:
87
- return f"{type(self).__qualname__}({self._file}, {self._keywords})"
88
-
89
- def as_dict(self) -> FoamDict._Dict:
90
- """Return a nested dict representation of the dictionary."""
91
- ret = self._file.as_dict()
92
-
93
- for k in self._keywords:
94
- assert isinstance(ret, dict)
95
- v = ret[k]
96
- assert isinstance(v, dict)
97
- ret = v
98
-
99
- return ret
100
-
101
- def __getitem__(
102
- self, keywords: Union[str, Tuple[str, ...]]
103
- ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
104
- if not isinstance(keywords, tuple):
105
- keywords = (keywords,)
106
-
107
- _, parsed = self._read()
108
-
109
- value = parsed[keywords]
110
-
111
- if value is ...:
112
- return FoamFile.SubDict(self, keywords)
113
- else:
114
- return value # type: ignore [return-value]
115
-
116
- def _setitem(
117
- self,
118
- keywords: Union[str, Tuple[str, ...]],
119
- data: "FoamFile._SetData",
120
- *,
121
- assume_field: bool = False,
122
- assume_dimensions: bool = False,
123
- ) -> None:
124
- if not isinstance(keywords, tuple):
125
- keywords = (keywords,)
126
-
127
- contents, parsed = self._read()
128
-
129
- if isinstance(data, Mapping):
130
- with self:
131
- if isinstance(data, FoamDict):
132
- data = data.as_dict()
133
-
134
- start, end = parsed.entry_location(keywords, missing_ok=True)
135
-
136
- self._write(
137
- f"{contents[:start]}\n{dumps({keywords[-1]: {}})}\n{contents[end:]}"
138
- )
139
-
140
- for k, v in data.items():
141
- self[(*keywords, k)] = v
142
- else:
143
- start, end = parsed.entry_location(keywords, missing_ok=True)
144
-
145
- self._write(
146
- f"{contents[:start]}\n{dumps({keywords[-1]: data}, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
147
- )
148
-
149
- def __setitem__(
150
- self,
151
- keywords: Union[str, Tuple[str, ...]],
152
- data: "FoamFile._SetData",
153
- ) -> None:
154
- self._setitem(keywords, data)
155
-
156
- def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
157
- if not isinstance(keywords, tuple):
158
- keywords = (keywords,)
159
-
160
- contents, parsed = self._read()
161
-
162
- start, end = parsed.entry_location(keywords)
163
-
164
- self._write(contents[:start] + contents[end:])
165
-
166
- def _iter(self, keywords: Union[str, Tuple[str, ...]] = ()) -> Iterator[str]:
167
- if not isinstance(keywords, tuple):
168
- keywords = (keywords,)
169
-
170
- _, parsed = self._read()
171
-
172
- yield from (k[-1] for k in parsed if k[:-1] == keywords)
173
-
174
- def __iter__(self) -> Iterator[str]:
175
- return self._iter()
176
-
177
- def __contains__(self, keywords: object) -> bool:
178
- if not isinstance(keywords, tuple):
179
- keywords = (keywords,)
180
- _, parsed = self._read()
181
- return keywords in parsed
182
-
183
- def __len__(self) -> int:
184
- return len(list(iter(self)))
185
-
186
- def update(self, *args: Any, **kwargs: Any) -> None:
187
- with self:
188
- super().update(*args, **kwargs)
189
-
190
- def clear(self) -> None:
191
- with self:
192
- super().clear()
193
-
194
- def __fspath__(self) -> str:
195
- return str(self.path)
196
-
197
- def __repr__(self) -> str:
198
- return f"{type(self).__name__}({self.path})"
199
-
200
- def as_dict(self) -> FoamDict._Dict:
201
- """Return a nested dict representation of the file."""
202
- _, parsed = self._read()
203
- return parsed.as_dict()
@@ -1,99 +0,0 @@
1
- import sys
2
-
3
- if sys.version_info >= (3, 9):
4
- from collections.abc import Mapping
5
- else:
6
- from typing import Mapping
7
-
8
- from .._util import is_sequence
9
- from ._base import FoamDict
10
-
11
- try:
12
- import numpy as np
13
-
14
- numpy = True
15
- except ModuleNotFoundError:
16
- numpy = False
17
-
18
-
19
- def dumps(
20
- data: FoamDict._SetData,
21
- *,
22
- assume_field: bool = False,
23
- assume_dimensions: bool = False,
24
- assume_data_entries: bool = False,
25
- ) -> str:
26
- if numpy and isinstance(data, np.ndarray):
27
- return dumps(
28
- data.tolist(),
29
- assume_field=assume_field,
30
- assume_dimensions=assume_dimensions,
31
- )
32
-
33
- elif isinstance(data, Mapping):
34
- entries = []
35
- for k, v in data.items():
36
- s = dumps(
37
- v,
38
- assume_field=assume_field,
39
- assume_dimensions=assume_dimensions,
40
- assume_data_entries=True,
41
- )
42
- if isinstance(v, Mapping):
43
- entries.append(f"{k}\n{{\n{s}\n}}")
44
- elif s:
45
- entries.append(f"{k} {s};")
46
- else:
47
- entries.append(f"{k};")
48
- return "\n".join(entries)
49
-
50
- elif isinstance(data, FoamDict.DimensionSet) or (
51
- assume_dimensions and is_sequence(data) and len(data) == 7
52
- ):
53
- return f"[{' '.join(str(v) for v in data)}]"
54
-
55
- elif assume_field and isinstance(data, (int, float)):
56
- return f"uniform {data}"
57
-
58
- elif assume_field and is_sequence(data):
59
- if isinstance(data[0], (int, float)) and len(data) in (3, 6, 9):
60
- return f"uniform {dumps(data)}"
61
- elif isinstance(data[0], (int, float)):
62
- return f"nonuniform List<scalar> {len(data)}{dumps(data)}"
63
- elif len(data[0]) == 3:
64
- return f"nonuniform List<vector> {len(data)}{dumps(data)}"
65
- elif len(data[0]) == 6:
66
- return f"nonuniform List<symmTensor> {len(data)}{dumps(data)}"
67
- elif len(data[0]) == 9:
68
- return f"nonuniform List<tensor> {len(data)}{dumps(data)}"
69
- else:
70
- return dumps(
71
- data,
72
- assume_dimensions=assume_dimensions,
73
- assume_data_entries=assume_data_entries,
74
- )
75
-
76
- elif assume_data_entries and isinstance(data, tuple):
77
- return " ".join(
78
- dumps(v, assume_field=assume_field, assume_dimensions=assume_dimensions)
79
- for v in data
80
- )
81
-
82
- elif isinstance(data, FoamDict.Dimensioned):
83
- if data.name is not None:
84
- return f"{data.name} {dumps(data.dimensions, assume_dimensions=True)} {dumps(data.value)}"
85
- else:
86
- return (
87
- f"{dumps(data.dimensions, assume_dimensions=True)} {dumps(data.value)}"
88
- )
89
-
90
- elif is_sequence(data):
91
- return f"({' '.join(dumps(v) for v in data)})"
92
-
93
- elif data is True:
94
- return "yes"
95
- elif data is False:
96
- return "no"
97
-
98
- else:
99
- return str(data)
File without changes
File without changes
File without changes
File without changes
File without changes