foamlib 0.6.13__tar.gz → 0.6.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {foamlib-0.6.13 → foamlib-0.6.15}/PKG-INFO +5 -6
  2. {foamlib-0.6.13 → foamlib-0.6.15}/README.md +4 -5
  3. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/__init__.py +1 -1
  4. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/_files.py +6 -2
  5. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/_parsing.py +101 -71
  6. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/_serialization.py +32 -12
  7. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib.egg-info/PKG-INFO +5 -6
  8. {foamlib-0.6.13 → foamlib-0.6.15}/LICENSE.txt +0 -0
  9. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/__init__.py +0 -0
  10. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_async.py +0 -0
  11. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_base.py +0 -0
  12. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_run.py +0 -0
  13. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_slurm.py +0 -0
  14. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_subprocess.py +0 -0
  15. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_sync.py +0 -0
  16. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_cases/_util.py +0 -0
  17. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/__init__.py +0 -0
  18. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/_base.py +0 -0
  19. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/_io.py +0 -0
  20. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/_files/_util.py +0 -0
  21. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib/py.typed +0 -0
  22. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib.egg-info/SOURCES.txt +0 -0
  23. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib.egg-info/dependency_links.txt +0 -0
  24. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib.egg-info/requires.txt +0 -0
  25. {foamlib-0.6.13 → foamlib-0.6.15}/foamlib.egg-info/top_level.txt +0 -0
  26. {foamlib-0.6.13 → foamlib-0.6.15}/pyproject.toml +0 -0
  27. {foamlib-0.6.13 → foamlib-0.6.15}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.13
3
+ Version: 0.6.15
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
@@ -169,21 +169,20 @@ U = FoamFieldFile(Path(my_pitz) / "0/U")
169
169
  print(U.internal_field)
170
170
  ```
171
171
 
172
- ### 🔁 Run an optimization loop in parallel
172
+ ### 🔁 Run an optimization loop on a Slurm-based cluster
173
173
 
174
174
  ```python
175
175
  import os
176
176
  from pathlib import Path
177
- from foamlib import AsyncFoamCase
177
+ from foamlib import AsyncSlurmFoamCase
178
178
  from scipy.optimize import differential_evolution
179
179
 
180
- base = AsyncFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
181
- # Replace with `AsyncSlurmFoamCase` if on a cluster and you want cases to be run as Slurm jobs
180
+ base = AsyncSlurmFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
182
181
 
183
182
  async def cost(x):
184
183
  async with base.clone() as clone:
185
184
  clone[0]["U"].boundary_field["inlet"].value = [x[0], 0, 0]
186
- await clone.run()
185
+ await clone.run(fallback=True) # Run locally if Slurm is not available
187
186
  return abs(clone[-1]["U"].internal_field[0][0])
188
187
 
189
188
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
@@ -114,21 +114,20 @@ U = FoamFieldFile(Path(my_pitz) / "0/U")
114
114
  print(U.internal_field)
115
115
  ```
116
116
 
117
- ### 🔁 Run an optimization loop in parallel
117
+ ### 🔁 Run an optimization loop on a Slurm-based cluster
118
118
 
119
119
  ```python
120
120
  import os
121
121
  from pathlib import Path
122
- from foamlib import AsyncFoamCase
122
+ from foamlib import AsyncSlurmFoamCase
123
123
  from scipy.optimize import differential_evolution
124
124
 
125
- base = AsyncFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
126
- # Replace with `AsyncSlurmFoamCase` if on a cluster and you want cases to be run as Slurm jobs
125
+ base = AsyncSlurmFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
127
126
 
128
127
  async def cost(x):
129
128
  async with base.clone() as clone:
130
129
  clone[0]["U"].boundary_field["inlet"].value = [x[0], 0, 0]
131
- await clone.run()
130
+ await clone.run(fallback=True) # Run locally if Slurm is not available
132
131
  return abs(clone[-1]["U"].internal_field[0][0])
133
132
 
134
133
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
@@ -1,6 +1,6 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.6.13"
3
+ __version__ = "0.6.15"
4
4
 
5
5
  from ._cases import (
6
6
  AsyncFoamCase,
@@ -189,6 +189,10 @@ class FoamFile(
189
189
  elif not isinstance(keywords, tuple):
190
190
  keywords = (keywords,)
191
191
 
192
+ if keywords and not isinstance(normalize(keywords[-1], kind=Kind.KEYWORD), str):
193
+ msg = f"Invalid keyword: {keywords[-1]}"
194
+ raise ValueError(msg)
195
+
192
196
  with self:
193
197
  try:
194
198
  write_header = (
@@ -293,7 +297,7 @@ class FoamFile(
293
297
  ...,
294
298
  before
295
299
  + indentation
296
- + dumps(keywords[-1])
300
+ + dumps(keywords[-1], kind=Kind.KEYWORD)
297
301
  + b"\n"
298
302
  + indentation
299
303
  + b"{\n"
@@ -311,7 +315,7 @@ class FoamFile(
311
315
  normalize(data, kind=kind),
312
316
  before
313
317
  + indentation
314
- + dumps(keywords[-1])
318
+ + dumps(keywords[-1], kind=Kind.KEYWORD)
315
319
  + b" "
316
320
  + dumps(data, kind=kind)
317
321
  + b";"
@@ -29,7 +29,6 @@ from pyparsing import (
29
29
  ParseResults,
30
30
  QuotedString,
31
31
  Word,
32
- c_style_comment,
33
32
  common,
34
33
  counted_array,
35
34
  cpp_style_comment,
@@ -41,9 +40,7 @@ from ._base import FoamFileBase
41
40
 
42
41
 
43
42
  def _list_of(entry: ParserElement) -> ParserElement:
44
- return Opt(
45
- Literal("List") + Literal("<") + common.identifier + Literal(">")
46
- ).suppress() + (
43
+ return (
47
44
  (
48
45
  counted_array(entry, common.integer + Literal("(").suppress())
49
46
  + Literal(")").suppress()
@@ -124,7 +121,13 @@ _SWITCH = (
124
121
  _DIMENSIONS = (
125
122
  Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
126
123
  ).set_parse_action(lambda tks: FoamFileBase.DimensionSet(*tks))
127
- _TENSOR = _list_of(common.number) | common.number
124
+ _TENSOR = common.ieee_float | (
125
+ Literal("(").suppress()
126
+ + Group(
127
+ common.ieee_float[9] | common.ieee_float[6] | common.ieee_float[3], aslist=True
128
+ )
129
+ + Literal(")").suppress()
130
+ )
128
131
  _IDENTIFIER = Combine(
129
132
  Word(_IDENTCHARS, _IDENTBODYCHARS, exclude_chars="()")
130
133
  + Opt(Literal("(") + Word(_IDENTBODYCHARS, exclude_chars="()") + Literal(")"))
@@ -135,87 +138,116 @@ _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
135
138
  _FIELD = (Keyword("uniform", _IDENTBODYCHARS).suppress() + _TENSOR) | (
136
139
  Keyword("nonuniform", _IDENTBODYCHARS).suppress()
137
140
  + (
138
- _list_of(_TENSOR)
139
- | (
140
- Literal("List").suppress()
141
- + Literal("<").suppress()
142
- + (
143
- (
144
- Literal("scalar").suppress()
145
- + Literal(">").suppress()
146
- + (
141
+ Literal("List").suppress()
142
+ + Literal("<").suppress()
143
+ + (
144
+ (
145
+ Literal("scalar").suppress()
146
+ + Literal(">").suppress()
147
+ + (
148
+ _list_of(common.ieee_float)
149
+ | (
147
150
  (
148
- counted_array(
149
- CharsNotIn(exact=8),
150
- common.integer + Literal("(").suppress(),
151
+ (
152
+ counted_array(
153
+ CharsNotIn(exact=8),
154
+ common.integer + Literal("(").suppress(),
155
+ )
151
156
  )
152
- )
153
- | (
154
- counted_array(
155
- CharsNotIn(exact=4),
156
- common.integer + Literal("(").suppress(),
157
+ | (
158
+ counted_array(
159
+ CharsNotIn(exact=4),
160
+ common.integer + Literal("(").suppress(),
161
+ )
157
162
  )
158
163
  )
164
+ + Literal(")").suppress()
165
+ ).set_parse_action(_unpack_binary_field)
166
+ )
167
+ )
168
+ | (
169
+ Literal("vector").suppress()
170
+ + Literal(">").suppress()
171
+ + (
172
+ _list_of(
173
+ Literal("(").suppress()
174
+ + Group(common.ieee_float[3], aslist=True)
175
+ + Literal(")").suppress()
159
176
  )
160
- + Literal(")").suppress()
161
- ).set_parse_action(_unpack_binary_field)
162
- | (
163
- Literal("vector").suppress()
164
- + Literal(">").suppress()
165
- + (
177
+ | (
166
178
  (
167
- counted_array(
168
- CharsNotIn(exact=8 * 3),
169
- common.integer + Literal("(").suppress(),
179
+ (
180
+ counted_array(
181
+ CharsNotIn(exact=8 * 3),
182
+ common.integer + Literal("(").suppress(),
183
+ )
170
184
  )
171
- )
172
- | (
173
- counted_array(
174
- CharsNotIn(exact=4 * 3),
175
- common.integer + Literal("(").suppress(),
185
+ | (
186
+ counted_array(
187
+ CharsNotIn(exact=4 * 3),
188
+ common.integer + Literal("(").suppress(),
189
+ )
176
190
  )
177
191
  )
192
+ + Literal(")").suppress()
193
+ ).set_parse_action(lambda tks: _unpack_binary_field(tks, elsize=3))
194
+ )
195
+ )
196
+ | (
197
+ Literal("vector").suppress()
198
+ + Literal(">").suppress()
199
+ + (
200
+ _list_of(
201
+ Literal("(").suppress()
202
+ + Group(common.ieee_float[6], aslist=True)
203
+ + Literal(")").suppress()
178
204
  )
179
- + Literal(")").suppress()
180
- ).set_parse_action(lambda tks: _unpack_binary_field(tks, elsize=3))
181
- | (
182
- Literal("symmTensor").suppress()
183
- + Literal(">").suppress()
184
- + (
205
+ | (
185
206
  (
186
- counted_array(
187
- CharsNotIn(exact=8 * 6),
188
- common.integer + Literal("(").suppress(),
207
+ (
208
+ counted_array(
209
+ CharsNotIn(exact=8 * 6),
210
+ common.integer + Literal("(").suppress(),
211
+ )
189
212
  )
190
- )
191
- | (
192
- counted_array(
193
- CharsNotIn(exact=4 * 6),
194
- common.integer + Literal("(").suppress(),
213
+ | (
214
+ counted_array(
215
+ CharsNotIn(exact=4 * 6),
216
+ common.integer + Literal("(").suppress(),
217
+ )
195
218
  )
196
219
  )
220
+ + Literal(")").suppress()
221
+ ).set_parse_action(lambda tks: _unpack_binary_field(tks, elsize=6))
222
+ )
223
+ )
224
+ | (
225
+ Literal("tensor").suppress()
226
+ + Literal(">").suppress()
227
+ + (
228
+ _list_of(
229
+ Literal("(").suppress()
230
+ + Group(common.ieee_float[9], aslist=True)
231
+ + Literal(")").suppress()
197
232
  )
198
- + Literal(")").suppress()
199
- ).set_parse_action(lambda tks: _unpack_binary_field(tks, elsize=6))
200
- | (
201
- Literal("tensor").suppress()
202
- + Literal(">").suppress()
203
- + (
233
+ | (
204
234
  (
205
- counted_array(
206
- CharsNotIn(exact=8 * 9),
207
- common.integer + Literal("(").suppress(),
235
+ (
236
+ counted_array(
237
+ CharsNotIn(exact=8 * 9),
238
+ common.integer + Literal("(").suppress(),
239
+ )
208
240
  )
209
- )
210
- | (
211
- counted_array(
212
- CharsNotIn(exact=4 * 9),
213
- common.integer + Literal("(").suppress(),
241
+ | (
242
+ counted_array(
243
+ CharsNotIn(exact=4 * 9),
244
+ common.integer + Literal("(").suppress(),
245
+ )
214
246
  )
215
247
  )
216
- )
217
- + Literal(")").suppress()
218
- ).set_parse_action(lambda tks: _unpack_binary_field(tks, elsize=9))
248
+ + Literal(")").suppress()
249
+ ).set_parse_action(lambda tks: _unpack_binary_field(tks, elsize=9))
250
+ )
219
251
  )
220
252
  )
221
253
  )
@@ -229,9 +261,8 @@ _KEYWORD_ENTRY = Dict(Group(_keyword_entry_of(_KEYWORD, _DATA)), asdict=True)
229
261
  _DATA_ENTRY = Forward()
230
262
  _LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
231
263
  _LIST = _list_of(_LIST_ENTRY)
232
- _DATA_ENTRY <<= (
233
- _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
234
- )
264
+ _NUMBER = common.signed_integer ^ common.ieee_float
265
+ _DATA_ENTRY <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | _NUMBER | _SWITCH | _TOKEN
235
266
 
236
267
  _DATA <<= _DATA_ENTRY[1, ...].set_parse_action(
237
268
  lambda tks: tuple(tks) if len(tks) > 1 else [tks[0]]
@@ -251,7 +282,6 @@ _FILE = (
251
282
  )
252
283
  + Group(_keyword_entry_of(_KEYWORD, Opt(_DATA, default=""), located=True))[...]
253
284
  )
254
- .ignore(c_style_comment)
255
285
  .ignore(cpp_style_comment)
256
286
  .ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
257
287
  .parse_with_tabs()
@@ -26,6 +26,7 @@ except ModuleNotFoundError:
26
26
 
27
27
  class Kind(Enum):
28
28
  DEFAULT = auto()
29
+ KEYWORD = auto()
29
30
  SINGLE_ENTRY = auto()
30
31
  ASCII_FIELD = auto()
31
32
  DOUBLE_PRECISION_BINARY_FIELD = auto()
@@ -56,11 +57,6 @@ def normalize(
56
57
  assert isinstance(ret, list)
57
58
  return ret
58
59
 
59
- if kind == Kind.SINGLE_ENTRY and isinstance(data, tuple):
60
- ret = normalize(list(data))
61
- assert isinstance(ret, list)
62
- return ret
63
-
64
60
  if isinstance(data, Mapping):
65
61
  return {k: normalize(v, kind=kind) for k, v in data.items()}
66
62
 
@@ -73,22 +69,43 @@ def normalize(
73
69
  data = cast(Sequence[float], data)
74
70
  return FoamFileBase.DimensionSet(*data)
75
71
 
76
- if is_sequence(data) and not isinstance(data, tuple):
72
+ if is_sequence(data) and (kind == Kind.SINGLE_ENTRY or not isinstance(data, tuple)):
77
73
  return [normalize(d, kind=Kind.SINGLE_ENTRY) for d in data]
78
74
 
79
75
  if isinstance(data, str):
76
+ data = data.strip()
77
+
78
+ if data.startswith("(") and data.endswith(")"):
79
+ data = data[1:-1].split()
80
+ if kind == Kind.KEYWORD:
81
+ return "(" + " ".join(data) + ")"
82
+ return [normalize(d, kind=Kind.SINGLE_ENTRY) for d in data]
83
+
84
+ if data.startswith("[") and data.endswith("]"):
85
+ data = data[1:-1].split()
86
+ return normalize(data, kind=Kind.DIMENSIONS)
87
+
80
88
  with contextlib.suppress(ValueError):
81
89
  return int(data)
82
90
 
83
91
  with contextlib.suppress(ValueError):
84
92
  return float(data)
85
93
 
94
+ if kind != Kind.KEYWORD:
95
+ if data in ("yes", "true", "on", "y", "t"):
96
+ return True
97
+ if data in ("no", "false", "off", "n", "f"):
98
+ return False
99
+
86
100
  tokens: list[str] = re.findall(_TOKENS, data)
87
101
 
88
102
  if len(tokens) == 1:
89
103
  return tokens[0]
90
104
 
91
- return tuple(tokens) if kind != Kind.SINGLE_ENTRY else " ".join(tokens)
105
+ if kind == Kind.KEYWORD:
106
+ return " ".join(tokens)
107
+
108
+ return tuple(tokens)
92
109
 
93
110
  if isinstance(data, FoamFileBase.Dimensioned):
94
111
  value = normalize(data.value, kind=Kind.SINGLE_ENTRY)
@@ -115,12 +132,15 @@ def dumps(
115
132
  if isinstance(data, Mapping):
116
133
  entries = []
117
134
  for k, v in data.items():
118
- if isinstance(v, Mapping):
119
- entries.append(dumps(k) + b" {" + dumps(v) + b"}")
120
- elif not v:
121
- entries.append(dumps(k) + b";")
135
+ value = normalize(v)
136
+ if isinstance(value, Mapping):
137
+ entries.append(
138
+ dumps(k, kind=Kind.KEYWORD) + b" {" + dumps(value) + b"}"
139
+ )
140
+ elif not value:
141
+ entries.append(dumps(k, kind=Kind.KEYWORD) + b";")
122
142
  else:
123
- entries.append(dumps(k) + b" " + dumps(v) + b";")
143
+ entries.append(dumps(k, kind=Kind.KEYWORD) + b" " + dumps(value) + b";")
124
144
 
125
145
  return b" ".join(entries)
126
146
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.6.13
3
+ Version: 0.6.15
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
@@ -169,21 +169,20 @@ U = FoamFieldFile(Path(my_pitz) / "0/U")
169
169
  print(U.internal_field)
170
170
  ```
171
171
 
172
- ### 🔁 Run an optimization loop in parallel
172
+ ### 🔁 Run an optimization loop on a Slurm-based cluster
173
173
 
174
174
  ```python
175
175
  import os
176
176
  from pathlib import Path
177
- from foamlib import AsyncFoamCase
177
+ from foamlib import AsyncSlurmFoamCase
178
178
  from scipy.optimize import differential_evolution
179
179
 
180
- base = AsyncFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
181
- # Replace with `AsyncSlurmFoamCase` if on a cluster and you want cases to be run as Slurm jobs
180
+ base = AsyncSlurmFoamCase(Path(os.environ["FOAM_TUTORIALS"]) / "incompressible/simpleFoam/pitzDaily")
182
181
 
183
182
  async def cost(x):
184
183
  async with base.clone() as clone:
185
184
  clone[0]["U"].boundary_field["inlet"].value = [x[0], 0, 0]
186
- await clone.run()
185
+ await clone.run(fallback=True) # Run locally if Slurm is not available
187
186
  return abs(clone[-1]["U"].internal_field[0][0])
188
187
 
189
188
  result = differential_evolution(cost, bounds=[(-1, 1)], workers=AsyncFoamCase.map, polish=False)
File without changes
File without changes
File without changes
File without changes
File without changes