foamlib 0.2.10__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.2.10
3
+ Version: 0.3.1
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
@@ -1,9 +1,10 @@
1
1
  """A Python interface for interacting with OpenFOAM."""
2
2
 
3
- __version__ = "0.2.10"
3
+ __version__ = "0.3.1"
4
4
 
5
5
  from ._cases import AsyncFoamCase, FoamCase, FoamCaseBase
6
- from ._files import FoamDictionaryBase, FoamFieldFile, FoamFile
6
+ from ._files import FoamDict, FoamFieldFile, FoamFile
7
+ from ._util import CalledProcessError
7
8
 
8
9
  __all__ = [
9
10
  "FoamCase",
@@ -11,5 +12,6 @@ __all__ = [
11
12
  "FoamCaseBase",
12
13
  "FoamFile",
13
14
  "FoamFieldFile",
14
- "FoamDictionaryBase",
15
+ "FoamDict",
16
+ "CalledProcessError",
15
17
  ]
@@ -28,7 +28,7 @@ else:
28
28
  import aioshutil
29
29
 
30
30
  from ._files import FoamFieldFile, FoamFile
31
- from ._util import CalledProcessError, is_sequence, run_process, run_process_async
31
+ from ._util import is_sequence, run_process, run_process_async
32
32
 
33
33
 
34
34
  class FoamCaseBase(Sequence["FoamCaseBase.TimeDirectory"]):
@@ -325,7 +325,7 @@ class FoamCase(FoamCaseBase):
325
325
  Clean this case.
326
326
 
327
327
  :param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
328
- :param check: If True, raise a RuntimeError if the clean script returns a non-zero exit code.
328
+ :param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
329
329
  """
330
330
  script_path = self._clean_script() if script else None
331
331
 
@@ -349,24 +349,18 @@ class FoamCase(FoamCaseBase):
349
349
  :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.
350
350
  :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.
351
351
  :param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
352
- :param check: If True, raise a RuntimeError if any command returns a non-zero exit code.
352
+ :param check: If True, raise a CalledProcessError if any command returns a non-zero exit code.
353
353
  """
354
354
  if cmd is not None:
355
355
  if parallel:
356
356
  cmd = self._parallel_cmd(cmd)
357
357
 
358
- try:
359
- run_process(
360
- cmd,
361
- check=check,
362
- cwd=self.path,
363
- env=self._env(),
364
- )
365
- except CalledProcessError as e:
366
- raise RuntimeError(
367
- f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
368
- ) from None
369
-
358
+ run_process(
359
+ cmd,
360
+ check=check,
361
+ cwd=self.path,
362
+ env=self._env(),
363
+ )
370
364
  else:
371
365
  script_path = self._run_script(parallel=parallel) if script else None
372
366
 
@@ -485,7 +479,7 @@ class AsyncFoamCase(FoamCaseBase):
485
479
  Clean this case.
486
480
 
487
481
  :param script: If True, use an (All)clean script if it exists. If False, ignore any clean scripts.
488
- :param check: If True, raise a RuntimeError if the clean script returns a non-zero exit code.
482
+ :param check: If True, raise a CalledProcessError if the clean script returns a non-zero exit code.
489
483
  """
490
484
  script_path = self._clean_script() if script else None
491
485
 
@@ -511,7 +505,7 @@ class AsyncFoamCase(FoamCaseBase):
511
505
  :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.
512
506
  :param parallel: If True, run in parallel using MPI. If None, autodetect whether to run in parallel.
513
507
  :param cpus: The number of CPUs to reserve for the run. The run will wait until the requested number of CPUs is available. If None, autodetect the number of CPUs to reserve.
514
- :param check: If True, raise a RuntimeError if a command returns a non-zero exit code.
508
+ :param check: If True, raise a CalledProcessError if a command returns a non-zero exit code.
515
509
  """
516
510
  if cmd is not None:
517
511
  if cpus is None:
@@ -523,19 +517,13 @@ class AsyncFoamCase(FoamCaseBase):
523
517
  if parallel:
524
518
  cmd = self._parallel_cmd(cmd)
525
519
 
526
- try:
527
- async with self._cpus(cpus):
528
- await run_process_async(
529
- cmd,
530
- check=check,
531
- cwd=self.path,
532
- env=self._env(),
533
- )
534
- except CalledProcessError as e:
535
- raise RuntimeError(
536
- f"{e.cmd} failed with return code {e.returncode}\n{e.stderr}"
537
- ) from None
538
-
520
+ async with self._cpus(cpus):
521
+ await run_process_async(
522
+ cmd,
523
+ check=check,
524
+ cwd=self.path,
525
+ env=self._env(),
526
+ )
539
527
  else:
540
528
  script_path = self._run_script(parallel=parallel) if script else None
541
529
 
@@ -1,9 +1,9 @@
1
- from ._base import FoamDictionaryBase
1
+ from ._base import FoamDict
2
2
  from ._fields import FoamFieldFile
3
3
  from ._files import FoamFile
4
4
 
5
5
  __all__ = [
6
6
  "FoamFile",
7
7
  "FoamFieldFile",
8
- "FoamDictionaryBase",
8
+ "FoamDict",
9
9
  ]
@@ -1,7 +1,7 @@
1
1
  import sys
2
2
  from abc import abstractmethod
3
3
  from dataclasses import dataclass
4
- from typing import Dict, NamedTuple, Optional, Union
4
+ from typing import Dict, NamedTuple, Optional, Tuple, Union
5
5
 
6
6
  if sys.version_info >= (3, 9):
7
7
  from collections.abc import Mapping, Sequence
@@ -10,12 +10,11 @@ else:
10
10
 
11
11
  try:
12
12
  import numpy as np
13
- from numpy.typing import NDArray
14
13
  except ModuleNotFoundError:
15
14
  pass
16
15
 
17
16
 
18
- class FoamDictionaryBase:
17
+ class FoamDict:
19
18
  class DimensionSet(NamedTuple):
20
19
  mass: Union[int, float] = 0
21
20
  length: Union[int, float] = 0
@@ -31,34 +30,44 @@ class FoamDictionaryBase:
31
30
  @dataclass
32
31
  class Dimensioned:
33
32
  value: Union[int, float, Sequence[Union[int, float]]] = 0
34
- dimensions: Union[
35
- "FoamDictionaryBase.DimensionSet", Sequence[Union[int, float]]
36
- ] = ()
33
+ dimensions: Union["FoamDict.DimensionSet", Sequence[Union[int, float]]] = ()
37
34
  name: Optional[str] = None
38
35
 
39
36
  def __post_init__(self) -> None:
40
- if not isinstance(self.dimensions, FoamDictionaryBase.DimensionSet):
41
- self.dimensions = FoamDictionaryBase.DimensionSet(*self.dimensions)
37
+ if not isinstance(self.dimensions, FoamDict.DimensionSet):
38
+ self.dimensions = FoamDict.DimensionSet(*self.dimensions)
42
39
 
43
- Value = Union[
40
+ Data = Union[
44
41
  str,
45
42
  int,
46
43
  float,
47
44
  bool,
48
45
  Dimensioned,
49
46
  DimensionSet,
50
- Sequence["Value"],
51
- Mapping[str, "Value"],
47
+ Sequence["Data"],
48
+ Mapping[str, "Data"],
52
49
  ]
53
50
  """
54
51
  A value that can be stored in an OpenFOAM dictionary.
55
52
  """
56
53
 
57
- _Dict = Dict[str, Union["Value", "_Dict"]]
54
+ _Dict = Dict[str, Union["Data", "_Dict"]]
58
55
 
59
56
  @abstractmethod
60
57
  def as_dict(self) -> _Dict:
61
58
  """Return a nested dict representation of the dictionary."""
62
59
  raise NotImplementedError
63
60
 
64
- _SetValue = Union[Value, "NDArray[np.generic]"]
61
+ _SetData = Union[
62
+ str,
63
+ int,
64
+ float,
65
+ bool,
66
+ Dimensioned,
67
+ DimensionSet,
68
+ Sequence["_SetData"],
69
+ Mapping[str, "_SetData"],
70
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
71
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
72
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
73
+ ]
@@ -10,7 +10,6 @@ from ._files import FoamFile
10
10
 
11
11
  try:
12
12
  import numpy as np
13
- from numpy.typing import NDArray
14
13
  except ModuleNotFoundError:
15
14
  pass
16
15
 
@@ -18,26 +17,26 @@ except ModuleNotFoundError:
18
17
  class FoamFieldFile(FoamFile):
19
18
  """An OpenFOAM dictionary file representing a field as a mutable mapping."""
20
19
 
21
- class BoundariesDictionary(FoamFile.Dictionary):
22
- def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundaryDictionary":
20
+ class BoundariesSubDict(FoamFile.SubDict):
21
+ def __getitem__(self, keyword: str) -> "FoamFieldFile.BoundarySubDict":
23
22
  value = super().__getitem__(keyword)
24
- if not isinstance(value, FoamFieldFile.BoundaryDictionary):
25
- assert not isinstance(value, FoamFile.Dictionary)
23
+ if not isinstance(value, FoamFieldFile.BoundarySubDict):
24
+ assert not isinstance(value, FoamFile.SubDict)
26
25
  raise TypeError(f"boundary {keyword} is not a dictionary")
27
26
  return value
28
27
 
29
- class BoundaryDictionary(FoamFile.Dictionary):
28
+ class BoundarySubDict(FoamFile.SubDict):
30
29
  """An OpenFOAM dictionary representing a boundary condition as a mutable mapping."""
31
30
 
32
31
  def __setitem__(
33
32
  self,
34
- key: str,
35
- value: FoamFile._SetValue,
33
+ keyword: str,
34
+ data: FoamFile._SetData,
36
35
  ) -> None:
37
- if key == "value":
38
- self._setitem(key, value, assume_field=True)
36
+ if keyword == "value":
37
+ self._setitem(keyword, data, assume_field=True)
39
38
  else:
40
- self._setitem(key, value)
39
+ self._setitem(keyword, data)
41
40
 
42
41
  @property
43
42
  def type(self) -> str:
@@ -48,8 +47,8 @@ class FoamFieldFile(FoamFile):
48
47
  return ret
49
48
 
50
49
  @type.setter
51
- def type(self, value: str) -> None:
52
- self["type"] = value
50
+ def type(self, data: str) -> None:
51
+ self["type"] = data
53
52
 
54
53
  @property
55
54
  def value(
@@ -58,7 +57,9 @@ class FoamFieldFile(FoamFile):
58
57
  int,
59
58
  float,
60
59
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
61
- "NDArray[np.generic]",
60
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
61
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
62
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
62
63
  ]:
63
64
  """Alias of `self["value"]`."""
64
65
  ret = self["value"]
@@ -73,7 +74,9 @@ class FoamFieldFile(FoamFile):
73
74
  int,
74
75
  float,
75
76
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
76
- "NDArray[np.generic]",
77
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
78
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
79
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
77
80
  ],
78
81
  ) -> None:
79
82
  self["value"] = value
@@ -84,16 +87,16 @@ class FoamFieldFile(FoamFile):
84
87
 
85
88
  def __getitem__(
86
89
  self, keywords: Union[str, Tuple[str, ...]]
87
- ) -> Union[FoamFile.Value, FoamFile.Dictionary]:
90
+ ) -> Union[FoamFile.Data, FoamFile.SubDict]:
88
91
  if not isinstance(keywords, tuple):
89
92
  keywords = (keywords,)
90
93
 
91
94
  ret = super().__getitem__(keywords)
92
- if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.Dictionary):
95
+ if keywords[0] == "boundaryField" and isinstance(ret, FoamFile.SubDict):
93
96
  if len(keywords) == 1:
94
- ret = FoamFieldFile.BoundariesDictionary(self, keywords)
97
+ ret = FoamFieldFile.BoundariesSubDict(self, keywords)
95
98
  elif len(keywords) == 2:
96
- ret = FoamFieldFile.BoundaryDictionary(self, keywords)
99
+ ret = FoamFieldFile.BoundarySubDict(self, keywords)
97
100
  return ret
98
101
 
99
102
  def __setitem__(self, keywords: Union[str, Tuple[str, ...]], value: Any) -> None:
@@ -128,7 +131,9 @@ class FoamFieldFile(FoamFile):
128
131
  int,
129
132
  float,
130
133
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
131
- "NDArray[np.generic]",
134
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
135
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
136
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
132
137
  ]:
133
138
  """Alias of `self["internalField"]`."""
134
139
  ret = self["internalField"]
@@ -143,16 +148,18 @@ class FoamFieldFile(FoamFile):
143
148
  int,
144
149
  float,
145
150
  Sequence[Union[int, float, Sequence[Union[int, float]]]],
146
- "NDArray[np.generic]",
151
+ "np.ndarray[Tuple[()], np.dtype[np.generic]]",
152
+ "np.ndarray[Tuple[int], np.dtype[np.generic]]",
153
+ "np.ndarray[Tuple[int, int], np.dtype[np.generic]]",
147
154
  ],
148
155
  ) -> None:
149
156
  self["internalField"] = value
150
157
 
151
158
  @property
152
- def boundary_field(self) -> "FoamFieldFile.BoundariesDictionary":
159
+ def boundary_field(self) -> "FoamFieldFile.BoundariesSubDict":
153
160
  """Alias of `self["boundaryField"]`."""
154
161
  ret = self["boundaryField"]
155
- if not isinstance(ret, FoamFieldFile.BoundariesDictionary):
156
- assert not isinstance(ret, FoamFile.Dictionary)
162
+ if not isinstance(ret, FoamFieldFile.BoundariesSubDict):
163
+ assert not isinstance(ret, FoamFile.SubDict)
157
164
  raise TypeError("boundaryField is not a dictionary")
158
165
  return ret
@@ -10,15 +10,15 @@ if sys.version_info >= (3, 9):
10
10
  else:
11
11
  from typing import Iterator, Mapping, MutableMapping
12
12
 
13
- from ._base import FoamDictionaryBase
13
+ from ._base import FoamDict
14
14
  from ._io import FoamFileIO
15
- from ._serialization import serialize_entry
15
+ from ._serialization import serialize
16
16
 
17
17
 
18
18
  class FoamFile(
19
- FoamDictionaryBase,
19
+ FoamDict,
20
20
  MutableMapping[
21
- Union[str, Tuple[str, ...]], Union["FoamFile.Value", "FoamFile.Dictionary"]
21
+ Union[str, Tuple[str, ...]], Union["FoamFile.Data", "FoamFile.SubDict"]
22
22
  ],
23
23
  FoamFileIO,
24
24
  ):
@@ -30,9 +30,9 @@ class FoamFile(
30
30
  Use as a context manager to make multiple changes to the file while saving all changes only once at the end.
31
31
  """
32
32
 
33
- class Dictionary(
34
- FoamDictionaryBase,
35
- MutableMapping[str, Union["FoamFile.Value", "FoamFile.Dictionary"]],
33
+ class SubDict(
34
+ FoamDict,
35
+ MutableMapping[str, Union["FoamFile.Data", "FoamFile.SubDict"]],
36
36
  ):
37
37
  """An OpenFOAM dictionary within a file as a mutable mapping."""
38
38
 
@@ -42,25 +42,25 @@ class FoamFile(
42
42
 
43
43
  def __getitem__(
44
44
  self, keyword: str
45
- ) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
45
+ ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
46
46
  return self._file[(*self._keywords, keyword)]
47
47
 
48
48
  def _setitem(
49
49
  self,
50
50
  keyword: str,
51
- value: Any,
51
+ data: Any,
52
52
  *,
53
53
  assume_field: bool = False,
54
54
  assume_dimensions: bool = False,
55
55
  ) -> None:
56
56
  self._file._setitem(
57
57
  (*self._keywords, keyword),
58
- value,
58
+ data,
59
59
  assume_field=assume_field,
60
60
  assume_dimensions=assume_dimensions,
61
61
  )
62
62
 
63
- def __setitem__(self, keyword: str, value: "FoamFile._SetValue") -> None:
63
+ def __setitem__(self, keyword: str, value: "FoamFile._SetData") -> None:
64
64
  self._setitem(keyword, value)
65
65
 
66
66
  def __delitem__(self, keyword: str) -> None:
@@ -86,7 +86,7 @@ class FoamFile(
86
86
  def __repr__(self) -> str:
87
87
  return f"{type(self).__qualname__}({self._file}, {self._keywords})"
88
88
 
89
- def as_dict(self) -> FoamDictionaryBase._Dict:
89
+ def as_dict(self) -> FoamDict._Dict:
90
90
  """Return a nested dict representation of the dictionary."""
91
91
  ret = self._file.as_dict()
92
92
 
@@ -100,7 +100,7 @@ class FoamFile(
100
100
 
101
101
  def __getitem__(
102
102
  self, keywords: Union[str, Tuple[str, ...]]
103
- ) -> Union["FoamFile.Value", "FoamFile.Dictionary"]:
103
+ ) -> Union["FoamFile.Data", "FoamFile.SubDict"]:
104
104
  if not isinstance(keywords, tuple):
105
105
  keywords = (keywords,)
106
106
 
@@ -109,14 +109,14 @@ class FoamFile(
109
109
  value = parsed[keywords]
110
110
 
111
111
  if value is ...:
112
- return FoamFile.Dictionary(self, keywords)
112
+ return FoamFile.SubDict(self, keywords)
113
113
  else:
114
114
  return value # type: ignore [return-value]
115
115
 
116
116
  def _setitem(
117
117
  self,
118
118
  keywords: Union[str, Tuple[str, ...]],
119
- value: "FoamFile._SetValue",
119
+ data: "FoamFile._SetData",
120
120
  *,
121
121
  assume_field: bool = False,
122
122
  assume_dimensions: bool = False,
@@ -126,32 +126,32 @@ class FoamFile(
126
126
 
127
127
  contents, parsed = self._read()
128
128
 
129
- if isinstance(value, Mapping):
129
+ if isinstance(data, Mapping):
130
130
  with self:
131
- if isinstance(value, FoamDictionaryBase):
132
- value = value.as_dict()
131
+ if isinstance(data, FoamDict):
132
+ data = data.as_dict()
133
133
 
134
134
  start, end = parsed.entry_location(keywords, missing_ok=True)
135
135
 
136
136
  self._write(
137
- f"{contents[:start]}\n{serialize_entry(keywords[-1], {})}\n{contents[end:]}"
137
+ f"{contents[:start]}\n{serialize({keywords[-1]: {}})}\n{contents[end:]}"
138
138
  )
139
139
 
140
- for k, v in value.items():
140
+ for k, v in data.items():
141
141
  self[(*keywords, k)] = v
142
142
  else:
143
143
  start, end = parsed.entry_location(keywords, missing_ok=True)
144
144
 
145
145
  self._write(
146
- f"{contents[:start]}\n{serialize_entry(keywords[-1], value, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
146
+ f"{contents[:start]}\n{serialize({keywords[-1]: data}, assume_field=assume_field, assume_dimensions=assume_dimensions)}\n{contents[end:]}"
147
147
  )
148
148
 
149
149
  def __setitem__(
150
150
  self,
151
151
  keywords: Union[str, Tuple[str, ...]],
152
- value: "FoamFile._SetValue",
152
+ data: "FoamFile._SetData",
153
153
  ) -> None:
154
- self._setitem(keywords, value)
154
+ self._setitem(keywords, data)
155
155
 
156
156
  def __delitem__(self, keywords: Union[str, Tuple[str, ...]]) -> None:
157
157
  if not isinstance(keywords, tuple):
@@ -197,7 +197,7 @@ class FoamFile(
197
197
  def __repr__(self) -> str:
198
198
  return f"{type(self).__name__}({self.path})"
199
199
 
200
- def as_dict(self) -> FoamDictionaryBase._Dict:
200
+ def as_dict(self) -> FoamDict._Dict:
201
201
  """Return a nested dict representation of the file."""
202
202
  _, parsed = self._read()
203
203
  return parsed.as_dict()
@@ -31,10 +31,10 @@ from pyparsing import (
31
31
  printables,
32
32
  )
33
33
 
34
- from ._base import FoamDictionaryBase
34
+ from ._base import FoamDict
35
35
 
36
36
 
37
- def _list_of(elem: ParserElement) -> ParserElement:
37
+ def _list_of(entry: ParserElement) -> ParserElement:
38
38
  return Opt(
39
39
  Literal("List") + Literal("<") + common.identifier + Literal(">")
40
40
  ).suppress() + (
@@ -42,36 +42,36 @@ def _list_of(elem: ParserElement) -> ParserElement:
42
42
  Opt(common.integer).suppress()
43
43
  + (
44
44
  Literal("(").suppress()
45
- + Group((elem)[...], aslist=True)
45
+ + Group((entry)[...], aslist=True)
46
46
  + Literal(")").suppress()
47
47
  )
48
48
  )
49
49
  | (
50
- common.integer + Literal("{").suppress() + elem + Literal("}").suppress()
50
+ common.integer + Literal("{").suppress() + entry + Literal("}").suppress()
51
51
  ).set_parse_action(lambda tks: [[tks[1]] * tks[0]])
52
52
  )
53
53
 
54
54
 
55
55
  def _dictionary_of(
56
56
  keyword: ParserElement,
57
- value: ParserElement,
57
+ data_entries: ParserElement,
58
58
  *,
59
59
  len: Union[int, EllipsisType] = ...,
60
60
  located: bool = False,
61
61
  ) -> ParserElement:
62
62
  subdict = Forward()
63
63
 
64
- entry = keyword + (
64
+ keyword_entry = keyword + (
65
65
  (Literal("{").suppress() + subdict + Literal("}").suppress())
66
- | (value + Literal(";").suppress())
66
+ | (data_entries + Literal(";").suppress())
67
67
  )
68
68
 
69
69
  if located:
70
- entry = Located(entry)
70
+ keyword_entry = Located(keyword_entry)
71
71
 
72
- subdict <<= Dict(Group(entry)[...], asdict=not located)
72
+ subdict <<= Dict(Group(keyword_entry)[...], asdict=not located)
73
73
 
74
- return Dict(Group(entry)[len], asdict=not located)
74
+ return Dict(Group(keyword_entry)[len], asdict=not located)
75
75
 
76
76
 
77
77
  _SWITCH = (
@@ -81,41 +81,42 @@ _SWITCH = (
81
81
  ).set_parse_action(lambda: False)
82
82
  _DIMENSIONS = (
83
83
  Literal("[").suppress() + common.number * 7 + Literal("]").suppress()
84
- ).set_parse_action(lambda tks: FoamDictionaryBase.DimensionSet(*tks))
85
-
84
+ ).set_parse_action(lambda tks: FoamDict.DimensionSet(*tks))
86
85
  _TENSOR = _list_of(common.number) | common.number
87
86
  _IDENTIFIER = Word(identbodychars + "$", printables.replace(";", ""))
88
87
  _DIMENSIONED = (Opt(_IDENTIFIER) + _DIMENSIONS + _TENSOR).set_parse_action(
89
- lambda tks: FoamDictionaryBase.Dimensioned(*reversed(tks.as_list()))
88
+ lambda tks: FoamDict.Dimensioned(*reversed(tks.as_list()))
90
89
  )
91
90
  _FIELD = (Keyword("uniform").suppress() + _TENSOR) | (
92
91
  Keyword("nonuniform").suppress() + _list_of(_TENSOR)
93
92
  )
94
93
  _TOKEN = QuotedString('"', unquote_results=False) | _IDENTIFIER
95
- _ITEM = Forward()
96
- _ENTRY = _dictionary_of(_IDENTIFIER, _ITEM, len=1)
97
- _LIST = _list_of(_ENTRY | _ITEM)
98
- _ITEM <<= _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
99
-
100
- _TOKENS = (
101
- QuotedString('"', unquote_results=False) | Word(printables.replace(";", ""))
102
- )[2, ...].set_parse_action(lambda tks: " ".join(tks))
94
+ _DATA = Forward()
95
+ _KEYWORD_ENTRY = _dictionary_of(_TOKEN, _DATA, len=1)
96
+ _DATA_ENTRY = Forward()
97
+ _LIST_ENTRY = _KEYWORD_ENTRY | _DATA_ENTRY
98
+ _LIST = _list_of(_LIST_ENTRY)
99
+ _DATA_ENTRY <<= (
100
+ _FIELD | _LIST | _DIMENSIONED | _DIMENSIONS | common.number | _SWITCH | _TOKEN
101
+ )
103
102
 
104
- _VALUE = _ITEM ^ _TOKENS
103
+ _DATA <<= _DATA_ENTRY[1, ...].set_parse_action(
104
+ lambda tks: tuple(tks) if len(tks) > 1 else [tks[0]]
105
+ )
105
106
 
106
107
  _FILE = (
107
- _dictionary_of(_TOKEN, Opt(_VALUE, default=""), located=True)
108
+ _dictionary_of(_TOKEN, Opt(_DATA, default=""), located=True)
108
109
  .ignore(c_style_comment)
109
110
  .ignore(cpp_style_comment)
110
111
  .ignore(Literal("#include") + ... + LineEnd()) # type: ignore [no-untyped-call]
111
112
  )
112
113
 
113
114
 
114
- class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisType]]):
115
+ class Parsed(Mapping[Tuple[str, ...], Union[FoamDict.Data, EllipsisType]]):
115
116
  def __init__(self, contents: str) -> None:
116
117
  self._parsed: MutableMapping[
117
118
  Tuple[str, ...],
118
- Tuple[int, Union[FoamDictionaryBase.Value, EllipsisType], int],
119
+ Tuple[int, Union[FoamDict.Data, EllipsisType], int],
119
120
  ] = {}
120
121
  for parse_result in _FILE.parse_string(contents, parse_all=True):
121
122
  self._parsed.update(self._flatten_result(parse_result))
@@ -123,12 +124,10 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
123
124
  @staticmethod
124
125
  def _flatten_result(
125
126
  parse_result: ParseResults, *, _keywords: Tuple[str, ...] = ()
126
- ) -> Mapping[
127
- Tuple[str, ...], Tuple[int, Union[FoamDictionaryBase.Value, EllipsisType], int]
128
- ]:
127
+ ) -> Mapping[Tuple[str, ...], Tuple[int, Union[FoamDict.Data, EllipsisType], int]]:
129
128
  ret: MutableMapping[
130
129
  Tuple[str, ...],
131
- Tuple[int, Union[FoamDictionaryBase.Value, EllipsisType], int],
130
+ Tuple[int, Union[FoamDict.Data, EllipsisType], int],
132
131
  ] = {}
133
132
  start = parse_result.locn_start
134
133
  assert isinstance(start, int)
@@ -136,21 +135,21 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
136
135
  assert isinstance(item, Sequence)
137
136
  end = parse_result.locn_end
138
137
  assert isinstance(end, int)
139
- key, *values = item
140
- assert isinstance(key, str)
141
- ret[(*_keywords, key)] = (start, ..., end)
142
- for value in values:
143
- if isinstance(value, ParseResults):
144
- ret.update(Parsed._flatten_result(value, _keywords=(*_keywords, key)))
138
+ keyword, *data = item
139
+ assert isinstance(keyword, str)
140
+ ret[(*_keywords, keyword)] = (start, ..., end)
141
+ for d in data:
142
+ if isinstance(d, ParseResults):
143
+ ret.update(Parsed._flatten_result(d, _keywords=(*_keywords, keyword)))
145
144
  else:
146
- ret[(*_keywords, key)] = (start, value, end)
145
+ ret[(*_keywords, keyword)] = (start, d, end)
147
146
  return ret
148
147
 
149
148
  def __getitem__(
150
149
  self, keywords: Tuple[str, ...]
151
- ) -> Union[FoamDictionaryBase.Value, EllipsisType]:
152
- _, value, _ = self._parsed[keywords]
153
- return value
150
+ ) -> Union[FoamDict.Data, EllipsisType]:
151
+ _, data, _ = self._parsed[keywords]
152
+ return data
154
153
 
155
154
  def __contains__(self, keywords: object) -> bool:
156
155
  return keywords in self._parsed
@@ -180,9 +179,9 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
180
179
 
181
180
  return start, end
182
181
 
183
- def as_dict(self) -> FoamDictionaryBase._Dict:
184
- ret: FoamDictionaryBase._Dict = {}
185
- for keywords, (_, value, _) in self._parsed.items():
182
+ def as_dict(self) -> FoamDict._Dict:
183
+ ret: FoamDict._Dict = {}
184
+ for keywords, (_, data, _) in self._parsed.items():
186
185
  r = ret
187
186
  for k in keywords[:-1]:
188
187
  assert isinstance(r, dict)
@@ -191,6 +190,6 @@ class Parsed(Mapping[Tuple[str, ...], Union[FoamDictionaryBase.Value, EllipsisTy
191
190
  r = v
192
191
 
193
192
  assert isinstance(r, dict)
194
- r[keywords[-1]] = {} if value is ... else value # type: ignore [assignment]
193
+ r[keywords[-1]] = {} if data is ... else data # type: ignore [assignment]
195
194
 
196
195
  return ret
@@ -0,0 +1,83 @@
1
+ import sys
2
+
3
+ if sys.version_info >= (3, 9):
4
+ from collections.abc import Mapping
5
+ else:
6
+ from typing import Mapping
7
+
8
+ from .._util import is_sequence
9
+ from ._base import FoamDict
10
+
11
+
12
+ def serialize(
13
+ data: FoamDict._SetData,
14
+ *,
15
+ assume_field: bool = False,
16
+ assume_dimensions: bool = False,
17
+ assume_data_entries: bool = False,
18
+ ) -> str:
19
+ if isinstance(data, Mapping):
20
+ entries = []
21
+ for k, v in data.items():
22
+ s = serialize(
23
+ v,
24
+ assume_field=assume_field,
25
+ assume_dimensions=assume_dimensions,
26
+ assume_data_entries=True,
27
+ )
28
+ if isinstance(v, Mapping):
29
+ entries.append(f"{k}\n{{\n{s}\n}}")
30
+ elif s:
31
+ entries.append(f"{k} {s};")
32
+ else:
33
+ entries.append(f"{k};")
34
+ return "\n".join(entries)
35
+
36
+ elif isinstance(data, FoamDict.DimensionSet) or (
37
+ assume_dimensions and is_sequence(data) and len(data) == 7
38
+ ):
39
+ return f"[{' '.join(str(v) for v in data)}]"
40
+
41
+ elif assume_field and isinstance(data, (int, float)):
42
+ return f"uniform {data}"
43
+
44
+ elif assume_field and is_sequence(data):
45
+ if isinstance(data[0], (int, float)) and len(data) in (3, 6, 9):
46
+ return f"uniform {serialize(data)}"
47
+ elif isinstance(data[0], (int, float)):
48
+ return f"nonuniform List<scalar> {len(data)}{serialize(data)}"
49
+ elif len(data[0]) == 3:
50
+ return f"nonuniform List<vector> {len(data)}{serialize(data)}"
51
+ elif len(data[0]) == 6:
52
+ return f"nonuniform List<symmTensor> {len(data)}{serialize(data)}"
53
+ elif len(data[0]) == 9:
54
+ return f"nonuniform List<tensor> {len(data)}{serialize(data)}"
55
+ else:
56
+ return serialize(
57
+ data,
58
+ assume_dimensions=assume_dimensions,
59
+ assume_data_entries=assume_data_entries,
60
+ )
61
+
62
+ elif assume_data_entries and isinstance(data, tuple):
63
+ return " ".join(
64
+ serialize(v, assume_field=assume_field, assume_dimensions=assume_dimensions)
65
+ for v in data
66
+ )
67
+
68
+ elif isinstance(data, FoamDict.Dimensioned):
69
+ if data.name is not None:
70
+ return f"{data.name} {serialize(data.dimensions, assume_dimensions=True)} {serialize(data.value)}"
71
+ else:
72
+ return f"{serialize(data.dimensions, assume_dimensions=True)} {serialize(data.value)}"
73
+
74
+ elif is_sequence(data):
75
+ return f"({' '.join(serialize(v) for v in data)})"
76
+
77
+ elif data is True:
78
+ return "yes"
79
+ elif data is False:
80
+ return "no"
81
+
82
+ else:
83
+ return str(data)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: foamlib
3
- Version: 0.2.10
3
+ Version: 0.3.1
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
@@ -1,129 +0,0 @@
1
- import sys
2
- from contextlib import suppress
3
-
4
- if sys.version_info >= (3, 9):
5
- from collections.abc import Mapping
6
- else:
7
- from typing import Mapping
8
-
9
- from .._util import is_sequence
10
- from ._base import FoamDictionaryBase
11
-
12
-
13
- def _serialize_switch(value: FoamDictionaryBase._SetValue) -> str:
14
- if value is True:
15
- return "yes"
16
- elif value is False:
17
- return "no"
18
- else:
19
- raise TypeError(f"Not a bool: {type(value)}")
20
-
21
-
22
- def _serialize_list(
23
- value: FoamDictionaryBase._SetValue,
24
- ) -> str:
25
- if is_sequence(value):
26
- return f"({' '.join(_serialize_value(v) for v in value)})"
27
- else:
28
- raise TypeError(f"Not a valid sequence: {type(value)}")
29
-
30
-
31
- def _serialize_field(
32
- value: FoamDictionaryBase._SetValue,
33
- ) -> str:
34
- if is_sequence(value):
35
- try:
36
- s = _serialize_list(value)
37
- except TypeError:
38
- raise TypeError(f"Not a valid field: {type(value)}") from None
39
- else:
40
- if not is_sequence(value[0]) and len(value) < 10:
41
- return f"uniform {s}"
42
- else:
43
- if not is_sequence(value[0]):
44
- kind = "scalar"
45
- elif len(value[0]) == 3:
46
- kind = "vector"
47
- elif len(value[0]) == 6:
48
- kind = "symmTensor"
49
- elif len(value[0]) == 9:
50
- kind = "tensor"
51
- else:
52
- raise TypeError(
53
- f"Unsupported sequence length for field: {len(value[0])}"
54
- )
55
- return f"nonuniform List<{kind}> {len(value)}{s}"
56
- else:
57
- return f"uniform {value}"
58
-
59
-
60
- def _serialize_dimensions(
61
- value: FoamDictionaryBase._SetValue,
62
- ) -> str:
63
- if is_sequence(value) and len(value) == 7:
64
- return f"[{' '.join(str(v) for v in value)}]"
65
- else:
66
- raise TypeError(f"Not a valid dimension set: {type(value)}")
67
-
68
-
69
- def _serialize_dimensioned(
70
- value: FoamDictionaryBase._SetValue,
71
- ) -> str:
72
- if isinstance(value, FoamDictionaryBase.Dimensioned):
73
- if value.name is not None:
74
- return f"{value.name} {_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
75
- else:
76
- return f"{_serialize_dimensions(value.dimensions)} {_serialize_value(value.value)}"
77
- else:
78
- raise TypeError(f"Not a valid dimensioned value: {type(value)}")
79
-
80
-
81
- def _serialize_value(
82
- value: FoamDictionaryBase._SetValue,
83
- *,
84
- assume_field: bool = False,
85
- assume_dimensions: bool = False,
86
- ) -> str:
87
- if isinstance(value, FoamDictionaryBase.DimensionSet) or assume_dimensions:
88
- with suppress(TypeError):
89
- return _serialize_dimensions(value)
90
-
91
- if assume_field:
92
- with suppress(TypeError):
93
- return _serialize_field(value)
94
-
95
- with suppress(TypeError):
96
- return _serialize_dimensioned(value)
97
-
98
- with suppress(TypeError):
99
- return _serialize_list(value)
100
-
101
- with suppress(TypeError):
102
- return _serialize_switch(value)
103
-
104
- with suppress(TypeError):
105
- return _serialize_dictionary(value)
106
-
107
- return str(value)
108
-
109
-
110
- def _serialize_dictionary(
111
- value: FoamDictionaryBase._SetValue,
112
- ) -> str:
113
- if isinstance(value, Mapping):
114
- return "\n".join(serialize_entry(k, v) for k, v in value.items())
115
- else:
116
- raise TypeError(f"Not a valid dictionary: {type(value)}")
117
-
118
-
119
- def serialize_entry(
120
- keyword: str,
121
- value: FoamDictionaryBase._SetValue,
122
- *,
123
- assume_field: bool = False,
124
- assume_dimensions: bool = False,
125
- ) -> str:
126
- try:
127
- return f"{keyword}\n{{\n{_serialize_dictionary(value)}\n}}"
128
- except TypeError:
129
- return f"{keyword} {_serialize_value(value, assume_field=assume_field, assume_dimensions=assume_dimensions)};"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes