foamlib 0.5.1__py3-none-any.whl → 0.6.0__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 +41 -90
- foamlib/_cases/{_recipes.py → _run.py} +164 -76
- foamlib/_cases/_sync.py +38 -93
- foamlib/_cases/_util.py +11 -0
- foamlib/_files/_files.py +21 -43
- foamlib/_files/_io.py +16 -8
- foamlib/_files/_parsing.py +46 -46
- {foamlib-0.5.1.dist-info → foamlib-0.6.0.dist-info}/METADATA +1 -1
- foamlib-0.6.0.dist-info/RECORD +21 -0
- foamlib-0.5.1.dist-info/RECORD +0 -21
- {foamlib-0.5.1.dist-info → foamlib-0.6.0.dist-info}/LICENSE.txt +0 -0
- {foamlib-0.5.1.dist-info → foamlib-0.6.0.dist-info}/WHEEL +0 -0
- {foamlib-0.5.1.dist-info → foamlib-0.6.0.dist-info}/top_level.txt +0 -0
foamlib/__init__.py
CHANGED
foamlib/_cases/_async.py
CHANGED
@@ -2,10 +2,8 @@ import asyncio
|
|
2
2
|
import multiprocessing
|
3
3
|
import os
|
4
4
|
import sys
|
5
|
-
import tempfile
|
6
5
|
from contextlib import asynccontextmanager
|
7
|
-
from
|
8
|
-
from typing import Callable, Optional, TypeVar, Union
|
6
|
+
from typing import Any, Callable, Optional, TypeVar, Union
|
9
7
|
|
10
8
|
if sys.version_info >= (3, 9):
|
11
9
|
from collections.abc import (
|
@@ -25,15 +23,15 @@ else:
|
|
25
23
|
|
26
24
|
import aioshutil
|
27
25
|
|
28
|
-
from .
|
26
|
+
from ._run import FoamCaseRunBase
|
29
27
|
from ._subprocess import run_async
|
30
|
-
from ._util import awaitableasynccontextmanager
|
28
|
+
from ._util import ValuedGenerator, awaitableasynccontextmanager
|
31
29
|
|
32
30
|
X = TypeVar("X")
|
33
31
|
Y = TypeVar("Y")
|
34
32
|
|
35
33
|
|
36
|
-
class AsyncFoamCase(
|
34
|
+
class AsyncFoamCase(FoamCaseRunBase):
|
37
35
|
"""
|
38
36
|
An OpenFOAM case with asynchronous support.
|
39
37
|
|
@@ -50,14 +48,11 @@ class AsyncFoamCase(_FoamCaseRecipes):
|
|
50
48
|
"""
|
51
49
|
|
52
50
|
_reserved_cpus = 0
|
53
|
-
_cpus_cond =
|
51
|
+
_cpus_cond = asyncio.Condition()
|
54
52
|
|
55
53
|
@staticmethod
|
56
54
|
@asynccontextmanager
|
57
55
|
async def _cpus(cpus: int) -> AsyncGenerator[None, None]:
|
58
|
-
if AsyncFoamCase._cpus_cond is None:
|
59
|
-
AsyncFoamCase._cpus_cond = asyncio.Condition()
|
60
|
-
|
61
56
|
cpus = min(cpus, AsyncFoamCase.max_cpus)
|
62
57
|
if cpus > 0:
|
63
58
|
async with AsyncFoamCase._cpus_cond:
|
@@ -74,6 +69,16 @@ class AsyncFoamCase(_FoamCaseRecipes):
|
|
74
69
|
AsyncFoamCase._reserved_cpus -= cpus
|
75
70
|
AsyncFoamCase._cpus_cond.notify(cpus)
|
76
71
|
|
72
|
+
@staticmethod
|
73
|
+
async def _run(
|
74
|
+
cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
|
75
|
+
*,
|
76
|
+
cpus: int,
|
77
|
+
**kwargs: Any,
|
78
|
+
) -> None:
|
79
|
+
async with AsyncFoamCase._cpus(cpus):
|
80
|
+
await run_async(cmd, **kwargs)
|
81
|
+
|
77
82
|
@staticmethod
|
78
83
|
async def _rmtree(
|
79
84
|
path: Union["os.PathLike[str]", str], ignore_errors: bool = False
|
@@ -92,75 +97,37 @@ class AsyncFoamCase(_FoamCaseRecipes):
|
|
92
97
|
) -> None:
|
93
98
|
await aioshutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
|
94
99
|
|
95
|
-
async def clean(
|
96
|
-
self,
|
97
|
-
*,
|
98
|
-
script: bool = True,
|
99
|
-
check: bool = False,
|
100
|
-
) -> None:
|
100
|
+
async def clean(self, *, check: bool = False) -> None:
|
101
101
|
"""
|
102
102
|
Clean this case.
|
103
103
|
|
104
|
-
:param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
|
105
104
|
:param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
|
106
105
|
"""
|
107
|
-
for
|
108
|
-
await
|
109
|
-
|
110
|
-
async def _run(
|
111
|
-
self,
|
112
|
-
cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
|
113
|
-
*,
|
114
|
-
parallel: bool = False,
|
115
|
-
cpus: int = 1,
|
116
|
-
check: bool = True,
|
117
|
-
log: bool = True,
|
118
|
-
) -> None:
|
119
|
-
with self._output(cmd, log=log) as (stdout, stderr):
|
120
|
-
if parallel:
|
121
|
-
if isinstance(cmd, str):
|
122
|
-
cmd = [
|
123
|
-
"mpiexec",
|
124
|
-
"-n",
|
125
|
-
str(cpus),
|
126
|
-
"/bin/sh",
|
127
|
-
"-c",
|
128
|
-
f"{cmd} -parallel",
|
129
|
-
]
|
130
|
-
else:
|
131
|
-
cmd = ["mpiexec", "-n", str(cpus), *cmd, "-parallel"]
|
132
|
-
|
133
|
-
async with self._cpus(cpus):
|
134
|
-
await run_async(
|
135
|
-
cmd,
|
136
|
-
check=check,
|
137
|
-
cwd=self.path,
|
138
|
-
env=self._env(shell=isinstance(cmd, str)),
|
139
|
-
stdout=stdout,
|
140
|
-
stderr=stderr,
|
141
|
-
)
|
106
|
+
for coro in self._clean_calls(check=check):
|
107
|
+
await coro
|
142
108
|
|
143
109
|
async def run(
|
144
110
|
self,
|
145
111
|
cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
|
146
112
|
*,
|
147
|
-
script: bool = True,
|
148
113
|
parallel: Optional[bool] = None,
|
149
114
|
cpus: Optional[int] = None,
|
150
115
|
check: bool = True,
|
116
|
+
log: bool = True,
|
151
117
|
) -> None:
|
152
118
|
"""
|
153
119
|
Run this case, or a specified command in the context of this case.
|
154
120
|
|
155
121
|
:param cmd: The command to run. If None, run the case. If a sequence, the first element is the command and the rest are arguments. If a string, `cmd` is executed in a shell.
|
156
|
-
:param script: If True and `cmd` is None, use an (All)run(-parallel) script if it exists for running the case. If False or no run script is found, autodetermine the command(s) needed to run the case.
|
157
122
|
:param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
|
123
|
+
:param cpus: The number of CPUs to use. If None, autodetect according to the case.
|
158
124
|
:param check: If True, raise a CalledProcessError if any command returns a non-zero exit code.
|
125
|
+
:param log: If True, log the command output to a file.
|
159
126
|
"""
|
160
|
-
for
|
161
|
-
cmd=cmd,
|
127
|
+
for coro in self._run_calls(
|
128
|
+
cmd=cmd, parallel=parallel, cpus=cpus, check=check, log=log
|
162
129
|
):
|
163
|
-
await
|
130
|
+
await coro
|
164
131
|
|
165
132
|
async def block_mesh(self, *, check: bool = True) -> None:
|
166
133
|
"""Run blockMesh on this case."""
|
@@ -176,8 +143,8 @@ class AsyncFoamCase(_FoamCaseRecipes):
|
|
176
143
|
|
177
144
|
async def restore_0_dir(self) -> None:
|
178
145
|
"""Restore the 0 directory from the 0.orig directory."""
|
179
|
-
for
|
180
|
-
await
|
146
|
+
for coro in self._restore_0_dir_calls():
|
147
|
+
await coro
|
181
148
|
|
182
149
|
@awaitableasynccontextmanager
|
183
150
|
@asynccontextmanager
|
@@ -189,24 +156,16 @@ class AsyncFoamCase(_FoamCaseRecipes):
|
|
189
156
|
|
190
157
|
Use as an async context manager to automatically delete the copy when done.
|
191
158
|
|
192
|
-
:param dst: The destination path. If None,
|
159
|
+
:param dst: The destination path. If None, clone to `$FOAM_RUN/foamlib`.
|
193
160
|
"""
|
194
|
-
|
195
|
-
dst = Path(tempfile.mkdtemp(), self.name)
|
196
|
-
tmp = True
|
197
|
-
else:
|
198
|
-
tmp = False
|
161
|
+
calls = ValuedGenerator(self._copy_calls(dst))
|
199
162
|
|
200
|
-
for
|
201
|
-
await
|
163
|
+
for coro in calls:
|
164
|
+
await coro
|
202
165
|
|
203
|
-
yield
|
166
|
+
yield calls.value
|
204
167
|
|
205
|
-
|
206
|
-
assert isinstance(dst, Path)
|
207
|
-
await self._rmtree(dst.parent)
|
208
|
-
else:
|
209
|
-
await self._rmtree(dst)
|
168
|
+
await self._rmtree(calls.value.path)
|
210
169
|
|
211
170
|
@awaitableasynccontextmanager
|
212
171
|
@asynccontextmanager
|
@@ -218,24 +177,16 @@ class AsyncFoamCase(_FoamCaseRecipes):
|
|
218
177
|
|
219
178
|
Use as an async context manager to automatically delete the clone when done.
|
220
179
|
|
221
|
-
:param dst: The destination path. If None, clone to
|
180
|
+
:param dst: The destination path. If None, clone to `$FOAM_RUN/foamlib`.
|
222
181
|
"""
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
yield type(self)(dst)
|
233
|
-
|
234
|
-
if tmp:
|
235
|
-
assert isinstance(dst, Path)
|
236
|
-
await self._rmtree(dst.parent)
|
237
|
-
else:
|
238
|
-
await self._rmtree(dst)
|
182
|
+
calls = ValuedGenerator(self._clone_calls(dst))
|
183
|
+
|
184
|
+
for coro in calls:
|
185
|
+
await coro
|
186
|
+
|
187
|
+
yield calls.value
|
188
|
+
|
189
|
+
await self._rmtree(calls.value.path)
|
239
190
|
|
240
191
|
@staticmethod
|
241
192
|
def map(coro: Callable[[X], Awaitable[Y]], iterable: Iterable[X]) -> Iterable[Y]:
|
@@ -2,6 +2,8 @@ import os
|
|
2
2
|
import shlex
|
3
3
|
import shutil
|
4
4
|
import sys
|
5
|
+
import tempfile
|
6
|
+
from abc import abstractmethod
|
5
7
|
from contextlib import contextmanager
|
6
8
|
from pathlib import Path
|
7
9
|
from typing import (
|
@@ -16,6 +18,7 @@ if sys.version_info >= (3, 9):
|
|
16
18
|
from collections.abc import (
|
17
19
|
Callable,
|
18
20
|
Collection,
|
21
|
+
Coroutine,
|
19
22
|
Generator,
|
20
23
|
Mapping,
|
21
24
|
Sequence,
|
@@ -26,17 +29,96 @@ else:
|
|
26
29
|
from typing import (
|
27
30
|
Callable,
|
28
31
|
Collection,
|
32
|
+
Coroutine,
|
29
33
|
Generator,
|
30
34
|
Mapping,
|
31
35
|
Sequence,
|
32
36
|
)
|
33
37
|
|
38
|
+
if sys.version_info >= (3, 11):
|
39
|
+
from typing import Self
|
40
|
+
else:
|
41
|
+
from typing_extensions import Self
|
42
|
+
|
34
43
|
from ._base import FoamCaseBase
|
35
44
|
from ._subprocess import DEVNULL, STDOUT
|
36
45
|
|
37
46
|
|
38
|
-
class
|
39
|
-
def
|
47
|
+
class FoamCaseRunBase(FoamCaseBase):
|
48
|
+
def __delitem__(self, key: Union[int, float, str]) -> None:
|
49
|
+
shutil.rmtree(self[key].path)
|
50
|
+
|
51
|
+
@staticmethod
|
52
|
+
@abstractmethod
|
53
|
+
def _run(
|
54
|
+
cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
|
55
|
+
*,
|
56
|
+
cpus: int,
|
57
|
+
**kwargs: Any,
|
58
|
+
) -> Union[None, Coroutine[None, None, None]]:
|
59
|
+
raise NotImplementedError
|
60
|
+
|
61
|
+
@staticmethod
|
62
|
+
@abstractmethod
|
63
|
+
def _rmtree(
|
64
|
+
path: Union["os.PathLike[str]", str], *, ignore_errors: bool = False
|
65
|
+
) -> Union[None, Coroutine[None, None, None]]:
|
66
|
+
raise NotImplementedError
|
67
|
+
|
68
|
+
@staticmethod
|
69
|
+
@abstractmethod
|
70
|
+
def _copytree(
|
71
|
+
src: Union["os.PathLike[str]", str],
|
72
|
+
dest: Union["os.PathLike[str]", str],
|
73
|
+
*,
|
74
|
+
symlinks: bool = False,
|
75
|
+
ignore: Optional[
|
76
|
+
Callable[[Union["os.PathLike[str]", str], Collection[str]], Collection[str]]
|
77
|
+
] = None,
|
78
|
+
) -> Union[None, Coroutine[None, None, None]]:
|
79
|
+
raise NotImplementedError
|
80
|
+
|
81
|
+
@abstractmethod
|
82
|
+
def clean(self, *, check: bool = False) -> Union[None, Coroutine[None, None, None]]:
|
83
|
+
raise NotImplementedError
|
84
|
+
|
85
|
+
@abstractmethod
|
86
|
+
def copy(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Any:
|
87
|
+
raise NotImplementedError
|
88
|
+
|
89
|
+
@abstractmethod
|
90
|
+
def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Any:
|
91
|
+
raise NotImplementedError
|
92
|
+
|
93
|
+
@abstractmethod
|
94
|
+
def run(
|
95
|
+
self,
|
96
|
+
cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
|
97
|
+
*,
|
98
|
+
parallel: Optional[bool] = None,
|
99
|
+
cpus: Optional[int] = None,
|
100
|
+
check: bool = True,
|
101
|
+
log: bool = True,
|
102
|
+
) -> Union[None, Coroutine[None, None, None]]:
|
103
|
+
raise NotImplementedError
|
104
|
+
|
105
|
+
@abstractmethod
|
106
|
+
def block_mesh(
|
107
|
+
self, *, check: bool = True
|
108
|
+
) -> Union[None, Coroutine[None, None, None]]:
|
109
|
+
raise NotImplementedError
|
110
|
+
|
111
|
+
@abstractmethod
|
112
|
+
def decompose_par(
|
113
|
+
self, *, check: bool = True
|
114
|
+
) -> Union[None, Coroutine[None, None, None]]:
|
115
|
+
raise NotImplementedError
|
116
|
+
|
117
|
+
@abstractmethod
|
118
|
+
def restore_0_dir(self) -> Union[None, Coroutine[None, None, None]]:
|
119
|
+
raise NotImplementedError
|
120
|
+
|
121
|
+
def __clean_paths(self) -> Set[Path]:
|
40
122
|
has_decompose_par_dict = (self.path / "system" / "decomposeParDict").is_file()
|
41
123
|
has_block_mesh_dict = (self.path / "system" / "blockMeshDict").is_file()
|
42
124
|
|
@@ -61,18 +143,14 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
61
143
|
if has_block_mesh_dict and (self.path / "constant" / "polyMesh").exists():
|
62
144
|
paths.add(self.path / "constant" / "polyMesh")
|
63
145
|
|
64
|
-
|
65
|
-
paths.update(self.path.glob("log.*"))
|
146
|
+
paths.update(self.path.glob("log.*"))
|
66
147
|
|
67
148
|
return paths
|
68
149
|
|
69
|
-
def
|
70
|
-
shutil.rmtree(self[key].path)
|
71
|
-
|
72
|
-
def _clone_ignore(
|
150
|
+
def __clone_ignore(
|
73
151
|
self,
|
74
152
|
) -> Callable[[Union["os.PathLike[str]", str], Collection[str]], Collection[str]]:
|
75
|
-
clean_paths = self.
|
153
|
+
clean_paths = self.__clean_paths()
|
76
154
|
|
77
155
|
def ignore(
|
78
156
|
path: Union["os.PathLike[str]", str], names: Collection[str]
|
@@ -82,7 +160,7 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
82
160
|
|
83
161
|
return ignore
|
84
162
|
|
85
|
-
def
|
163
|
+
def __clean_script(self) -> Optional[Path]:
|
86
164
|
"""Return the path to the (All)clean script, or None if no clean script is found."""
|
87
165
|
clean = self.path / "clean"
|
88
166
|
all_clean = self.path / "Allclean"
|
@@ -97,9 +175,9 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
97
175
|
if sys.argv and Path(sys.argv[0]).absolute() == script.absolute():
|
98
176
|
return None
|
99
177
|
|
100
|
-
return script
|
178
|
+
return script
|
101
179
|
|
102
|
-
def
|
180
|
+
def __run_script(self, *, parallel: Optional[bool] = None) -> Optional[Path]:
|
103
181
|
"""Return the path to the (All)run script, or None if no run script is found."""
|
104
182
|
run = self.path / "run"
|
105
183
|
run_parallel = self.path / "run-parallel"
|
@@ -132,7 +210,7 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
132
210
|
|
133
211
|
return script
|
134
212
|
|
135
|
-
def
|
213
|
+
def __env(self, *, shell: bool) -> Optional[Mapping[str, str]]:
|
136
214
|
sip_workaround = os.environ.get(
|
137
215
|
"FOAM_LD_LIBRARY_PATH", ""
|
138
216
|
) and not os.environ.get("DYLD_LIBRARY_PATH", "")
|
@@ -151,7 +229,7 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
151
229
|
return None
|
152
230
|
|
153
231
|
@contextmanager
|
154
|
-
def
|
232
|
+
def __output(
|
155
233
|
self, cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str], *, log: bool
|
156
234
|
) -> Generator[Tuple[Union[int, IO[bytes]], Union[int, IO[bytes]]], None, None]:
|
157
235
|
if log:
|
@@ -168,70 +246,64 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
168
246
|
else:
|
169
247
|
yield DEVNULL, DEVNULL
|
170
248
|
|
171
|
-
def
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
self,
|
185
|
-
|
186
|
-
|
249
|
+
def __mkrundir(self) -> Path:
|
250
|
+
d = Path(os.environ["FOAM_RUN"], "foamlib")
|
251
|
+
d.mkdir(parents=True, exist_ok=True)
|
252
|
+
ret = Path(tempfile.mkdtemp(prefix=f"{self.name}-", dir=d))
|
253
|
+
ret.rmdir()
|
254
|
+
return ret
|
255
|
+
|
256
|
+
def _copy_calls(
|
257
|
+
self, dst: Optional[Union["os.PathLike[str]", str]]
|
258
|
+
) -> Generator[Any, None, Self]:
|
259
|
+
if dst is None:
|
260
|
+
dst = self.__mkrundir()
|
261
|
+
|
262
|
+
yield self._copytree(self.path, dst, symlinks=True)
|
263
|
+
|
264
|
+
return type(self)(dst)
|
265
|
+
|
266
|
+
def _clean_calls(self, *, check: bool = False) -> Generator[Any, None, None]:
|
267
|
+
script_path = self.__clean_script()
|
187
268
|
|
188
269
|
if script_path is not None:
|
189
|
-
yield (
|
270
|
+
yield self.run([script_path], cpus=0, check=check, log=False)
|
190
271
|
else:
|
191
|
-
for p in self.
|
272
|
+
for p in self.__clean_paths():
|
192
273
|
if p.is_dir():
|
193
|
-
yield
|
274
|
+
yield self._rmtree(p)
|
194
275
|
else:
|
195
276
|
p.unlink()
|
196
277
|
|
197
|
-
def
|
198
|
-
self,
|
199
|
-
) -> Generator[
|
200
|
-
if
|
201
|
-
|
202
|
-
|
278
|
+
def _clone_calls(
|
279
|
+
self, dst: Optional[Union["os.PathLike[str]", str]]
|
280
|
+
) -> Generator[Any, None, Self]:
|
281
|
+
if dst is None:
|
282
|
+
dst = self.__mkrundir()
|
283
|
+
|
284
|
+
if self.__clean_script() is not None:
|
285
|
+
yield self.copy(dst)
|
286
|
+
yield type(self)(dst).clean()
|
203
287
|
else:
|
204
|
-
yield (
|
205
|
-
|
206
|
-
(
|
207
|
-
self.path,
|
208
|
-
dest,
|
209
|
-
),
|
210
|
-
{"symlinks": True, "ignore": self._clone_ignore()},
|
288
|
+
yield self._copytree(
|
289
|
+
self.path, dst, symlinks=True, ignore=self.__clone_ignore()
|
211
290
|
)
|
212
291
|
|
213
|
-
|
214
|
-
|
215
|
-
) -> Generator[
|
216
|
-
yield
|
217
|
-
yield (
|
218
|
-
|
219
|
-
|
220
|
-
self.path / "0.orig",
|
221
|
-
self.path / "0",
|
222
|
-
),
|
223
|
-
{"symlinks": True},
|
224
|
-
)
|
225
|
-
|
226
|
-
def _run_cmds(
|
292
|
+
return type(self)(dst)
|
293
|
+
|
294
|
+
def _restore_0_dir_calls(self) -> Generator[Any, None, None]:
|
295
|
+
yield self._rmtree(self.path / "0", ignore_errors=True)
|
296
|
+
yield self._copytree(self.path / "0.orig", self.path / "0", symlinks=True)
|
297
|
+
|
298
|
+
def _run_calls(
|
227
299
|
self,
|
228
300
|
cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
|
229
301
|
*,
|
230
|
-
script: bool = True,
|
231
302
|
parallel: Optional[bool] = None,
|
232
303
|
cpus: Optional[int] = None,
|
233
304
|
check: bool = True,
|
234
|
-
|
305
|
+
log: bool = True,
|
306
|
+
) -> Generator[Any, None, None]:
|
235
307
|
if cmd is not None:
|
236
308
|
if parallel:
|
237
309
|
if cpus is None:
|
@@ -241,10 +313,32 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
241
313
|
if cpus is None:
|
242
314
|
cpus = 1
|
243
315
|
|
244
|
-
|
316
|
+
with self.__output(cmd, log=log) as (stdout, stderr):
|
317
|
+
if parallel:
|
318
|
+
if isinstance(cmd, str):
|
319
|
+
cmd = [
|
320
|
+
"mpiexec",
|
321
|
+
"-n",
|
322
|
+
str(cpus),
|
323
|
+
"/bin/sh",
|
324
|
+
"-c",
|
325
|
+
f"{cmd} -parallel",
|
326
|
+
]
|
327
|
+
else:
|
328
|
+
cmd = ["mpiexec", "-n", str(cpus), *cmd, "-parallel"]
|
329
|
+
|
330
|
+
yield self._run(
|
331
|
+
cmd,
|
332
|
+
cpus=cpus,
|
333
|
+
check=check,
|
334
|
+
cwd=self.path,
|
335
|
+
env=self.__env(shell=isinstance(cmd, str)),
|
336
|
+
stdout=stdout,
|
337
|
+
stderr=stderr,
|
338
|
+
)
|
245
339
|
|
246
340
|
else:
|
247
|
-
script_path = self.
|
341
|
+
script_path = self.__run_script(parallel=parallel)
|
248
342
|
|
249
343
|
if script_path is not None:
|
250
344
|
if parallel or parallel is None:
|
@@ -259,18 +353,14 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
259
353
|
if cpus is None:
|
260
354
|
cpus = 1
|
261
355
|
|
262
|
-
yield (
|
263
|
-
"_run",
|
264
|
-
([script_path],),
|
265
|
-
{"parallel": False, "cpus": cpus, "check": check},
|
266
|
-
)
|
356
|
+
yield self.run([script_path], parallel=False, cpus=cpus, check=check)
|
267
357
|
|
268
358
|
else:
|
269
359
|
if not self and (self.path / "0.orig").is_dir():
|
270
|
-
yield
|
360
|
+
yield self.restore_0_dir()
|
271
361
|
|
272
362
|
if (self.path / "system" / "blockMeshDict").is_file():
|
273
|
-
yield
|
363
|
+
yield self.block_mesh(check=check)
|
274
364
|
|
275
365
|
if parallel is None:
|
276
366
|
parallel = (
|
@@ -284,7 +374,7 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
284
374
|
self._nprocessors == 0
|
285
375
|
and (self.path / "system" / "decomposeParDict").is_file()
|
286
376
|
):
|
287
|
-
yield
|
377
|
+
yield self.decompose_par(check=check)
|
288
378
|
|
289
379
|
if cpus is None:
|
290
380
|
cpus = max(self._nprocessors, 1)
|
@@ -292,8 +382,6 @@ class _FoamCaseRecipes(FoamCaseBase):
|
|
292
382
|
if cpus is None:
|
293
383
|
cpus = 1
|
294
384
|
|
295
|
-
yield (
|
296
|
-
|
297
|
-
([self.application],),
|
298
|
-
{"parallel": parallel, "cpus": cpus, "check": check},
|
385
|
+
yield self.run(
|
386
|
+
[self.application], parallel=parallel, cpus=cpus, check=check
|
299
387
|
)
|
foamlib/_cases/_sync.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import os
|
2
2
|
import shutil
|
3
3
|
import sys
|
4
|
-
import tempfile
|
5
4
|
from pathlib import Path
|
6
5
|
from types import TracebackType
|
7
6
|
from typing import (
|
7
|
+
Any,
|
8
8
|
Callable,
|
9
9
|
Optional,
|
10
10
|
Type,
|
@@ -21,11 +21,12 @@ if sys.version_info >= (3, 11):
|
|
21
21
|
else:
|
22
22
|
from typing_extensions import Self
|
23
23
|
|
24
|
-
from .
|
24
|
+
from ._run import FoamCaseRunBase
|
25
25
|
from ._subprocess import run_sync
|
26
|
+
from ._util import ValuedGenerator
|
26
27
|
|
27
28
|
|
28
|
-
class FoamCase(
|
29
|
+
class FoamCase(FoamCaseRunBase):
|
29
30
|
"""
|
30
31
|
An OpenFOAM case.
|
31
32
|
|
@@ -38,7 +39,15 @@ class FoamCase(_FoamCaseRecipes):
|
|
38
39
|
|
39
40
|
def __init__(self, path: Union["os.PathLike[str]", str] = Path()):
|
40
41
|
super().__init__(path)
|
41
|
-
|
42
|
+
|
43
|
+
@staticmethod
|
44
|
+
def _run(
|
45
|
+
cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
|
46
|
+
*,
|
47
|
+
cpus: int,
|
48
|
+
**kwargs: Any,
|
49
|
+
) -> None:
|
50
|
+
run_sync(cmd, **kwargs)
|
42
51
|
|
43
52
|
@staticmethod
|
44
53
|
def _rmtree(
|
@@ -59,10 +68,6 @@ class FoamCase(_FoamCaseRecipes):
|
|
59
68
|
shutil.copytree(src, dest, symlinks=symlinks, ignore=ignore)
|
60
69
|
|
61
70
|
def __enter__(self) -> "FoamCase":
|
62
|
-
if self._tmp is None:
|
63
|
-
raise RuntimeError(
|
64
|
-
"Cannot use a non-copied/cloned case as a context manager"
|
65
|
-
)
|
66
71
|
return self
|
67
72
|
|
68
73
|
def __exit__(
|
@@ -71,83 +76,37 @@ class FoamCase(_FoamCaseRecipes):
|
|
71
76
|
exc_val: Optional[BaseException],
|
72
77
|
exc_tb: Optional[TracebackType],
|
73
78
|
) -> None:
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
else:
|
78
|
-
self._rmtree(self.path)
|
79
|
-
else:
|
80
|
-
raise RuntimeError(
|
81
|
-
"Cannot use a non-copied/cloned case as a context manager"
|
82
|
-
)
|
83
|
-
|
84
|
-
def clean(
|
85
|
-
self,
|
86
|
-
*,
|
87
|
-
script: bool = True,
|
88
|
-
check: bool = False,
|
89
|
-
) -> None:
|
79
|
+
self._rmtree(self.path)
|
80
|
+
|
81
|
+
def clean(self, *, check: bool = False) -> None:
|
90
82
|
"""
|
91
83
|
Clean this case.
|
92
84
|
|
93
|
-
:param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
|
94
85
|
:param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
|
95
86
|
"""
|
96
|
-
for
|
97
|
-
|
98
|
-
|
99
|
-
def _run(
|
100
|
-
self,
|
101
|
-
cmd: Union[Sequence[Union[str, "os.PathLike[str]"]], str],
|
102
|
-
*,
|
103
|
-
parallel: bool = False,
|
104
|
-
cpus: int = 1,
|
105
|
-
check: bool = True,
|
106
|
-
log: bool = True,
|
107
|
-
) -> None:
|
108
|
-
with self._output(cmd, log=log) as (stdout, stderr):
|
109
|
-
if parallel:
|
110
|
-
if isinstance(cmd, str):
|
111
|
-
cmd = [
|
112
|
-
"mpiexec",
|
113
|
-
"-n",
|
114
|
-
str(cpus),
|
115
|
-
"/bin/sh",
|
116
|
-
"-c",
|
117
|
-
f"{cmd} -parallel",
|
118
|
-
]
|
119
|
-
else:
|
120
|
-
cmd = ["mpiexec", "-n", str(cpus), *cmd, "-parallel"]
|
121
|
-
|
122
|
-
run_sync(
|
123
|
-
cmd,
|
124
|
-
check=check,
|
125
|
-
cwd=self.path,
|
126
|
-
env=self._env(shell=isinstance(cmd, str)),
|
127
|
-
stdout=stdout,
|
128
|
-
stderr=stderr,
|
129
|
-
)
|
87
|
+
for _ in self._clean_calls(check=check):
|
88
|
+
pass
|
130
89
|
|
131
90
|
def run(
|
132
91
|
self,
|
133
92
|
cmd: Optional[Union[Sequence[Union[str, "os.PathLike[str]"]], str]] = None,
|
134
93
|
*,
|
135
|
-
script: bool = True,
|
136
94
|
parallel: Optional[bool] = None,
|
95
|
+
cpus: Optional[int] = None,
|
137
96
|
check: bool = True,
|
97
|
+
log: bool = True,
|
138
98
|
) -> None:
|
139
99
|
"""
|
140
100
|
Run this case, or a specified command in the context of this case.
|
141
101
|
|
142
102
|
:param cmd: The command to run. If None, run the case. If a sequence, the first element is the command and the rest are arguments. If a string, `cmd` is executed in a shell.
|
143
|
-
:param script: If True and `cmd` is None, use an (All)run(-parallel) script if it exists for running the case. If False or no run script is found, autodetermine the command(s) needed to run the case.
|
144
103
|
:param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
|
104
|
+
:param cpus: The number of CPUs to use. If None, autodetect according to the case.
|
145
105
|
:param check: If True, raise a CalledProcessError if any command returns a non-zero exit code.
|
106
|
+
:param log: If True, log the command output to a file.
|
146
107
|
"""
|
147
|
-
for
|
148
|
-
|
149
|
-
):
|
150
|
-
getattr(self, name)(*args, **kwargs)
|
108
|
+
for _ in self._run_calls(cmd=cmd, parallel=parallel, check=check):
|
109
|
+
pass
|
151
110
|
|
152
111
|
def block_mesh(self, *, check: bool = True) -> None:
|
153
112
|
"""Run blockMesh on this case."""
|
@@ -163,49 +122,35 @@ class FoamCase(_FoamCaseRecipes):
|
|
163
122
|
|
164
123
|
def restore_0_dir(self) -> None:
|
165
124
|
"""Restore the 0 directory from the 0.orig directory."""
|
166
|
-
for
|
167
|
-
|
125
|
+
for _ in self._restore_0_dir_calls():
|
126
|
+
pass
|
168
127
|
|
169
|
-
def copy(self, dst: Optional[Union["os.PathLike[str]", str]] = None) ->
|
128
|
+
def copy(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Self:
|
170
129
|
"""
|
171
130
|
Make a copy of this case.
|
172
131
|
|
173
132
|
Use as a context manager to automatically delete the copy when done.
|
174
133
|
|
175
|
-
:param dst: The destination path. If None,
|
134
|
+
:param dst: The destination path. If None, clone to `$FOAM_RUN/foamlib`.
|
176
135
|
"""
|
177
|
-
|
178
|
-
dst = Path(tempfile.mkdtemp(), self.name)
|
179
|
-
tmp = True
|
180
|
-
else:
|
181
|
-
tmp = False
|
136
|
+
cmds = ValuedGenerator(self._copy_calls(dst))
|
182
137
|
|
183
|
-
for
|
184
|
-
|
138
|
+
for _ in cmds:
|
139
|
+
pass
|
185
140
|
|
186
|
-
|
187
|
-
ret._tmp = tmp
|
141
|
+
return cmds.value
|
188
142
|
|
189
|
-
|
190
|
-
|
191
|
-
def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> "Self":
|
143
|
+
def clone(self, dst: Optional[Union["os.PathLike[str]", str]] = None) -> Self:
|
192
144
|
"""
|
193
145
|
Clone this case (make a clean copy).
|
194
146
|
|
195
147
|
Use as a context manager to automatically delete the clone when done.
|
196
148
|
|
197
|
-
:param dst: The destination path. If None, clone to
|
149
|
+
:param dst: The destination path. If None, clone to `$FOAM_RUN/foamlib`.
|
198
150
|
"""
|
199
|
-
|
200
|
-
dst = Path(tempfile.mkdtemp(), self.name)
|
201
|
-
tmp = True
|
202
|
-
else:
|
203
|
-
tmp = False
|
204
|
-
|
205
|
-
for name, args, kwargs in self._clone_cmds(dst):
|
206
|
-
getattr(self, name)(*args, **kwargs)
|
151
|
+
cmds = ValuedGenerator(self._clone_calls(dst))
|
207
152
|
|
208
|
-
|
209
|
-
|
153
|
+
for _ in cmds:
|
154
|
+
pass
|
210
155
|
|
211
|
-
return
|
156
|
+
return cmds.value
|
foamlib/_cases/_util.py
CHANGED
@@ -17,9 +17,20 @@ else:
|
|
17
17
|
from typing import Generator
|
18
18
|
|
19
19
|
|
20
|
+
Y = TypeVar("Y")
|
21
|
+
S = TypeVar("S")
|
20
22
|
R = TypeVar("R")
|
21
23
|
|
22
24
|
|
25
|
+
class ValuedGenerator(Generic[Y, S, R]):
|
26
|
+
def __init__(self, generator: Generator[Y, S, R]):
|
27
|
+
self._generator = generator
|
28
|
+
|
29
|
+
def __iter__(self) -> Generator[Y, S, R]:
|
30
|
+
self.value = yield from self._generator
|
31
|
+
return self.value
|
32
|
+
|
33
|
+
|
23
34
|
class _AwaitableAsyncContextManager(Generic[R]):
|
24
35
|
def __init__(self, cm: "AsyncContextManager[R]"):
|
25
36
|
self._cm = cm
|
foamlib/_files/_files.py
CHANGED
@@ -11,13 +11,8 @@ if sys.version_info >= (3, 9):
|
|
11
11
|
else:
|
12
12
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
13
13
|
|
14
|
-
if sys.version_info >= (3, 11):
|
15
|
-
from typing import Self
|
16
|
-
else:
|
17
|
-
from typing_extensions import Self
|
18
|
-
|
19
14
|
from ._base import FoamFileBase
|
20
|
-
from ._io import
|
15
|
+
from ._io import FoamFileIO
|
21
16
|
from ._serialization import Kind, dumpb
|
22
17
|
from ._util import is_sequence
|
23
18
|
|
@@ -33,7 +28,7 @@ class FoamFile(
|
|
33
28
|
Optional[Union[str, Tuple[str, ...]]],
|
34
29
|
Union["FoamFile.Data", "FoamFile.SubDict"],
|
35
30
|
],
|
36
|
-
|
31
|
+
FoamFileIO,
|
37
32
|
):
|
38
33
|
"""
|
39
34
|
An OpenFOAM data file.
|
@@ -101,28 +96,6 @@ class FoamFile(
|
|
101
96
|
|
102
97
|
return cast(FoamFileBase._Dict, ret)
|
103
98
|
|
104
|
-
def create(self, *, exist_ok: bool = False, parents: bool = False) -> Self:
|
105
|
-
"""
|
106
|
-
Create the file.
|
107
|
-
|
108
|
-
:param exist_ok: If False, raise a FileExistsError if the file already exists.
|
109
|
-
|
110
|
-
:param parents: If True, also create parent directories as needed.
|
111
|
-
"""
|
112
|
-
if self.path.exists():
|
113
|
-
if not exist_ok:
|
114
|
-
raise FileExistsError(self.path)
|
115
|
-
else:
|
116
|
-
return self
|
117
|
-
|
118
|
-
if parents:
|
119
|
-
self.path.parent.mkdir(parents=True, exist_ok=True)
|
120
|
-
|
121
|
-
self.path.touch()
|
122
|
-
self._write_header()
|
123
|
-
|
124
|
-
return self
|
125
|
-
|
126
99
|
@property
|
127
100
|
def version(self) -> float:
|
128
101
|
"""Alias of `self["FoamFile", "version"]`."""
|
@@ -185,17 +158,6 @@ class FoamFile(
|
|
185
158
|
def object_(self, value: str) -> None:
|
186
159
|
self["FoamFile", "object"] = value
|
187
160
|
|
188
|
-
def _write_header(self) -> None:
|
189
|
-
assert "FoamFile" not in self
|
190
|
-
assert not self
|
191
|
-
|
192
|
-
self["FoamFile"] = {}
|
193
|
-
self.version = 2.0
|
194
|
-
self.format = "ascii"
|
195
|
-
self.class_ = "dictionary"
|
196
|
-
self.location = f'"{self.path.parent.name}"'
|
197
|
-
self.object_ = self.path.name
|
198
|
-
|
199
161
|
def __getitem__(
|
200
162
|
self, keywords: Optional[Union[str, Tuple[str, ...]]]
|
201
163
|
) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
|
@@ -222,8 +184,24 @@ class FoamFile(
|
|
222
184
|
elif not isinstance(keywords, tuple):
|
223
185
|
keywords = (keywords,)
|
224
186
|
|
225
|
-
|
226
|
-
|
187
|
+
try:
|
188
|
+
write_header = (
|
189
|
+
not self
|
190
|
+
and "FoamFile" not in self
|
191
|
+
and (not keywords or keywords[0] != "FoamFile")
|
192
|
+
)
|
193
|
+
except FileNotFoundError:
|
194
|
+
write_header = not keywords or keywords[0] != "FoamFile"
|
195
|
+
|
196
|
+
if write_header:
|
197
|
+
self["FoamFile"] = {}
|
198
|
+
self.version = 2.0
|
199
|
+
self.format = "ascii"
|
200
|
+
self.class_ = "dictionary"
|
201
|
+
self.location = f'"{self.path.parent.name}"'
|
202
|
+
self.object_ = (
|
203
|
+
self.path.stem if self.path.suffix == ".gz" else self.path.name
|
204
|
+
)
|
227
205
|
|
228
206
|
kind = Kind.DEFAULT
|
229
207
|
if keywords == ("internalField",) or (
|
@@ -253,7 +231,7 @@ class FoamFile(
|
|
253
231
|
self[keywords] = data
|
254
232
|
|
255
233
|
else:
|
256
|
-
contents, parsed = self._read()
|
234
|
+
contents, parsed = self._read(missing_ok=True)
|
257
235
|
|
258
236
|
start, end = parsed.entry_location(keywords, missing_ok=True)
|
259
237
|
|
foamlib/_files/_io.py
CHANGED
@@ -18,7 +18,7 @@ else:
|
|
18
18
|
from ._parsing import Parsed
|
19
19
|
|
20
20
|
|
21
|
-
class
|
21
|
+
class FoamFileIO:
|
22
22
|
def __init__(self, path: Union[str, Path]) -> None:
|
23
23
|
self.path = Path(path).absolute()
|
24
24
|
|
@@ -29,7 +29,7 @@ class _FoamFileIO:
|
|
29
29
|
|
30
30
|
def __enter__(self) -> Self:
|
31
31
|
if self.__defer_io == 0:
|
32
|
-
self._read()
|
32
|
+
self._read(missing_ok=True)
|
33
33
|
self.__defer_io += 1
|
34
34
|
return self
|
35
35
|
|
@@ -44,18 +44,26 @@ class _FoamFileIO:
|
|
44
44
|
assert self.__contents is not None
|
45
45
|
self._write(self.__contents)
|
46
46
|
|
47
|
-
def _read(self) -> Tuple[bytes, Parsed]:
|
47
|
+
def _read(self, *, missing_ok: bool = False) -> Tuple[bytes, Parsed]:
|
48
48
|
if not self.__defer_io:
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
contents =
|
49
|
+
try:
|
50
|
+
contents = self.path.read_bytes()
|
51
|
+
except FileNotFoundError:
|
52
|
+
contents = None
|
53
|
+
else:
|
54
|
+
assert isinstance(contents, bytes)
|
55
|
+
if self.path.suffix == ".gz":
|
56
|
+
contents = gzip.decompress(contents)
|
53
57
|
|
54
58
|
if contents != self.__contents:
|
55
59
|
self.__contents = contents
|
56
60
|
self.__parsed = None
|
57
61
|
|
58
|
-
|
62
|
+
if self.__contents is None:
|
63
|
+
if missing_ok:
|
64
|
+
return b"", Parsed(b"")
|
65
|
+
else:
|
66
|
+
raise FileNotFoundError(self.path)
|
59
67
|
|
60
68
|
if self.__parsed is None:
|
61
69
|
parsed = Parsed(self.__contents)
|
foamlib/_files/_parsing.py
CHANGED
@@ -95,68 +95,68 @@ def _unpack_binary_field(
|
|
95
95
|
return [all]
|
96
96
|
|
97
97
|
|
98
|
-
_BINARY_FIELD = (
|
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
|
-
)
|
132
|
-
+ Literal(")").suppress()
|
133
|
-
).set_parse_action(_unpack_binary_field)
|
134
|
-
|
135
|
-
|
136
98
|
_SWITCH = (
|
137
99
|
Keyword("yes") | Keyword("true") | Keyword("on") | Keyword("y") | Keyword("t")
|
138
100
|
).set_parse_action(lambda: True) | (
|
139
101
|
Keyword("no") | Keyword("false") | Keyword("off") | Keyword("n") | Keyword("f")
|
140
102
|
).set_parse_action(lambda: False)
|
141
103
|
_DIMENSIONS = (
|
142
|
-
Literal("[").suppress() + common.number
|
104
|
+
Literal("[").suppress() + common.number[0, 7] + Literal("]").suppress()
|
143
105
|
).set_parse_action(lambda tks: FoamFileBase.DimensionSet(*tks))
|
144
106
|
_TENSOR = _list_of(common.number) | common.number
|
145
107
|
_IDENTIFIER = Combine(
|
146
|
-
Word(identchars + "$", printables, exclude_chars="{(;)}")
|
147
|
-
+ Opt(Literal("(") + Word(printables, exclude_chars="{(;)}") + Literal(")"))
|
108
|
+
Word(identchars + "$", printables, exclude_chars="[{(;)}]")
|
109
|
+
+ Opt(Literal("(") + Word(printables, exclude_chars="[{(;)}]") + Literal(")"))
|
148
110
|
)
|
149
111
|
_DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
|
150
112
|
lambda tks: FoamFileBase.Dimensioned(*reversed(tks.as_list()))
|
151
113
|
)
|
152
|
-
_FIELD = (
|
153
|
-
|
154
|
-
|
155
|
-
|
114
|
+
_FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
|
115
|
+
Keyword("nonuniform").suppress()
|
116
|
+
+ (
|
117
|
+
_list_of(_TENSOR)
|
118
|
+
| (
|
119
|
+
Literal("List").suppress()
|
120
|
+
+ Literal("<").suppress()
|
121
|
+
+ (
|
122
|
+
counted_array(
|
123
|
+
CharsNotIn(exact=8),
|
124
|
+
Literal("scalar").suppress()
|
125
|
+
+ Literal(">").suppress()
|
126
|
+
+ common.integer
|
127
|
+
+ Literal("(").suppress(),
|
128
|
+
)
|
129
|
+
| counted_array(
|
130
|
+
CharsNotIn(exact=8 * 3),
|
131
|
+
Literal("vector").suppress()
|
132
|
+
+ Literal(">").suppress()
|
133
|
+
+ common.integer
|
134
|
+
+ Literal("(").suppress(),
|
135
|
+
)
|
136
|
+
| counted_array(
|
137
|
+
CharsNotIn(exact=8 * 6),
|
138
|
+
Literal("symmTensor").suppress()
|
139
|
+
+ Literal(">").suppress()
|
140
|
+
+ common.integer
|
141
|
+
+ Literal("(").suppress(),
|
142
|
+
)
|
143
|
+
| counted_array(
|
144
|
+
CharsNotIn(exact=8 * 9),
|
145
|
+
Literal("tensor").suppress()
|
146
|
+
+ Literal(">").suppress()
|
147
|
+
+ common.integer
|
148
|
+
+ Literal("(").suppress(),
|
149
|
+
)
|
150
|
+
)
|
151
|
+
+ Literal(")").suppress()
|
152
|
+
).set_parse_action(_unpack_binary_field)
|
153
|
+
)
|
156
154
|
)
|
157
155
|
_TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
|
158
156
|
_DATA = Forward()
|
159
|
-
_KEYWORD =
|
157
|
+
_KEYWORD = _TOKEN | _list_of(_IDENTIFIER).set_parse_action(
|
158
|
+
lambda tks: "(" + " ".join(tks[0]) + ")"
|
159
|
+
)
|
160
160
|
_KEYWORD_ENTRY = Dict(Group(_keyword_entry_of(_KEYWORD, _DATA)), asdict=True)
|
161
161
|
_DATA_ENTRY = Forward()
|
162
162
|
_LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
|
@@ -0,0 +1,21 @@
|
|
1
|
+
foamlib/__init__.py,sha256=Qg1zNl1cgh7eMuYuDmUROom5krkuBVijkBu6M4m44No,392
|
2
|
+
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
foamlib/_cases/__init__.py,sha256=C0mpRu7c-X-4uVMKmVrZhwIyhBNyvUoCv0o-BQ72RC0,236
|
4
|
+
foamlib/_cases/_async.py,sha256=8x8Mkql6XPzMjH4t-NszAEDx4gpjNPnQiWxFkHrcFJU,6436
|
5
|
+
foamlib/_cases/_base.py,sha256=1CUkkK4afBxDgP79dmho97WJdj-GLgYhnrCSf_52Eao,6604
|
6
|
+
foamlib/_cases/_run.py,sha256=0xu5V3qvWnXYJvOh0GV8DM1gOiNc65frWJF3mviHPOM,12307
|
7
|
+
foamlib/_cases/_subprocess.py,sha256=CfUy_LrqLnMLR9FHINqInCd3soN6eYvonwZ30epiLu8,2234
|
8
|
+
foamlib/_cases/_sync.py,sha256=BqEA-EE5JltOGwisAOeTM8kqawWqw33y0Ze92TZe6KQ,4758
|
9
|
+
foamlib/_cases/_util.py,sha256=GNndpqw3Jg_S-Hxzl5vwRgD0czcTNb9NYHMhcfBoMBg,1493
|
10
|
+
foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
|
11
|
+
foamlib/_files/_base.py,sha256=zaFDjLE6jB7WtGWk8hfKusjLtlGu6CZV16AHJpRUibs,1929
|
12
|
+
foamlib/_files/_files.py,sha256=DxM5JmXv19PJrqlKNwuP6SsB0nye6aN1hiPwble7fFM,15657
|
13
|
+
foamlib/_files/_io.py,sha256=uSh5XlgukwJkQSLELa4mshRD2aTajNk5vr_ZsBImSeU,2423
|
14
|
+
foamlib/_files/_parsing.py,sha256=DzgJ53QnohRQyLXn2zOs52RIQ7bJ_fS_S2Z7rmRyVK4,9023
|
15
|
+
foamlib/_files/_serialization.py,sha256=pb8_cIVgRhGS_ZV2p3x8p5_lK1SS6xzQHscAYYuOgFY,3407
|
16
|
+
foamlib/_files/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
|
17
|
+
foamlib-0.6.0.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
18
|
+
foamlib-0.6.0.dist-info/METADATA,sha256=IR3vRgU_77iKtMM_PiKdeGZIHi8EczgdhBbEsUF6dpE,6338
|
19
|
+
foamlib-0.6.0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
20
|
+
foamlib-0.6.0.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
21
|
+
foamlib-0.6.0.dist-info/RECORD,,
|
foamlib-0.5.1.dist-info/RECORD
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
foamlib/__init__.py,sha256=bRzkPtbKy1RniCL9eYinI6Yc8g8utDoXrPV6hFimMZY,392
|
2
|
-
foamlib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
foamlib/_cases/__init__.py,sha256=C0mpRu7c-X-4uVMKmVrZhwIyhBNyvUoCv0o-BQ72RC0,236
|
4
|
-
foamlib/_cases/_async.py,sha256=um1nrAjGcT8Vp1hz8XvC_87xVBjfeU3MrpnuzAHN5GY,8251
|
5
|
-
foamlib/_cases/_base.py,sha256=1CUkkK4afBxDgP79dmho97WJdj-GLgYhnrCSf_52Eao,6604
|
6
|
-
foamlib/_cases/_recipes.py,sha256=UTFnVuTvEf-9wn-Fr30a9wOOmyOxvWeDhbqBhdtbhzA,9692
|
7
|
-
foamlib/_cases/_subprocess.py,sha256=CfUy_LrqLnMLR9FHINqInCd3soN6eYvonwZ30epiLu8,2234
|
8
|
-
foamlib/_cases/_sync.py,sha256=R32Ea4BTOPq-x9OIndObcYZACF8e0RnzB4TywNvDxbA,6733
|
9
|
-
foamlib/_cases/_util.py,sha256=BpPW_91bFLzV3b4r50u-2pGMR3tfQQfY2xqToOHCUBk,1204
|
10
|
-
foamlib/_files/__init__.py,sha256=-UqB9YTH6mrJfXCX00kPTAAY20XG64u1MGPw_1ewLVs,148
|
11
|
-
foamlib/_files/_base.py,sha256=zaFDjLE6jB7WtGWk8hfKusjLtlGu6CZV16AHJpRUibs,1929
|
12
|
-
foamlib/_files/_files.py,sha256=6Fdrc0lksFK99i1a6wEsBi-BtYgSQnUc5s10h2LQ9ew,16091
|
13
|
-
foamlib/_files/_io.py,sha256=f_tYI7AqaFsQ8mtK__fEoIUqpYb3YmrI8X5D8updmNM,2084
|
14
|
-
foamlib/_files/_parsing.py,sha256=8V2CKZ45mKE3f9fP8lAfexIdhPGrq7elIZkpBkkGB6Q,8773
|
15
|
-
foamlib/_files/_serialization.py,sha256=pb8_cIVgRhGS_ZV2p3x8p5_lK1SS6xzQHscAYYuOgFY,3407
|
16
|
-
foamlib/_files/_util.py,sha256=UMzXmTFgvbp46w6k3oEZJoYC98pFgEK6LN5uLOwrlCg,397
|
17
|
-
foamlib-0.5.1.dist-info/LICENSE.txt,sha256=5Dte9TUnLZzPRs4NQzl-Jc2-Ljd-t_v0ZR5Ng5r0UsY,35131
|
18
|
-
foamlib-0.5.1.dist-info/METADATA,sha256=4Fq_okfGu-zY0IGrcUCD5IuuJxAESwNtr0EgXD0NzF8,6338
|
19
|
-
foamlib-0.5.1.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
20
|
-
foamlib-0.5.1.dist-info/top_level.txt,sha256=ZdVYtetXGwPwyfL-WhlhbTFQGAwKX5P_gXxtH9JYFPI,8
|
21
|
-
foamlib-0.5.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|