foamlib 0.3.2__py3-none-any.whl → 0.3.4__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.3.2"
3
+ __version__ = "0.3.4"
4
4
 
5
5
  from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
6
6
  from ._files import FoamDict, FoamFieldFile, FoamFile
foamlib/_cases.py CHANGED
@@ -32,7 +32,7 @@ from ._util import is_sequence, run_process, run_process_async
32
32
 
33
33
 
34
34
  class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
35
- def __init__(self, path: Union[Path, str]):
35
+ def __init__(self, path: Union[Path, str] = Path()):
36
36
  self.path = Path(path).absolute()
37
37
  if not self.path.is_dir():
38
38
  raise NotADirectoryError(f"{self.path} is not a directory")
@@ -87,7 +87,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
87
87
  return str(self.path)
88
88
 
89
89
  def __repr__(self) -> str:
90
- return f"{type(self).__name__}({self.path})"
90
+ return f"{type(self).__name__}('{self.path}')"
91
91
 
92
92
  def __str__(self) -> str:
93
93
  return str(self.path)
@@ -298,7 +298,7 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
298
298
  return str(self.path)
299
299
 
300
300
  def __repr__(self) -> str:
301
- return f"{type(self).__name__}({self.path})"
301
+ return f"{type(self).__name__}('{self.path}')"
302
302
 
303
303
  def __str__(self) -> str:
304
304
  return str(self.path)
foamlib/_files/_fields.py CHANGED
@@ -65,7 +65,12 @@ class FoamFieldFile(FoamFile):
65
65
  ret = self["value"]
66
66
  if not isinstance(ret, (int, float, Sequence)):
67
67
  raise TypeError("value is not a field")
68
- return cast(Union[int, float, Sequence[Union[int, float]]], ret)
68
+ return cast(
69
+ Union[
70
+ int, float, Sequence[Union[int, float, Sequence[Union[int, float]]]]
71
+ ],
72
+ ret,
73
+ )
69
74
 
70
75
  @value.setter
71
76
  def value(
@@ -111,7 +116,7 @@ class FoamFieldFile(FoamFile):
111
116
  self._setitem(keywords, value)
112
117
 
113
118
  @property
114
- def dimensions(self) -> FoamFile.DimensionSet:
119
+ def dimensions(self) -> Union[FoamFile.DimensionSet, Sequence[Union[int, float]]]:
115
120
  """Alias of `self["dimensions"]`."""
116
121
  ret = self["dimensions"]
117
122
  if not isinstance(ret, FoamFile.DimensionSet):
foamlib/_files/_files.py CHANGED
@@ -12,7 +12,7 @@ else:
12
12
 
13
13
  from ._base import FoamDict
14
14
  from ._io import FoamFileIO
15
- from ._serialization import dumps
15
+ from ._serialization import dumpb
16
16
 
17
17
 
18
18
  class FoamFile(
@@ -84,7 +84,7 @@ class FoamFile(
84
84
  super().clear()
85
85
 
86
86
  def __repr__(self) -> str:
87
- return f"{type(self).__qualname__}({self._file}, {self._keywords})"
87
+ return f"{type(self).__qualname__}('{self._file}', {self._keywords})"
88
88
 
89
89
  def as_dict(self) -> FoamDict._Dict:
90
90
  """Return a nested dict representation of the dictionary."""
@@ -113,6 +113,10 @@ class FoamFile(
113
113
  else:
114
114
  return value # type: ignore [return-value]
115
115
 
116
+ @property
117
+ def _binary(self) -> bool:
118
+ return self.get(("FoamFile", "format"), None) == "binary"
119
+
116
120
  def _setitem(
117
121
  self,
118
122
  keywords: Union[str, Tuple[str, ...]],
@@ -134,7 +138,11 @@ class FoamFile(
134
138
  start, end = parsed.entry_location(keywords, missing_ok=True)
135
139
 
136
140
  self._write(
137
- f"{contents[:start]}\n{dumps({keywords[-1]: {}})}\n{contents[end:]}"
141
+ contents[:start]
142
+ + b"\n"
143
+ + dumpb({keywords[-1]: {}})
144
+ + b"\n"
145
+ + contents[end:]
138
146
  )
139
147
 
140
148
  for k, v in data.items():
@@ -143,7 +151,16 @@ class FoamFile(
143
151
  start, end = parsed.entry_location(keywords, missing_ok=True)
144
152
 
145
153
  self._write(
146
- f"{contents[:start]}\n{dumps({keywords[-1]: data}, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
154
+ contents[:start]
155
+ + b"\n"
156
+ + dumpb(
157
+ {keywords[-1]: data},
158
+ assume_field=assume_field,
159
+ assume_dimensions=assume_dimensions,
160
+ binary_fields=self._binary,
161
+ )
162
+ + b"\n"
163
+ + contents[end:]
147
164
  )
148
165
 
149
166
  def __setitem__(
@@ -194,9 +211,6 @@ class FoamFile(
194
211
  def __fspath__(self) -> str:
195
212
  return str(self.path)
196
213
 
197
- def __repr__(self) -> str:
198
- return f"{type(self).__name__}({self.path})"
199
-
200
214
  def as_dict(self) -> FoamDict._Dict:
201
215
  """Return a nested dict representation of the file."""
202
216
  _, parsed = self._read()
foamlib/_files/_io.py CHANGED
@@ -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).__name__}('{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,
@@ -27,7 +29,7 @@ from pyparsing import (
27
29
  c_style_comment,
28
30
  common,
29
31
  cpp_style_comment,
30
- identbodychars,
32
+ identchars,
31
33
  printables,
32
34
  )
33
35
 
@@ -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) | (
@@ -83,12 +135,14 @@ _DIMENSIONS = (
83
135
  Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
84
136
  ).set_parse_action(lambda tks: FoamDict.DimensionSet(*tks))
85
137
  _TENSOR = _list_of(common.number) | common.number
86
- _IDENTIFIER = Word(identbodychars + "$", printables.replace(";", ""))
138
+ _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
@@ -1,3 +1,5 @@
1
+ import array
2
+ import itertools
1
3
  import sys
2
4
 
3
5
  if sys.version_info >= (3, 9):
@@ -16,84 +18,121 @@ except ModuleNotFoundError:
16
18
  numpy = False
17
19
 
18
20
 
19
- def dumps(
21
+ def dumpb(
20
22
  data: FoamDict._SetData,
21
23
  *,
22
24
  assume_field: bool = False,
23
25
  assume_dimensions: bool = False,
24
26
  assume_data_entries: bool = False,
25
- ) -> str:
27
+ binary_fields: bool = False,
28
+ ) -> bytes:
26
29
  if numpy and isinstance(data, np.ndarray):
27
- return dumps(
30
+ return dumpb(
28
31
  data.tolist(),
29
32
  assume_field=assume_field,
30
33
  assume_dimensions=assume_dimensions,
34
+ assume_data_entries=assume_data_entries,
35
+ binary_fields=binary_fields,
31
36
  )
32
37
 
33
38
  elif isinstance(data, Mapping):
34
39
  entries = []
35
40
  for k, v in data.items():
36
- s = dumps(
41
+ b = dumpb(
37
42
  v,
38
43
  assume_field=assume_field,
39
44
  assume_dimensions=assume_dimensions,
40
45
  assume_data_entries=True,
46
+ binary_fields=binary_fields,
41
47
  )
42
48
  if isinstance(v, Mapping):
43
- entries.append(f"{k}\n{{\n{s}\n}}")
44
- elif s:
45
- entries.append(f"{k} {s};")
49
+ entries.append(dumpb(k) + b"\n" + b"{\n" + b + b"\n}")
50
+ elif b:
51
+ entries.append(dumpb(k) + b" " + b + b";")
46
52
  else:
47
- entries.append(f"{k};")
48
- return "\n".join(entries)
53
+ entries.append(dumpb(k) + b";")
54
+
55
+ return b"\n".join(entries)
49
56
 
50
57
  elif isinstance(data, FoamDict.DimensionSet) or (
51
58
  assume_dimensions and is_sequence(data) and len(data) == 7
52
59
  ):
53
- return f"[{' '.join(str(v) for v in data)}]"
60
+ return b"[" + b" ".join(dumpb(v) for v in data) + b"]"
54
61
 
55
- elif assume_field and isinstance(data, (int, float)):
56
- return f"uniform {data}"
62
+ elif assume_field and (
63
+ isinstance(data, (int, float))
64
+ or is_sequence(data)
65
+ and data
66
+ and isinstance(data[0], (int, float))
67
+ and len(data) in (3, 6, 9)
68
+ ):
69
+ return b"uniform " + dumpb(data)
57
70
 
58
71
  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)}"
72
+ if isinstance(data[0], (int, float)):
73
+ kind = b"scalar"
63
74
  elif len(data[0]) == 3:
64
- return f"nonuniform List<vector> {len(data)}{dumps(data)}"
75
+ kind = b"vector"
65
76
  elif len(data[0]) == 6:
66
- return f"nonuniform List<symmTensor> {len(data)}{dumps(data)}"
77
+ kind = b"symmTensor"
67
78
  elif len(data[0]) == 9:
68
- return f"nonuniform List<tensor> {len(data)}{dumps(data)}"
79
+ kind = b"tensor"
69
80
  else:
70
- return dumps(
81
+ return dumpb(
71
82
  data,
72
83
  assume_dimensions=assume_dimensions,
73
84
  assume_data_entries=assume_data_entries,
85
+ binary_fields=binary_fields,
74
86
  )
75
87
 
88
+ if binary_fields:
89
+ if kind == b"scalar":
90
+ contents = b"(" + array.array("d", data).tobytes() + b")"
91
+ else:
92
+ contents = (
93
+ b"("
94
+ + array.array("d", itertools.chain.from_iterable(data)).tobytes()
95
+ + b")"
96
+ )
97
+ else:
98
+ contents = dumpb(data)
99
+
100
+ return b"nonuniform List<" + kind + b"> " + dumpb(len(data)) + contents
101
+
76
102
  elif assume_data_entries and isinstance(data, tuple):
77
- return " ".join(
78
- dumps(v, assume_field=assume_field, assume_dimensions=assume_dimensions)
103
+ return b" ".join(
104
+ dumpb(
105
+ v,
106
+ assume_field=assume_field,
107
+ assume_dimensions=assume_dimensions,
108
+ binary_fields=binary_fields,
109
+ )
79
110
  for v in data
80
111
  )
81
112
 
82
113
  elif isinstance(data, FoamDict.Dimensioned):
83
114
  if data.name is not None:
84
- return f"{data.name} {dumps(data.dimensions, assume_dimensions=True)} {dumps(data.value)}"
115
+ return (
116
+ dumpb(data.name)
117
+ + b" "
118
+ + dumpb(data.dimensions, assume_dimensions=True)
119
+ + b" "
120
+ + dumpb(data.value)
121
+ )
85
122
  else:
86
123
  return (
87
- f"{dumps(data.dimensions, assume_dimensions=True)} {dumps(data.value)}"
124
+ dumpb(data.dimensions, assume_dimensions=True)
125
+ + b" "
126
+ + dumpb(data.value)
88
127
  )
89
128
 
90
129
  elif is_sequence(data):
91
- return f"({' '.join(dumps(v) for v in data)})"
130
+ return b"(" + b" ".join(dumpb(v) for v in data) + b")"
92
131
 
93
132
  elif data is True:
94
- return "yes"
133
+ return b"yes"
95
134
  elif data is False:
96
- return "no"
135
+ return b"no"
97
136
 
98
137
  else:
99
- return str(data)
138
+ return str(data).encode("latin-1")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.3.2
3
+ Version: 0.3.4
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
@@ -0,0 +1,16 @@
1
+ foamlib/__init__.py,sha256=1kmwoS1I2xRIk3KC9kFbsnLNljDVG3TOtdq0l9bnKE4,431
2
+ foamlib/_cases.py,sha256=xR_m_4-YJbYfbIwhw17F6ieHw6BLCRgneDa4-xOoEf8,20567
3
+ foamlib/_util.py,sha256=0HJ9n8QG9K7jC_xUDzOJo_NA2DBCJKJRLpmd5Yu42kU,2901
4
+ foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ foamlib/_files/__init__.py,sha256=m4y_7wFV2Voly1aJrTYALjT3S1Aq7EbaijxdGFbJrmU,160
6
+ foamlib/_files/_base.py,sha256=YA5a-i5HZuA3JslCD6r-DwZzpSA8r42dqSXef286Ako,2050
7
+ foamlib/_files/_fields.py,sha256=kY8EKNYp89asDOQjr-U9UmCqC2_Tt11zMfTQSWTvoms,5782
8
+ foamlib/_files/_files.py,sha256=0xQ7Y-TEOgnbCq0X781gtX2RMxw7Xu024RXbQshmt6I,6293
9
+ foamlib/_files/_io.py,sha256=GaOrwJt_upUyNPgeRXXrooppto82dUwAK2C9jNFqSNc,2071
10
+ foamlib/_files/_parsing.py,sha256=-zSpEDjuPxzOdsLWaeQl7j6FZHyw2-jXNgOCXkcGskU,7308
11
+ foamlib/_files/_serialization.py,sha256=WBDGt_P0FJ6X2uzav2JGvUHDhhyoRQ40cSF4kOK9qhw,3879
12
+ foamlib-0.3.4.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
13
+ foamlib-0.3.4.dist-info/METADATA,sha256=IVi0yNXJRfXYBxNur8KU46Jg6EOOXvfZtEf8g4O_C3w,4650
14
+ foamlib-0.3.4.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
+ foamlib-0.3.4.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
16
+ foamlib-0.3.4.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- foamlib/__init__.py,sha256=RKXFmt6ZgoSEiW8H5loLPjL4VY_2oIla-Dd0C7pEUKY,431
2
- foamlib/_cases.py,sha256=SkUthKb98zXPMYACaGyfGC0Fo1jzD6cC4qEGesHfNfc,20554
3
- foamlib/_util.py,sha256=0HJ9n8QG9K7jC_xUDzOJo_NA2DBCJKJRLpmd5Yu42kU,2901
4
- foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- foamlib/_files/__init__.py,sha256=m4y_7wFV2Voly1aJrTYALjT3S1Aq7EbaijxdGFbJrmU,160
6
- foamlib/_files/_base.py,sha256=YA5a-i5HZuA3JslCD6r-DwZzpSA8r42dqSXef286Ako,2050
7
- foamlib/_files/_fields.py,sha256=jVHOHEV70i6QrWzqhF_W5CywUZyJxN5Eh5S1MrNn1G0,5632
8
- foamlib/_files/_files.py,sha256=aS8VDGifnDSX40HdgT512fuhQ9iGlPls3p133d64Rak,5975
9
- foamlib/_files/_io.py,sha256=alxMyxQh0zb6BZYqomZwOo9dQujMpRuS5yfpIKBEnaM,1976
10
- foamlib/_files/_parsing.py,sha256=blyt1kpYruoW5I6DMDg8jhg6f5oz7gzedmAImuJ5b4k,5966
11
- foamlib/_files/_serialization.py,sha256=H9STIpUxEBRwXh5WnqtjicCkGTpTYSwfWUwbVJf9x9A,2961
12
- foamlib-0.3.2.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
13
- foamlib-0.3.2.dist-info/METADATA,sha256=DBX2JpB09uqjPowv_ShTp7BZ9ryRI_WqVmcpp7T2-DQ,4650
14
- foamlib-0.3.2.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
15
- foamlib-0.3.2.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
16
- foamlib-0.3.2.dist-info/RECORD,,