foamlib 0.4.1__py3-none-any.whl → 0.4.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 +1 -1
- foamlib/_cases/_async.py +71 -64
- foamlib/_cases/_base.py +92 -23
- foamlib/_cases/_sync.py +38 -23
- foamlib/_files/_base.py +1 -0
- foamlib/_files/_files.py +3 -3
- foamlib/_files/_parsing.py +66 -55
- {foamlib-0.4.1.dist-info → foamlib-0.4.4.dist-info}/METADATA +4 -5
- foamlib-0.4.4.dist-info/RECORD +19 -0
- {foamlib-0.4.1.dist-info → foamlib-0.4.4.dist-info}/WHEEL +1 -1
- foamlib-0.4.1.dist-info/RECORD +0 -19
- {foamlib-0.4.1.dist-info → foamlib-0.4.4.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.4.1.dist-info → foamlib-0.4.4.dist-info}/top_level.txt +0 -0
foamlib/__init__.py
CHANGED
foamlib/_cases/_async.py
CHANGED
@@ -4,14 +4,15 @@ import sys
|
|
4
4
|
from contextlib import asynccontextmanager
|
5
5
|
from pathlib import Path
|
6
6
|
from typing import (
|
7
|
+
Callable,
|
7
8
|
Optional,
|
8
9
|
Union,
|
9
10
|
)
|
10
11
|
|
11
12
|
if sys.version_info >= (3, 9):
|
12
|
-
from collections.abc import AsyncGenerator, Sequence
|
13
|
+
from collections.abc import AsyncGenerator, Collection, Sequence
|
13
14
|
else:
|
14
|
-
from typing import AsyncGenerator, Sequence
|
15
|
+
from typing import AsyncGenerator, Collection, Sequence
|
15
16
|
|
16
17
|
import aioshutil
|
17
18
|
|
@@ -61,6 +62,22 @@ class AsyncFoamCase(FoamCaseBase):
|
|
61
62
|
AsyncFoamCase._reserved_cpus -= cpus
|
62
63
|
AsyncFoamCase._cpus_cond.notify(cpus)
|
63
64
|
|
65
|
+
@staticmethod
|
66
|
+
async def _rmtree(path: Path, ignore_errors: bool = False) -> None:
|
67
|
+
await aioshutil.rmtree(path, ignore_errors=ignore_errors) # type: ignore [call-arg]
|
68
|
+
|
69
|
+
@staticmethod
|
70
|
+
async def _copytree(
|
71
|
+
src: Path,
|
72
|
+
dest: Path,
|
73
|
+
*,
|
74
|
+
symlinks: bool = False,
|
75
|
+
ignore: Optional[
|
76
|
+
Callable[[Union[Path, str], Collection[str]], Collection[str]]
|
77
|
+
] = None,
|
78
|
+
) -> None:
|
79
|
+
await aioshutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
|
80
|
+
|
64
81
|
async def clean(
|
65
82
|
self,
|
66
83
|
*,
|
@@ -73,50 +90,55 @@ class AsyncFoamCase(FoamCaseBase):
|
|
73
90
|
:param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
|
74
91
|
:param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
|
75
92
|
"""
|
76
|
-
|
77
|
-
|
78
|
-
if script_path is not None:
|
79
|
-
await self.run([script_path], check=check)
|
80
|
-
else:
|
81
|
-
for p in self._clean_paths():
|
82
|
-
if p.is_dir():
|
83
|
-
await aioshutil.rmtree(p) # type: ignore [call-arg]
|
84
|
-
else:
|
85
|
-
p.unlink()
|
93
|
+
for name, args, kwargs in self._clean_cmds(script=script, check=check):
|
94
|
+
await getattr(self, name)(*args, **kwargs)
|
86
95
|
|
87
96
|
async def _run(
|
88
97
|
self,
|
89
98
|
cmd: Union[Sequence[Union[str, Path]], str, Path],
|
90
99
|
*,
|
100
|
+
parallel: bool = False,
|
101
|
+
cpus: int = 1,
|
91
102
|
check: bool = True,
|
92
103
|
) -> None:
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
104
|
+
async with self._cpus(cpus):
|
105
|
+
if not is_sequence(cmd):
|
106
|
+
if parallel:
|
107
|
+
cmd = f"mpiexec -np {cpus} {cmd} -parallel"
|
108
|
+
|
109
|
+
proc = await asyncio.create_subprocess_shell(
|
110
|
+
str(cmd),
|
111
|
+
cwd=self.path,
|
112
|
+
env=self._env(shell=True),
|
113
|
+
stdout=asyncio.subprocess.DEVNULL,
|
114
|
+
stderr=asyncio.subprocess.PIPE
|
115
|
+
if check
|
116
|
+
else asyncio.subprocess.DEVNULL,
|
117
|
+
)
|
118
|
+
|
119
|
+
else:
|
120
|
+
if parallel:
|
121
|
+
cmd = ["mpiexec", "-np", str(cpus), *cmd, "-parallel"]
|
122
|
+
|
123
|
+
if sys.version_info < (3, 8):
|
124
|
+
cmd = (str(arg) for arg in cmd)
|
125
|
+
proc = await asyncio.create_subprocess_exec(
|
126
|
+
*cmd,
|
127
|
+
cwd=self.path,
|
128
|
+
env=self._env(shell=False),
|
129
|
+
stdout=asyncio.subprocess.DEVNULL,
|
130
|
+
stderr=asyncio.subprocess.PIPE
|
131
|
+
if check
|
132
|
+
else asyncio.subprocess.DEVNULL,
|
133
|
+
)
|
134
|
+
|
135
|
+
stdout, stderr = await proc.communicate()
|
136
|
+
|
137
|
+
assert stdout is None
|
138
|
+
assert proc.returncode is not None
|
139
|
+
|
140
|
+
if check:
|
141
|
+
check_returncode(proc.returncode, cmd, stderr.decode())
|
120
142
|
|
121
143
|
async def run(
|
122
144
|
self,
|
@@ -128,19 +150,9 @@ class AsyncFoamCase(FoamCaseBase):
|
|
128
150
|
check: bool = True,
|
129
151
|
) -> None:
|
130
152
|
for name, args, kwargs in self._run_cmds(
|
131
|
-
cmd=cmd, script=script, parallel=parallel, check=check
|
153
|
+
cmd=cmd, script=script, parallel=parallel, cpus=cpus, check=check
|
132
154
|
):
|
133
|
-
|
134
|
-
if name == "run":
|
135
|
-
if kwargs.get("parallel", False):
|
136
|
-
cpus = max(self._nprocessors, 1)
|
137
|
-
else:
|
138
|
-
cpus = 1
|
139
|
-
else:
|
140
|
-
cpus = 0
|
141
|
-
|
142
|
-
async with self._cpus(cpus):
|
143
|
-
await getattr(self, name)(*args, **kwargs)
|
155
|
+
await getattr(self, name)(*args, **kwargs)
|
144
156
|
|
145
157
|
async def block_mesh(self, *, check: bool = True) -> None:
|
146
158
|
"""Run blockMesh on this case."""
|
@@ -156,8 +168,8 @@ class AsyncFoamCase(FoamCaseBase):
|
|
156
168
|
|
157
169
|
async def restore_0_dir(self) -> None:
|
158
170
|
"""Restore the 0 directory from the 0.orig directory."""
|
159
|
-
|
160
|
-
|
171
|
+
for name, args, kwargs in self._restore_0_dir_cmds():
|
172
|
+
await getattr(self, name)(*args, **kwargs)
|
161
173
|
|
162
174
|
async def copy(self, dest: Union[Path, str]) -> "AsyncFoamCase":
|
163
175
|
"""
|
@@ -165,7 +177,10 @@ class AsyncFoamCase(FoamCaseBase):
|
|
165
177
|
|
166
178
|
:param dest: The destination path.
|
167
179
|
"""
|
168
|
-
|
180
|
+
for name, args, kwargs in self._copy_cmds(dest):
|
181
|
+
await getattr(self, name)(*args, **kwargs)
|
182
|
+
|
183
|
+
return AsyncFoamCase(dest)
|
169
184
|
|
170
185
|
async def clone(self, dest: Union[Path, str]) -> "AsyncFoamCase":
|
171
186
|
"""
|
@@ -173,15 +188,7 @@ class AsyncFoamCase(FoamCaseBase):
|
|
173
188
|
|
174
189
|
:param dest: The destination path.
|
175
190
|
"""
|
176
|
-
|
177
|
-
|
178
|
-
await copy.clean()
|
179
|
-
return copy
|
180
|
-
|
181
|
-
dest = Path(dest)
|
182
|
-
|
183
|
-
await aioshutil.copytree(
|
184
|
-
self.path, dest, symlinks=True, ignore=self._clone_ignore()
|
185
|
-
)
|
191
|
+
for name, args, kwargs in self._clone_cmds(dest):
|
192
|
+
await getattr(self, name)(*args, **kwargs)
|
186
193
|
|
187
194
|
return AsyncFoamCase(dest)
|
foamlib/_cases/_base.py
CHANGED
@@ -33,11 +33,10 @@ else:
|
|
33
33
|
|
34
34
|
|
35
35
|
from .._files import FoamFieldFile, FoamFile
|
36
|
-
from .._util import is_sequence
|
37
36
|
|
38
37
|
|
39
38
|
class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
40
|
-
def __init__(self, path: Union[Path, str] = Path()):
|
39
|
+
def __init__(self, path: Union[Path, str, "FoamCaseBase"] = Path()):
|
41
40
|
self.path = Path(path).absolute()
|
42
41
|
|
43
42
|
class TimeDirectory(Set[FoamFieldFile]):
|
@@ -254,24 +253,102 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
254
253
|
else:
|
255
254
|
return None
|
256
255
|
|
256
|
+
def _copy_cmds(
|
257
|
+
self, dest: Union[Path, str]
|
258
|
+
) -> Generator[Tuple[str, Sequence[Any], Mapping[str, Any]], None, None]:
|
259
|
+
yield (
|
260
|
+
"_copytree",
|
261
|
+
(
|
262
|
+
self.path,
|
263
|
+
dest,
|
264
|
+
),
|
265
|
+
{"symlinks": True},
|
266
|
+
)
|
267
|
+
|
268
|
+
def _clean_cmds(
|
269
|
+
self, *, script: bool = True, check: bool = False
|
270
|
+
) -> Generator[Tuple[str, Sequence[Any], Mapping[str, Any]], None, None]:
|
271
|
+
script_path = self._clean_script() if script else None
|
272
|
+
|
273
|
+
if script_path is not None:
|
274
|
+
yield ("_run", ([script_path],), {"cpus": 0, "check": check})
|
275
|
+
else:
|
276
|
+
for p in self._clean_paths():
|
277
|
+
if p.is_dir():
|
278
|
+
yield ("_rmtree", (p,), {})
|
279
|
+
else:
|
280
|
+
p.unlink()
|
281
|
+
|
282
|
+
def _clone_cmds(
|
283
|
+
self, dest: Union[Path, str]
|
284
|
+
) -> Generator[Tuple[str, Sequence[Any], Mapping[str, Any]], None, None]:
|
285
|
+
if self._clean_script() is not None:
|
286
|
+
yield ("copy", (dest,), {})
|
287
|
+
yield ("clean", (), {})
|
288
|
+
else:
|
289
|
+
yield (
|
290
|
+
"_copytree",
|
291
|
+
(
|
292
|
+
self.path,
|
293
|
+
dest,
|
294
|
+
),
|
295
|
+
{"symlinks": True, "ignore": self._clone_ignore()},
|
296
|
+
)
|
297
|
+
|
298
|
+
def _restore_0_dir_cmds(
|
299
|
+
self,
|
300
|
+
) -> Generator[Tuple[str, Sequence[Any], Mapping[str, Any]], None, None]:
|
301
|
+
yield ("_rmtree", (self.path / "0",), {"ignore_errors": True})
|
302
|
+
yield (
|
303
|
+
"_copytree",
|
304
|
+
(
|
305
|
+
self.path / "0.orig",
|
306
|
+
self.path / "0",
|
307
|
+
),
|
308
|
+
{"symlinks": True},
|
309
|
+
)
|
310
|
+
|
257
311
|
def _run_cmds(
|
258
312
|
self,
|
259
313
|
cmd: Optional[Union[Sequence[Union[str, Path]], str, Path]] = None,
|
260
314
|
*,
|
261
315
|
script: bool = True,
|
262
316
|
parallel: Optional[bool] = None,
|
317
|
+
cpus: Optional[int] = None,
|
263
318
|
check: bool = True,
|
264
319
|
) -> Generator[Tuple[str, Sequence[Any], Mapping[str, Any]], None, None]:
|
265
320
|
if cmd is not None:
|
266
321
|
if parallel:
|
267
|
-
|
322
|
+
if cpus is None:
|
323
|
+
cpus = max(self._nprocessors, 1)
|
324
|
+
else:
|
325
|
+
parallel = False
|
326
|
+
if cpus is None:
|
327
|
+
cpus = 1
|
328
|
+
|
329
|
+
yield ("_run", (cmd,), {"parallel": parallel, "cpus": cpus, "check": check})
|
268
330
|
|
269
|
-
yield ("_run", (cmd,), {"check": check})
|
270
331
|
else:
|
271
332
|
script_path = self._run_script(parallel=parallel) if script else None
|
272
333
|
|
273
334
|
if script_path is not None:
|
274
|
-
|
335
|
+
if parallel or parallel is None:
|
336
|
+
if cpus is None:
|
337
|
+
if self._nprocessors > 0:
|
338
|
+
cpus = self._nprocessors
|
339
|
+
elif (self.path / "system" / "decomposeParDict").is_file():
|
340
|
+
cpus = self._nsubdomains
|
341
|
+
else:
|
342
|
+
cpus = 1
|
343
|
+
else:
|
344
|
+
if cpus is None:
|
345
|
+
cpus = 1
|
346
|
+
|
347
|
+
yield (
|
348
|
+
"_run",
|
349
|
+
([script_path],),
|
350
|
+
{"parallel": False, "cpus": cpus, "check": check},
|
351
|
+
)
|
275
352
|
|
276
353
|
else:
|
277
354
|
if not self and (self.path / "0.orig").is_dir():
|
@@ -282,7 +359,8 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
282
359
|
|
283
360
|
if parallel is None:
|
284
361
|
parallel = (
|
285
|
-
|
362
|
+
(cpus is not None and cpus > 1)
|
363
|
+
or self._nprocessors > 0
|
286
364
|
or (self.path / "system" / "decomposeParDict").is_file()
|
287
365
|
)
|
288
366
|
|
@@ -293,27 +371,18 @@ class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
|
|
293
371
|
):
|
294
372
|
yield ("decompose_par", (), {"check": check})
|
295
373
|
|
374
|
+
if cpus is None:
|
375
|
+
cpus = max(self._nprocessors, 1)
|
376
|
+
else:
|
377
|
+
if cpus is None:
|
378
|
+
cpus = 1
|
379
|
+
|
296
380
|
yield (
|
297
|
-
"
|
381
|
+
"_run",
|
298
382
|
([self.application],),
|
299
|
-
{"parallel": parallel, "check": check},
|
383
|
+
{"parallel": parallel, "cpus": cpus, "check": check},
|
300
384
|
)
|
301
385
|
|
302
|
-
def _parallel_cmd(
|
303
|
-
self, cmd: Union[Sequence[Union[str, Path]], str, Path]
|
304
|
-
) -> Union[Sequence[Union[str, Path]], str]:
|
305
|
-
if not is_sequence(cmd):
|
306
|
-
return f"mpiexec -np {self._nprocessors} {cmd} -parallel"
|
307
|
-
else:
|
308
|
-
return [
|
309
|
-
"mpiexec",
|
310
|
-
"-np",
|
311
|
-
str(self._nprocessors),
|
312
|
-
cmd[0],
|
313
|
-
"-parallel",
|
314
|
-
*cmd[1:],
|
315
|
-
]
|
316
|
-
|
317
386
|
@property
|
318
387
|
def name(self) -> str:
|
319
388
|
"""The name of the case."""
|
foamlib/_cases/_sync.py
CHANGED
@@ -3,14 +3,15 @@ import subprocess
|
|
3
3
|
import sys
|
4
4
|
from pathlib import Path
|
5
5
|
from typing import (
|
6
|
+
Callable,
|
6
7
|
Optional,
|
7
8
|
Union,
|
8
9
|
)
|
9
10
|
|
10
11
|
if sys.version_info >= (3, 9):
|
11
|
-
from collections.abc import Sequence
|
12
|
+
from collections.abc import Collection, Sequence
|
12
13
|
else:
|
13
|
-
from typing import Sequence
|
14
|
+
from typing import Collection, Sequence
|
14
15
|
|
15
16
|
from .._util import is_sequence
|
16
17
|
from ._base import FoamCaseBase
|
@@ -28,6 +29,22 @@ class FoamCase(FoamCaseBase):
|
|
28
29
|
:param path: The path to the case directory.
|
29
30
|
"""
|
30
31
|
|
32
|
+
@staticmethod
|
33
|
+
def _rmtree(path: Path, *, ignore_errors: bool = False) -> None:
|
34
|
+
shutil.rmtree(path, ignore_errors=ignore_errors)
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def _copytree(
|
38
|
+
src: Path,
|
39
|
+
dest: Path,
|
40
|
+
*,
|
41
|
+
symlinks: bool = False,
|
42
|
+
ignore: Optional[
|
43
|
+
Callable[[Union[Path, str], Collection[str]], Collection[str]]
|
44
|
+
] = None,
|
45
|
+
) -> None:
|
46
|
+
shutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
|
47
|
+
|
31
48
|
def clean(
|
32
49
|
self,
|
33
50
|
*,
|
@@ -40,25 +57,26 @@ class FoamCase(FoamCaseBase):
|
|
40
57
|
:param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
|
41
58
|
:param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
|
42
59
|
"""
|
43
|
-
|
44
|
-
|
45
|
-
if script_path is not None:
|
46
|
-
self.run([script_path], check=check)
|
47
|
-
else:
|
48
|
-
for p in self._clean_paths():
|
49
|
-
if p.is_dir():
|
50
|
-
shutil.rmtree(p)
|
51
|
-
else:
|
52
|
-
p.unlink()
|
60
|
+
for name, args, kwargs in self._clean_cmds(script=script, check=check):
|
61
|
+
getattr(self, name)(*args, **kwargs)
|
53
62
|
|
54
63
|
def _run(
|
55
64
|
self,
|
56
65
|
cmd: Union[Sequence[Union[str, Path]], str, Path],
|
57
66
|
*,
|
67
|
+
parallel: bool = False,
|
68
|
+
cpus: int = 1,
|
58
69
|
check: bool = True,
|
59
70
|
) -> None:
|
60
71
|
shell = not is_sequence(cmd)
|
61
72
|
|
73
|
+
if parallel:
|
74
|
+
if shell:
|
75
|
+
cmd = f"mpiexec -np {cpus} {cmd} -parallel"
|
76
|
+
else:
|
77
|
+
assert is_sequence(cmd)
|
78
|
+
cmd = ["mpiexec", "-np", str(cpus), *cmd, "-parallel"]
|
79
|
+
|
62
80
|
if sys.version_info < (3, 8):
|
63
81
|
if shell:
|
64
82
|
cmd = str(cmd)
|
@@ -113,8 +131,8 @@ class FoamCase(FoamCaseBase):
|
|
113
131
|
|
114
132
|
def restore_0_dir(self) -> None:
|
115
133
|
"""Restore the 0 directory from the 0.orig directory."""
|
116
|
-
|
117
|
-
|
134
|
+
for name, args, kwargs in self._restore_0_dir_cmds():
|
135
|
+
getattr(self, name)(*args, **kwargs)
|
118
136
|
|
119
137
|
def copy(self, dest: Union[Path, str]) -> "FoamCase":
|
120
138
|
"""
|
@@ -122,7 +140,10 @@ class FoamCase(FoamCaseBase):
|
|
122
140
|
|
123
141
|
:param dest: The destination path.
|
124
142
|
"""
|
125
|
-
|
143
|
+
for name, args, kwargs in self._copy_cmds(dest):
|
144
|
+
getattr(self, name)(*args, **kwargs)
|
145
|
+
|
146
|
+
return FoamCase(dest)
|
126
147
|
|
127
148
|
def clone(self, dest: Union[Path, str]) -> "FoamCase":
|
128
149
|
"""
|
@@ -130,13 +151,7 @@ class FoamCase(FoamCaseBase):
|
|
130
151
|
|
131
152
|
:param dest: The destination path.
|
132
153
|
"""
|
133
|
-
|
134
|
-
|
135
|
-
copy.clean()
|
136
|
-
return copy
|
137
|
-
|
138
|
-
dest = Path(dest)
|
139
|
-
|
140
|
-
shutil.copytree(self.path, dest, symlinks=True, ignore=self._clone_ignore())
|
154
|
+
for name, args, kwargs in self._clone_cmds(dest):
|
155
|
+
getattr(self, name)(*args, **kwargs)
|
141
156
|
|
142
157
|
return FoamCase(dest)
|
foamlib/_files/_base.py
CHANGED
foamlib/_files/_files.py
CHANGED
@@ -97,9 +97,9 @@ class FoamFile(
|
|
97
97
|
assert isinstance(ret, dict)
|
98
98
|
v = ret[k]
|
99
99
|
assert isinstance(v, dict)
|
100
|
-
ret = v
|
100
|
+
ret = cast(FoamFileBase._File, v)
|
101
101
|
|
102
|
-
return ret
|
102
|
+
return cast(FoamFileBase._Dict, ret)
|
103
103
|
|
104
104
|
def create(self, *, exist_ok: bool = False, parents: bool = False) -> Self:
|
105
105
|
"""
|
@@ -356,7 +356,7 @@ class FoamFile(
|
|
356
356
|
def __fspath__(self) -> str:
|
357
357
|
return str(self.path)
|
358
358
|
|
359
|
-
def as_dict(self) -> FoamFileBase.
|
359
|
+
def as_dict(self) -> FoamFileBase._File:
|
360
360
|
"""Return a nested dict representation of the file."""
|
361
361
|
_, parsed = self._read()
|
362
362
|
d = parsed.as_dict()
|
foamlib/_files/_parsing.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import array
|
2
2
|
import sys
|
3
|
-
from typing import Tuple, Union
|
3
|
+
from typing import Tuple, Union, cast
|
4
4
|
|
5
5
|
if sys.version_info >= (3, 9):
|
6
6
|
from collections.abc import Iterator, Mapping, MutableMapping, Sequence
|
@@ -29,6 +29,7 @@ from pyparsing import (
|
|
29
29
|
Word,
|
30
30
|
c_style_comment,
|
31
31
|
common,
|
32
|
+
counted_array,
|
32
33
|
cpp_style_comment,
|
33
34
|
identchars,
|
34
35
|
printables,
|
@@ -42,12 +43,13 @@ def _list_of(entry: ParserElement) -> ParserElement:
|
|
42
43
|
Literal("List") + Literal("<") + common.identifier + Literal(">")
|
43
44
|
).suppress() + (
|
44
45
|
(
|
45
|
-
|
46
|
-
+ (
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
)
|
46
|
+
counted_array(entry, common.integer + Literal("(").suppress())
|
47
|
+
+ Literal(")").suppress()
|
48
|
+
).set_parse_action(lambda tks: [tks.as_list()])
|
49
|
+
| (
|
50
|
+
Literal("(").suppress()
|
51
|
+
+ Group((entry)[...], aslist=True)
|
52
|
+
+ Literal(")").suppress()
|
51
53
|
)
|
52
54
|
| (
|
53
55
|
common.integer + Literal("{").suppress() + entry + Literal("}").suppress()
|
@@ -76,54 +78,59 @@ def _keyword_entry_of(
|
|
76
78
|
return keyword_entry
|
77
79
|
|
78
80
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
global _binary_contents
|
81
|
+
def _unpack_binary_field(
|
82
|
+
tks: ParseResults,
|
83
|
+
) -> Sequence[Union[Sequence[float], Sequence[Sequence[float]]]]:
|
84
|
+
elsize = len(tks[0]) // 8
|
84
85
|
|
85
|
-
|
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()
|
86
|
+
arr = array.array("d", "".join(tks).encode("latin-1"))
|
106
87
|
|
107
|
-
|
88
|
+
all: Union[Sequence[float], Sequence[Sequence[float]]]
|
108
89
|
|
109
|
-
|
90
|
+
if elsize != 1:
|
91
|
+
all = [arr[i : i + elsize].tolist() for i in range(0, len(arr), elsize)]
|
92
|
+
else:
|
93
|
+
all = arr.tolist()
|
110
94
|
|
111
|
-
|
95
|
+
return [all]
|
112
96
|
|
113
97
|
|
114
98
|
_BINARY_FIELD = (
|
115
|
-
(
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
99
|
+
Keyword("nonuniform").suppress()
|
100
|
+
+ Literal("List").suppress()
|
101
|
+
+ Literal("<").suppress()
|
102
|
+
+ (
|
103
|
+
counted_array(
|
104
|
+
CharsNotIn(exact=8),
|
105
|
+
Literal("scalar").suppress()
|
106
|
+
+ Literal(">").suppress()
|
107
|
+
+ common.integer
|
108
|
+
+ Literal("(").suppress(),
|
109
|
+
)
|
110
|
+
| counted_array(
|
111
|
+
CharsNotIn(exact=8 * 3),
|
112
|
+
Literal("vector").suppress()
|
113
|
+
+ Literal(">").suppress()
|
114
|
+
+ common.integer
|
115
|
+
+ Literal("(").suppress(),
|
116
|
+
)
|
117
|
+
| counted_array(
|
118
|
+
CharsNotIn(exact=8 * 6),
|
119
|
+
Literal("symmTensor").suppress()
|
120
|
+
+ Literal(">").suppress()
|
121
|
+
+ common.integer
|
122
|
+
+ Literal("(").suppress(),
|
123
|
+
)
|
124
|
+
| counted_array(
|
125
|
+
CharsNotIn(exact=8 * 9),
|
126
|
+
Literal("tensor").suppress()
|
127
|
+
+ Literal(">").suppress()
|
128
|
+
+ common.integer
|
129
|
+
+ Literal("(").suppress(),
|
130
|
+
)
|
131
|
+
)
|
125
132
|
+ Literal(")").suppress()
|
126
|
-
)
|
133
|
+
).set_parse_action(_unpack_binary_field)
|
127
134
|
|
128
135
|
|
129
136
|
_SWITCH = (
|
@@ -149,7 +156,8 @@ _FIELD = (
|
|
149
156
|
)
|
150
157
|
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
151
158
|
_DATA = Forward()
|
152
|
-
|
159
|
+
_KEYWORD = Combine(Literal("(") + Word(identchars + " ") + Literal(")")) | _TOKEN
|
160
|
+
_KEYWORD_ENTRY = Dict(Group(_keyword_entry_of(_KEYWORD, _DATA)), asdict=True)
|
153
161
|
_DATA_ENTRY = Forward()
|
154
162
|
_LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
|
155
163
|
_LIST = _list_of(_LIST_ENTRY)
|
@@ -163,7 +171,7 @@ _DATA <<= _DATA_ENTRY[1, ...].set_parse_action(
|
|
163
171
|
|
164
172
|
_FILE = (
|
165
173
|
Dict(
|
166
|
-
Group(_keyword_entry_of(
|
174
|
+
Group(_keyword_entry_of(_KEYWORD, Opt(_DATA, default=""), located=True))[...]
|
167
175
|
+ Opt(
|
168
176
|
Group(
|
169
177
|
Located(
|
@@ -173,7 +181,7 @@ _FILE = (
|
|
173
181
|
)
|
174
182
|
)
|
175
183
|
)
|
176
|
-
+ Group(_keyword_entry_of(
|
184
|
+
+ Group(_keyword_entry_of(_KEYWORD, Opt(_DATA, default=""), located=True))[...]
|
177
185
|
)
|
178
186
|
.ignore(c_style_comment)
|
179
187
|
.ignore(cpp_style_comment)
|
@@ -266,17 +274,20 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamFileBase.Data, EllipsisType]]):
|
|
266
274
|
|
267
275
|
return start, end
|
268
276
|
|
269
|
-
def as_dict(self) -> FoamFileBase.
|
270
|
-
ret: FoamFileBase.
|
277
|
+
def as_dict(self) -> FoamFileBase._File:
|
278
|
+
ret: FoamFileBase._File = {}
|
271
279
|
for keywords, (_, data, _) in self._parsed.items():
|
272
280
|
r = ret
|
273
281
|
for k in keywords[:-1]:
|
274
|
-
assert isinstance(r, dict)
|
275
282
|
v = r[k]
|
276
283
|
assert isinstance(v, dict)
|
277
|
-
r = v
|
284
|
+
r = cast(FoamFileBase._File, v)
|
278
285
|
|
279
286
|
assert isinstance(r, dict)
|
280
|
-
|
287
|
+
if keywords:
|
288
|
+
r[keywords[-1]] = {} if data is ... else data
|
289
|
+
else:
|
290
|
+
assert data is not ...
|
291
|
+
r[None] = data
|
281
292
|
|
282
293
|
return ret
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: foamlib
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.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
|
@@ -67,9 +67,9 @@ Requires-Dist: mypy <2,>=1 ; extra == 'typing'
|
|
67
67
|
|
68
68
|
It offers the following classes:
|
69
69
|
|
70
|
-
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable
|
71
|
-
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable
|
72
|
-
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable
|
70
|
+
* [`FoamFile`](https://foamlib.readthedocs.io/en/stable/files.html#foamlib.FoamFile) (and [`FoamFieldFile`](https://foamlib.readthedocs.io/en/stable/files.html#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.
|
71
|
+
* [`FoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.FoamCase): a class for manipulating, executing and accessing the results of OpenFOAM cases.
|
72
|
+
* [`AsyncFoamCase`](https://foamlib.readthedocs.io/en/stable/cases.html#foamlib.AsyncFoamCase): variant of `FoamCase` with asynchronous methods for running multiple cases at once.
|
73
73
|
|
74
74
|
## Get started
|
75
75
|
|
@@ -131,7 +131,6 @@ from foamlib import AsyncFoamCase
|
|
131
131
|
|
132
132
|
async def run_case():
|
133
133
|
my_pitz_async = AsyncFoamCase(my_pitz)
|
134
|
-
|
135
134
|
await my_pitz_async.run()
|
136
135
|
|
137
136
|
asyncio.run(run_case())
|
@@ -0,0 +1,19 @@
|
|
1
|
+
foamlib/__init__.py,sha256=r8VZ1cNvef7NfWWxf7GzPuveJO3LrXVwrF5pwgok-eg,446
|
2
|
+
foamlib/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
|
3
|
+
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
+
foamlib/_cases/__init__.py,sha256=xnQpR64EvFtCh07qnSz7kjQ1IhJXjRDwaZWwQGZhGv4,280
|
5
|
+
foamlib/_cases/_async.py,sha256=WgEunau8GvkI-k4amS98Uxz8O0fZBxLWqzUY3uiAmQQ,6345
|
6
|
+
foamlib/_cases/_base.py,sha256=4sndDYfXPWhkSel41e8d00xX3PX22wMrYniA_zb0luo,14958
|
7
|
+
foamlib/_cases/_sync.py,sha256=S-9-TOK7PTC3NMKgIdvKWTn4fS34FPccedvm-lpjpH8,5037
|
8
|
+
foamlib/_cases/_util.py,sha256=v6sHxHCEgagsVuup0S1xJW-x9py5xj3bUye8PiFfb3o,925
|
9
|
+
foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
|
10
|
+
foamlib/_files/_base.py,sha256=zaFDjLE6jB7WtGWk8hfKusjLtlGu6CZV16AHJpRUibs,1929
|
11
|
+
foamlib/_files/_files.py,sha256=-3mDRIsaQaxHF74q0Zfzlrhfieb7w_EPOFScSx8wRPE,16245
|
12
|
+
foamlib/_files/_io.py,sha256=f_tYI7AqaFsQ8mtK__fEoIUqpYb3YmrI8X5D8updmNM,2084
|
13
|
+
foamlib/_files/_parsing.py,sha256=8V2CKZ45mKE3f9fP8lAfexIdhPGrq7elIZkpBkkGB6Q,8773
|
14
|
+
foamlib/_files/_serialization.py,sha256=3yb9fgjCpDoRfZoLsbZaIFrkZ3vGBzleFRw6IbaZuuY,3408
|
15
|
+
foamlib-0.4.4.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
16
|
+
foamlib-0.4.4.dist-info/METADATA,sha256=i9qq7ZU7oFhmn1BO0rz-53wueFfOvEziP4am3x3qBA4,5496
|
17
|
+
foamlib-0.4.4.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
18
|
+
foamlib-0.4.4.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
19
|
+
foamlib-0.4.4.dist-info/RECORD,,
|
foamlib-0.4.1.dist-info/RECORD
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=Lm0oAsj1KsJFBiIAaNSSE231t6g92HybfoXNwBoFUBQ,446
|
2
|
-
foamlib/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
|
3
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
|
-
foamlib/_cases/__init__.py,sha256=xnQpR64EvFtCh07qnSz7kjQ1IhJXjRDwaZWwQGZhGv4,280
|
5
|
-
foamlib/_cases/_async.py,sha256=ehmAedDepDXaRdf8f0IznxDMYAQxoUWSIxbhxG_17Rw,6035
|
6
|
-
foamlib/_cases/_base.py,sha256=JrYJplmXV84qwgerdVSCHz2rCI-KWB4IvTBPMkYpecw,12657
|
7
|
-
foamlib/_cases/_sync.py,sha256=CloQgd-93jxfSXIt7J5NxcAu3N_iF3eXMKO-NfNOgi4,4522
|
8
|
-
foamlib/_cases/_util.py,sha256=v6sHxHCEgagsVuup0S1xJW-x9py5xj3bUye8PiFfb3o,925
|
9
|
-
foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
|
10
|
-
foamlib/_files/_base.py,sha256=sy1RP08pdv079KD_UBy7xUYEnXyNK35AiKVcCalYmPU,1873
|
11
|
-
foamlib/_files/_files.py,sha256=2bvl2YZ0bqHpbQV35vzYO7wWNOo_2WDF9LbyLagIvBE,16193
|
12
|
-
foamlib/_files/_io.py,sha256=f_tYI7AqaFsQ8mtK__fEoIUqpYb3YmrI8X5D8updmNM,2084
|
13
|
-
foamlib/_files/_parsing.py,sha256=eqY3grOnBdeXQLmM418VFKtCrpSd7FnLXuQ8h4mzD2M,8189
|
14
|
-
foamlib/_files/_serialization.py,sha256=3yb9fgjCpDoRfZoLsbZaIFrkZ3vGBzleFRw6IbaZuuY,3408
|
15
|
-
foamlib-0.4.1.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
16
|
-
foamlib-0.4.1.dist-info/METADATA,sha256=u17GpI0WPbXEBVwUo0lJI6J8sApdaTYzwX_DGbPr0rw,5457
|
17
|
-
foamlib-0.4.1.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
|
18
|
-
foamlib-0.4.1.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
19
|
-
foamlib-0.4.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|