cffview 0.3.2__tar.gz → 0.3.4__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.
- {cffview-0.3.2/src/cffview.egg-info → cffview-0.3.4}/PKG-INFO +4 -8
- {cffview-0.3.2 → cffview-0.3.4}/README.md +3 -7
- {cffview-0.3.2 → cffview-0.3.4}/pyproject.toml +1 -1
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview/boundary.py +33 -25
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview/main.py +134 -66
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview/utils.py +7 -0
- {cffview-0.3.2 → cffview-0.3.4/src/cffview.egg-info}/PKG-INFO +4 -8
- {cffview-0.3.2 → cffview-0.3.4}/LICENSE +0 -0
- {cffview-0.3.2 → cffview-0.3.4}/setup.cfg +0 -0
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview.egg-info/SOURCES.txt +0 -0
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview.egg-info/dependency_links.txt +0 -0
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview.egg-info/entry_points.txt +0 -0
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview.egg-info/requires.txt +0 -0
- {cffview-0.3.2 → cffview-0.3.4}/src/cffview.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cffview
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening Fluent
|
|
5
5
|
Author-email: preamer <1524477126@qq.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -35,8 +35,6 @@ A command-line tool for inspecting Ansys Fluent `.cas.h5` / `.msh.h5` files **wi
|
|
|
35
35
|
- Read solver settings, materials, boundary conditions, discretisation schemes, and more directly from the HDF5 file.
|
|
36
36
|
- Visualise the mesh with [PyVista](https://pyvista.org).
|
|
37
37
|
|
|
38
|
-
---
|
|
39
|
-
|
|
40
38
|
## Installation
|
|
41
39
|
|
|
42
40
|
### PyPI
|
|
@@ -53,8 +51,6 @@ cd cffview
|
|
|
53
51
|
pip install .
|
|
54
52
|
```
|
|
55
53
|
|
|
56
|
-
---
|
|
57
|
-
|
|
58
54
|
## Usage
|
|
59
55
|
|
|
60
56
|
> [!IMPORTANT]
|
|
@@ -70,7 +66,7 @@ cffview <file> [options]
|
|
|
70
66
|
|---|---|
|
|
71
67
|
| `--version` | Print the Fluent version of file |
|
|
72
68
|
| `--extract` | Dump raw Scheme settings to `general.scm` and `boundary.scm` |
|
|
73
|
-
| `--showmesh` | Visualise the mesh interactively with PyVista |
|
|
69
|
+
| `--mesh`, `--showmesh` | Visualise the mesh interactively with PyVista |
|
|
74
70
|
| `--solver` | Solver type, time, dimension, precision, turbulence model, energy, radiation, gravity |
|
|
75
71
|
| `--mat`, `--materials` | Material properties |
|
|
76
72
|
| `--bd`, `--boundary` | Boundary condition settings |
|
|
@@ -83,7 +79,9 @@ cffview <file> [options]
|
|
|
83
79
|
| `--iter` | Iteration / time-step settings |
|
|
84
80
|
| `--contours` | Graphics contours settings |
|
|
85
81
|
| `--vectors` | Graphics vectors settings |
|
|
82
|
+
| `--xy`, `--xy-plot` | Graphics xy-plot settings |
|
|
86
83
|
| `--save` | Save the output to `<file>.json` |
|
|
84
|
+
| `--plot` | Plot the outfile with matplotlib |
|
|
87
85
|
|
|
88
86
|
Multiple flags can be combined freely. Case settings flags (`--solver`, `--mat`, etc.) apply to `.cas.h5` files only.
|
|
89
87
|
|
|
@@ -111,8 +109,6 @@ cffview case.cas.h5 --extract
|
|
|
111
109
|
|
|
112
110
|
[demo.webm](https://github.com/user-attachments/assets/9047a354-b5cb-475e-b030-97609e2a1274)
|
|
113
111
|
|
|
114
|
-
---
|
|
115
|
-
|
|
116
112
|
## License
|
|
117
113
|
|
|
118
114
|
[BSD-3-Clause](LICENSE)
|
|
@@ -11,8 +11,6 @@ A command-line tool for inspecting Ansys Fluent `.cas.h5` / `.msh.h5` files **wi
|
|
|
11
11
|
- Read solver settings, materials, boundary conditions, discretisation schemes, and more directly from the HDF5 file.
|
|
12
12
|
- Visualise the mesh with [PyVista](https://pyvista.org).
|
|
13
13
|
|
|
14
|
-
---
|
|
15
|
-
|
|
16
14
|
## Installation
|
|
17
15
|
|
|
18
16
|
### PyPI
|
|
@@ -29,8 +27,6 @@ cd cffview
|
|
|
29
27
|
pip install .
|
|
30
28
|
```
|
|
31
29
|
|
|
32
|
-
---
|
|
33
|
-
|
|
34
30
|
## Usage
|
|
35
31
|
|
|
36
32
|
> [!IMPORTANT]
|
|
@@ -46,7 +42,7 @@ cffview <file> [options]
|
|
|
46
42
|
|---|---|
|
|
47
43
|
| `--version` | Print the Fluent version of file |
|
|
48
44
|
| `--extract` | Dump raw Scheme settings to `general.scm` and `boundary.scm` |
|
|
49
|
-
| `--showmesh` | Visualise the mesh interactively with PyVista |
|
|
45
|
+
| `--mesh`, `--showmesh` | Visualise the mesh interactively with PyVista |
|
|
50
46
|
| `--solver` | Solver type, time, dimension, precision, turbulence model, energy, radiation, gravity |
|
|
51
47
|
| `--mat`, `--materials` | Material properties |
|
|
52
48
|
| `--bd`, `--boundary` | Boundary condition settings |
|
|
@@ -59,7 +55,9 @@ cffview <file> [options]
|
|
|
59
55
|
| `--iter` | Iteration / time-step settings |
|
|
60
56
|
| `--contours` | Graphics contours settings |
|
|
61
57
|
| `--vectors` | Graphics vectors settings |
|
|
58
|
+
| `--xy`, `--xy-plot` | Graphics xy-plot settings |
|
|
62
59
|
| `--save` | Save the output to `<file>.json` |
|
|
60
|
+
| `--plot` | Plot the outfile with matplotlib |
|
|
63
61
|
|
|
64
62
|
Multiple flags can be combined freely. Case settings flags (`--solver`, `--mat`, etc.) apply to `.cas.h5` files only.
|
|
65
63
|
|
|
@@ -87,8 +85,6 @@ cffview case.cas.h5 --extract
|
|
|
87
85
|
|
|
88
86
|
[demo.webm](https://github.com/user-attachments/assets/9047a354-b5cb-475e-b030-97609e2a1274)
|
|
89
87
|
|
|
90
|
-
---
|
|
91
|
-
|
|
92
88
|
## License
|
|
93
89
|
|
|
94
90
|
[BSD-3-Clause](LICENSE)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "cffview"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.4"
|
|
8
8
|
description = "A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening Fluent"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class BoundaryConsts:
|
|
@@ -79,7 +79,7 @@ class Fluid:
|
|
|
79
79
|
id_: str
|
|
80
80
|
material: str = ''
|
|
81
81
|
sources: str = ''
|
|
82
|
-
|
|
82
|
+
source_terms: dict[str, dict[str, str]] = field(default_factory=dict)
|
|
83
83
|
fixed: str = ''
|
|
84
84
|
mrf_motion: str = ''
|
|
85
85
|
mgrid_motion: str = ''
|
|
@@ -92,7 +92,7 @@ class Fluid:
|
|
|
92
92
|
data = self.__dict__.copy()
|
|
93
93
|
|
|
94
94
|
if data['sources'] == '#f':
|
|
95
|
-
data.pop('
|
|
95
|
+
data.pop('source_terms', None)
|
|
96
96
|
|
|
97
97
|
return data
|
|
98
98
|
|
|
@@ -104,7 +104,7 @@ class Solid:
|
|
|
104
104
|
id_: str
|
|
105
105
|
material: str = ''
|
|
106
106
|
sources: str = ''
|
|
107
|
-
|
|
107
|
+
source_terms: dict[str, dict[str, str]] = field(default_factory=dict)
|
|
108
108
|
fixed: str = ''
|
|
109
109
|
solid_motion: str = ''
|
|
110
110
|
|
|
@@ -112,7 +112,7 @@ class Solid:
|
|
|
112
112
|
data = self.__dict__.copy()
|
|
113
113
|
|
|
114
114
|
if data['sources'] == '#f':
|
|
115
|
-
data.pop('
|
|
115
|
+
data.pop('source_terms', None)
|
|
116
116
|
|
|
117
117
|
return data
|
|
118
118
|
|
|
@@ -235,14 +235,11 @@ class PressureInlet:
|
|
|
235
235
|
data['coordinate_system'] = BoundaryConsts.COORDINATE_SYSTEM.get(self.coordinate_system, 'unknown')
|
|
236
236
|
data['ke_spec'] = BoundaryConsts.KE_SPEC.get(self.ke_spec, 'unknown')
|
|
237
237
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
data.pop('ni', None)
|
|
244
|
-
data.pop('nj', None)
|
|
245
|
-
data.pop('nk', None)
|
|
238
|
+
if data['direction_spec'] == 'Normal to Boundary':
|
|
239
|
+
data.pop('coordinate_system', None)
|
|
240
|
+
data.pop('ni', None)
|
|
241
|
+
data.pop('nj', None)
|
|
242
|
+
data.pop('nk', None)
|
|
246
243
|
|
|
247
244
|
match data['ke_spec']:
|
|
248
245
|
case 'Intensity and Length Scale':
|
|
@@ -265,6 +262,7 @@ class PressureOutlet:
|
|
|
265
262
|
id_: str
|
|
266
263
|
p: str = ''
|
|
267
264
|
t0: str = ''
|
|
265
|
+
|
|
268
266
|
ke_spec: str = ''
|
|
269
267
|
prevent_reverse_flow: str = ''
|
|
270
268
|
radial: str = ''
|
|
@@ -333,25 +331,28 @@ class Wall:
|
|
|
333
331
|
planar_conduction: str = ''
|
|
334
332
|
shell_conduction: str = ''
|
|
335
333
|
|
|
336
|
-
_THERMAL_BC_WHITELIST = {
|
|
337
|
-
'0': {'q_dot'}, # Heat Flux
|
|
338
|
-
'1': {'t'}, # Temperature
|
|
339
|
-
'3': set(), # Coupled
|
|
340
|
-
}
|
|
341
|
-
|
|
342
334
|
def to_dict(self) -> dict[str, str]:
|
|
343
335
|
data = self.__dict__.copy()
|
|
344
336
|
|
|
345
|
-
allowed_attrs = self._THERMAL_BC_WHITELIST.get(self.thermal_bc, set())
|
|
346
|
-
all_thermal_attrs = {'t', 'q_dot', 'h', 'q'}
|
|
347
|
-
attrs_to_remove = all_thermal_attrs - allowed_attrs
|
|
348
|
-
for attr in attrs_to_remove:
|
|
349
|
-
data.pop(attr, None)
|
|
350
|
-
|
|
351
337
|
data['thermal_bc'] = BoundaryConsts.THERMAL_BC.get(self.thermal_bc, 'unknown')
|
|
352
338
|
data['motion_bc'] = BoundaryConsts.MOTION_BC.get(self.motion_bc, 'unknown')
|
|
353
339
|
data['shear_bc'] = BoundaryConsts.SHEAR_BC.get(self.shear_bc, 'unknown')
|
|
354
340
|
|
|
341
|
+
match data['thermal_bc']:
|
|
342
|
+
case 'Heat Flux':
|
|
343
|
+
data.pop('t', None)
|
|
344
|
+
data.pop('q', None)
|
|
345
|
+
data.pop('h', None)
|
|
346
|
+
case 'Temperature':
|
|
347
|
+
data.pop('q_dot', None)
|
|
348
|
+
data.pop('q', None)
|
|
349
|
+
data.pop('h', None)
|
|
350
|
+
case 'Coupled':
|
|
351
|
+
data.pop('q_dot', None)
|
|
352
|
+
data.pop('t', None)
|
|
353
|
+
data.pop('q', None)
|
|
354
|
+
data.pop('h', None)
|
|
355
|
+
|
|
355
356
|
if data['planar_conduction'] == '#f':
|
|
356
357
|
data.pop('shell_conduction', None)
|
|
357
358
|
|
|
@@ -389,6 +390,13 @@ class Interior:
|
|
|
389
390
|
id_: str
|
|
390
391
|
|
|
391
392
|
|
|
393
|
+
@dataclass
|
|
394
|
+
@BoundaryFactory.register('interface')
|
|
395
|
+
class Interface:
|
|
396
|
+
name: str
|
|
397
|
+
id_: str
|
|
398
|
+
|
|
399
|
+
|
|
392
400
|
@dataclass
|
|
393
401
|
@BoundaryFactory.register('symmetry')
|
|
394
402
|
class Symmetry:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Literal, TypeAlias, Union
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def print_version(file_path: str) ->
|
|
4
|
+
def print_version(file_path: str) -> None:
|
|
5
5
|
"""Get the version of the .h5 file
|
|
6
6
|
|
|
7
7
|
Parameters
|
|
@@ -24,7 +24,7 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
24
24
|
Literal[
|
|
25
25
|
'solver', 'materials', 'boundary', 'named-expressions',
|
|
26
26
|
'disc-scheme', 'report-definitions', 'plotsets', 'monitorsets',
|
|
27
|
-
'residuals', 'iter', 'contours', 'vectors'
|
|
27
|
+
'residuals', 'iter', 'contours', 'vectors', 'xy-plot',
|
|
28
28
|
],
|
|
29
29
|
dict[str]
|
|
30
30
|
]:
|
|
@@ -43,8 +43,9 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
43
43
|
import re
|
|
44
44
|
import h5py
|
|
45
45
|
import sexpdata
|
|
46
|
+
from .utils import stringify_nested_list
|
|
46
47
|
|
|
47
|
-
|
|
48
|
+
NestedStrList: TypeAlias = list[Union[str, 'NestedStrList']]
|
|
48
49
|
|
|
49
50
|
with h5py.File(file_path) as f:
|
|
50
51
|
settings: h5py.Group = f['/settings']
|
|
@@ -159,35 +160,34 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
159
160
|
general_info,
|
|
160
161
|
re.M
|
|
161
162
|
).group(1)
|
|
162
|
-
materials:
|
|
163
|
+
materials: NestedStrList = stringify_nested_list(sexpdata.loads(materials))
|
|
163
164
|
for material in materials[1]:
|
|
164
|
-
name =
|
|
165
|
+
name = material[0]
|
|
165
166
|
data['materials'][name] = {}
|
|
166
|
-
data['materials'][name]['type'] =
|
|
167
|
+
data['materials'][name]['type'] = material[1]
|
|
167
168
|
for property_ in material[2:]:
|
|
168
|
-
property_name =
|
|
169
|
-
if property_[1] ==
|
|
170
|
-
data['materials'][name][property_name] =
|
|
169
|
+
property_name = property_[0]
|
|
170
|
+
if property_[1] == '.':
|
|
171
|
+
data['materials'][name][property_name] = property_[2]
|
|
171
172
|
elif isinstance(property_value_list := property_[1], list):
|
|
172
|
-
if property_value_list[1] ==
|
|
173
|
+
if property_value_list[1] == '.':
|
|
173
174
|
data['materials'][name][property_name] = f'{property_value_list[0]}/{property_value_list[2]}'
|
|
174
|
-
elif
|
|
175
|
+
elif property_value_list[1] == 'piecewise-linear':
|
|
175
176
|
value = [f'{p[0]}, {p[2]}' for p in property_value_list[2:]]
|
|
176
177
|
data['materials'][name][property_name] = {
|
|
177
178
|
f'{property_value_list[0]}/{property_value_list[1]}': value
|
|
178
179
|
}
|
|
179
|
-
elif
|
|
180
|
+
elif property_value_list[1] in ['piecewise-polynomial', 'nasa-9-piecewise-polynomial']:
|
|
180
181
|
value = [str(p).strip('[]') for p in property_value_list[2:]]
|
|
181
182
|
data['materials'][name][property_name] = {
|
|
182
183
|
f'{property_value_list[0]}/{property_value_list[1]}': value
|
|
183
184
|
}
|
|
184
|
-
elif
|
|
185
|
+
elif property_value_list[0] == 'orthotropic':
|
|
185
186
|
value = {}
|
|
186
187
|
for p in property_value_list[1:]:
|
|
187
|
-
orth_property_name =
|
|
188
|
+
orth_property_name = p[0]
|
|
188
189
|
if orth_property_name in ['direction-0', 'direction-1', 'direction-2']:
|
|
189
190
|
value[orth_property_name] = str(p[1:]).strip('[]')
|
|
190
|
-
value[orth_property_name] = sexpdata.cdr(p)
|
|
191
191
|
elif orth_property_name in ['k0', 'k1', 'k2']:
|
|
192
192
|
value[orth_property_name] = f'{sexpdata.car(p[1])}/{sexpdata.cdr(p[1])}'
|
|
193
193
|
data['materials'][name][property_name] = {f'{property_value_list[0]}': value}
|
|
@@ -199,19 +199,28 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
199
199
|
from .boundary import BoundaryFactory
|
|
200
200
|
|
|
201
201
|
data['boundary'] = {}
|
|
202
|
-
boundaries: list[
|
|
202
|
+
boundaries: list[NestedStrList] = stringify_nested_list(sexpdata.parse(boundary_info, true=None))
|
|
203
203
|
for boundary_info in boundaries:
|
|
204
|
-
id_, type_, name, _ = [
|
|
204
|
+
id_, type_, name, _ = [_ for _ in boundary_info[1]]
|
|
205
205
|
new_boundary = BoundaryFactory.create(name, id_, type_)
|
|
206
206
|
b_list = data['boundary'].get(type_, [])
|
|
207
207
|
|
|
208
|
-
for property_ in boundary_info[2]:
|
|
209
|
-
property_name =
|
|
208
|
+
for property_ in filter(lambda x: len(x) > 1, boundary_info[2]):
|
|
209
|
+
property_name = property_[0].replace('-', '_').replace('?', '').replace('/', '_')
|
|
210
210
|
if hasattr(new_boundary, property_name):
|
|
211
|
-
if
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
211
|
+
if property_[1] == '.':
|
|
212
|
+
setattr(new_boundary, property_name, property_[2])
|
|
213
|
+
elif isinstance(property_[1], list):
|
|
214
|
+
if property_name == 'source_terms':
|
|
215
|
+
source_terms_list = property_[1:]
|
|
216
|
+
value = {}
|
|
217
|
+
for source_term in filter(lambda x: len(x) > 1, source_terms_list):
|
|
218
|
+
value[source_term[0]] = {}
|
|
219
|
+
source_property = source_term[1]
|
|
220
|
+
for source_property_ in filter(lambda x: x[1] == '.', source_property):
|
|
221
|
+
value[source_term[0]][source_property_[0]] = source_property_[2]
|
|
222
|
+
setattr(new_boundary, property_name, value)
|
|
223
|
+
else:
|
|
215
224
|
setattr(new_boundary, property_name, f'{property_[1][0]}/{property_[1][2]}')
|
|
216
225
|
|
|
217
226
|
b_list.append(new_boundary.to_dict() if hasattr(new_boundary, 'to_dict') else new_boundary.__dict__)
|
|
@@ -224,10 +233,10 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
224
233
|
general_info,
|
|
225
234
|
re.M
|
|
226
235
|
).group(1)
|
|
227
|
-
nes:
|
|
236
|
+
nes: NestedStrList = stringify_nested_list(sexpdata.loads(nes, true=None)[1])
|
|
228
237
|
for ne in nes:
|
|
229
238
|
ne_dict = {
|
|
230
|
-
|
|
239
|
+
property_[0]: property_[2]
|
|
231
240
|
for property_ in ne
|
|
232
241
|
}
|
|
233
242
|
data['named-expressions'][ne_dict['name']] = ne_dict
|
|
@@ -276,22 +285,22 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
276
285
|
general_info,
|
|
277
286
|
re.M
|
|
278
287
|
).group(1)
|
|
279
|
-
rds:
|
|
288
|
+
rds: NestedStrList = stringify_nested_list(sexpdata.loads(rds, true=None)[1])
|
|
280
289
|
for rd in rds:
|
|
281
|
-
name =
|
|
282
|
-
type_ =
|
|
290
|
+
name = rd[0][2]
|
|
291
|
+
type_ = rd[1][1]
|
|
283
292
|
data['report-definitions'][name] = {'type': type_}
|
|
284
293
|
if 'volume' in type_:
|
|
285
|
-
data['report-definitions'][name]['field'] =
|
|
286
|
-
data['report-definitions'][name]['zones'] = [
|
|
287
|
-
data['report-definitions'][name]['per-zone?'] =
|
|
294
|
+
data['report-definitions'][name]['field'] = rd[1][2][2]
|
|
295
|
+
data['report-definitions'][name]['zones'] = [zone for zone in rd[1][6][1:]]
|
|
296
|
+
data['report-definitions'][name]['per-zone?'] = rd[1][-5][2]
|
|
288
297
|
elif 'surface' in type_:
|
|
289
|
-
data['report-definitions'][name]['field'] =
|
|
290
|
-
data['report-definitions'][name]['surfaces'] = [
|
|
291
|
-
data['report-definitions'][name]['per-surface?'] =
|
|
298
|
+
data['report-definitions'][name]['field'] = rd[1][2][2]
|
|
299
|
+
data['report-definitions'][name]['surfaces'] = [surface for surface in rd[1][5][1:]]
|
|
300
|
+
data['report-definitions'][name]['per-surface?'] = rd[1][-5][2]
|
|
292
301
|
elif 'flux' in type_:
|
|
293
|
-
data['report-definitions'][name]['zones'] = [
|
|
294
|
-
data['report-definitions'][name]['per-zone?'] =
|
|
302
|
+
data['report-definitions'][name]['zones'] = [zone for zone in rd[1][3][1:]]
|
|
303
|
+
data['report-definitions'][name]['per-zone?'] = rd[1][-5][2]
|
|
295
304
|
|
|
296
305
|
if kwargs['plotsets']:
|
|
297
306
|
data['plotsets'] = {}
|
|
@@ -300,12 +309,12 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
300
309
|
general_info,
|
|
301
310
|
re.M
|
|
302
311
|
).group(1)
|
|
303
|
-
plotsets:
|
|
312
|
+
plotsets: NestedStrList = stringify_nested_list(sexpdata.loads(plotsets, true=None)[1])
|
|
304
313
|
for plotset in plotsets:
|
|
305
314
|
plotset_dict = {
|
|
306
|
-
|
|
315
|
+
property_[0]: property_[2]
|
|
307
316
|
for property_ in plotset
|
|
308
|
-
if
|
|
317
|
+
if property_[0] not in ['old-props', 'report-defs']
|
|
309
318
|
}
|
|
310
319
|
plotset_dict['report-defs'] = plotset[6][1:]
|
|
311
320
|
data['plotsets'][plotset_dict['name']] = plotset_dict
|
|
@@ -317,12 +326,12 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
317
326
|
general_info,
|
|
318
327
|
re.M
|
|
319
328
|
).group(1)
|
|
320
|
-
monitorsets:
|
|
329
|
+
monitorsets: NestedStrList = stringify_nested_list(sexpdata.loads(monitorsets, true=None)[1])
|
|
321
330
|
for monitorset in monitorsets:
|
|
322
331
|
monitorset_dict = {
|
|
323
|
-
|
|
332
|
+
property_[0]: property_[2]
|
|
324
333
|
for property_ in monitorset
|
|
325
|
-
if
|
|
334
|
+
if property_[0] not in ['old-props', 'report-defs']
|
|
326
335
|
}
|
|
327
336
|
monitorset_dict['report-defs'] = monitorset[-4][1:]
|
|
328
337
|
data['monitorsets'][monitorset_dict['name']] = monitorset_dict
|
|
@@ -360,12 +369,12 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
360
369
|
general_info,
|
|
361
370
|
re.M
|
|
362
371
|
).group(1)
|
|
363
|
-
res:
|
|
372
|
+
res: NestedStrList = stringify_nested_list(sexpdata.loads(res, true=None)[1])
|
|
364
373
|
for eq in res:
|
|
365
|
-
data['residuals'][
|
|
366
|
-
'monitor':
|
|
367
|
-
'check-convergence':
|
|
368
|
-
'absolute-criteria':
|
|
374
|
+
data['residuals'][eq[0]] = {
|
|
375
|
+
'monitor': eq[1],
|
|
376
|
+
'check-convergence': eq[3],
|
|
377
|
+
'absolute-criteria': eq[4],
|
|
369
378
|
}
|
|
370
379
|
|
|
371
380
|
if kwargs['iter']:
|
|
@@ -411,12 +420,12 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
411
420
|
general_info,
|
|
412
421
|
re.M
|
|
413
422
|
).group(1)
|
|
414
|
-
contours:
|
|
423
|
+
contours: NestedStrList = stringify_nested_list(sexpdata.loads(contours, true=None)[1])
|
|
415
424
|
for contour in contours:
|
|
416
425
|
contour_dict = {
|
|
417
|
-
|
|
426
|
+
property_[0]: property_[2]
|
|
418
427
|
for property_ in contour
|
|
419
|
-
if
|
|
428
|
+
if property_[0] not in [
|
|
420
429
|
'locations', 'location-ids', 'options',
|
|
421
430
|
'range-options', 'range-option', 'surfaces-list',
|
|
422
431
|
'color-map', 'colorings', 'annotations-list',
|
|
@@ -424,11 +433,11 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
424
433
|
}
|
|
425
434
|
contour_dict['surface-list'] = contour[2][1]
|
|
426
435
|
contour_dict['range-options'] = {
|
|
427
|
-
|
|
436
|
+
range_option[0]: range_option[2]
|
|
428
437
|
for range_option in contour[6][1:]
|
|
429
438
|
}
|
|
430
439
|
contour_dict['color-map'] = {
|
|
431
|
-
|
|
440
|
+
color_map[0]: color_map[2]
|
|
432
441
|
for color_map in contour[-7][1:]
|
|
433
442
|
}
|
|
434
443
|
data['contours'][contour_dict['name']] = contour_dict
|
|
@@ -440,12 +449,12 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
440
449
|
general_info,
|
|
441
450
|
re.M
|
|
442
451
|
).group(1)
|
|
443
|
-
vectors:
|
|
452
|
+
vectors: NestedStrList = stringify_nested_list(sexpdata.loads(vectors, true=None)[1])
|
|
444
453
|
for vector in vectors:
|
|
445
454
|
vector_dict = {
|
|
446
|
-
|
|
455
|
+
property_[0]: property_[2]
|
|
447
456
|
for property_ in vector
|
|
448
|
-
if
|
|
457
|
+
if property_[0] not in [
|
|
449
458
|
'locations', 'location-ids', 'options', 'scale',
|
|
450
459
|
'range-options', 'range-option', 'surfaces-list',
|
|
451
460
|
'color-map', 'vector-opt', 'annotations-list',
|
|
@@ -453,23 +462,44 @@ def read_case(file_path: str, **kwargs) -> dict[
|
|
|
453
462
|
}
|
|
454
463
|
vector_dict['surface-list'] = vector[3][1]
|
|
455
464
|
vector_dict['range-options'] = {
|
|
456
|
-
|
|
465
|
+
range_option[0]: range_option[2]
|
|
457
466
|
for range_option in vector[7][1:]
|
|
458
467
|
}
|
|
459
468
|
vector_dict['scale'] = {
|
|
460
|
-
|
|
469
|
+
scale[0]: scale[2]
|
|
461
470
|
for scale in vector[9][1:]
|
|
462
471
|
}
|
|
463
472
|
vector_dict['vector-opt'] = {
|
|
464
|
-
|
|
473
|
+
vector_opt[0]: vector_opt[2]
|
|
465
474
|
for vector_opt in vector[-6][1:]
|
|
466
475
|
}
|
|
467
476
|
vector_dict['color-map'] = {
|
|
468
|
-
|
|
477
|
+
color_map[0]: color_map[2]
|
|
469
478
|
for color_map in vector[-5][1:]
|
|
470
479
|
}
|
|
471
480
|
data['vectors'][vector_dict['name']] = vector_dict
|
|
472
481
|
|
|
482
|
+
if kwargs['xy']:
|
|
483
|
+
data['xy-plot'] = {}
|
|
484
|
+
xy_plots = re.search(
|
|
485
|
+
r'(\(graphics/xy-plot.*)',
|
|
486
|
+
general_info,
|
|
487
|
+
re.M
|
|
488
|
+
).group(1)
|
|
489
|
+
xy_plots: NestedStrList = stringify_nested_list(sexpdata.loads(xy_plots, true=None)[1])
|
|
490
|
+
for xy_plot in xy_plots:
|
|
491
|
+
xy_plot_dict = {
|
|
492
|
+
property_[0]: property_[2]
|
|
493
|
+
for property_ in xy_plot
|
|
494
|
+
if property_[0] not in [
|
|
495
|
+
'options', 'x-axis-data', 'y-axis-data',
|
|
496
|
+
'surfaces-list', 'location-ids', 'locations',
|
|
497
|
+
'option', 'plot-direction', 'axes',
|
|
498
|
+
]
|
|
499
|
+
}
|
|
500
|
+
xy_plot_dict['surfaces-list'] = xy_plot[7][1]
|
|
501
|
+
data['xy-plot'][xy_plot_dict['name']] = xy_plot_dict
|
|
502
|
+
|
|
473
503
|
return data
|
|
474
504
|
|
|
475
505
|
|
|
@@ -606,8 +636,40 @@ def show_mesh(file_path: str) -> None:
|
|
|
606
636
|
pl.show()
|
|
607
637
|
|
|
608
638
|
|
|
639
|
+
def plot_outfile(file_path: str) -> None:
|
|
640
|
+
"""Plot Ansys Fluent report files
|
|
641
|
+
|
|
642
|
+
Parameters
|
|
643
|
+
---------
|
|
644
|
+
file_path : str
|
|
645
|
+
Path to the outfile
|
|
646
|
+
"""
|
|
647
|
+
import numpy as np
|
|
648
|
+
import matplotlib.pyplot as plt
|
|
649
|
+
|
|
650
|
+
with open(file_path, encoding='utf-8') as f:
|
|
651
|
+
for line_num, line in enumerate(f, start=1):
|
|
652
|
+
match line_num:
|
|
653
|
+
case 1:
|
|
654
|
+
title = line.strip().strip('"')
|
|
655
|
+
case 2:
|
|
656
|
+
_, *report_definitions = [s.strip('"') for s in line.strip().split()]
|
|
657
|
+
case 3:
|
|
658
|
+
xlabel, *ylabels = [s.strip('"') for s in line.strip('( )').split()]
|
|
659
|
+
case _:
|
|
660
|
+
break
|
|
661
|
+
|
|
662
|
+
data = np.loadtxt(file_path, skiprows=3)
|
|
663
|
+
plt.figure()
|
|
664
|
+
plt.plot(data[:, 0], data[:, 1:])
|
|
665
|
+
plt.title(title)
|
|
666
|
+
plt.xlabel(xlabel)
|
|
667
|
+
plt.legend(ylabels)
|
|
668
|
+
plt.grid()
|
|
669
|
+
plt.show()
|
|
670
|
+
|
|
671
|
+
|
|
609
672
|
def main() -> None:
|
|
610
|
-
import os
|
|
611
673
|
import argparse
|
|
612
674
|
|
|
613
675
|
BANNER = r"""
|
|
@@ -629,13 +691,13 @@ A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening
|
|
|
629
691
|
parser.add_argument(
|
|
630
692
|
"file_path",
|
|
631
693
|
type=str,
|
|
632
|
-
help="path to the .h5 file"
|
|
694
|
+
help="path to the .h5 file",
|
|
633
695
|
)
|
|
634
696
|
|
|
635
697
|
ARGUMENTS = [
|
|
636
698
|
(("--version",), "show the version of the .h5 file"),
|
|
637
699
|
(("--extract",), "extract cas.h5 general and boundary string to files"),
|
|
638
|
-
(("--showmesh",), "show mesh using pyvista"),
|
|
700
|
+
(("--mesh", "--showmesh",), "show mesh using pyvista"),
|
|
639
701
|
(("--solver",), "show solver settings"),
|
|
640
702
|
(("--mat", "--materials"), "show materials settings"),
|
|
641
703
|
(("--bd", "--boundary"), "show boundary settings"),
|
|
@@ -648,7 +710,9 @@ A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening
|
|
|
648
710
|
(("--iter",), "show iteration settings"),
|
|
649
711
|
(("--contours",), "show graphics contours settings"),
|
|
650
712
|
(("--vectors",), "show graphics vectors settings"),
|
|
713
|
+
(("--xy", "--xy-plot"), "show graphics xy-plot settings"),
|
|
651
714
|
(("--save",), "save output to file"),
|
|
715
|
+
(("--plot",), "plot outfile"),
|
|
652
716
|
]
|
|
653
717
|
|
|
654
718
|
for flags, help_text in ARGUMENTS:
|
|
@@ -656,8 +720,9 @@ A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening
|
|
|
656
720
|
|
|
657
721
|
args = parser.parse_args()
|
|
658
722
|
|
|
659
|
-
if not args.file_path.endswith((".cas.h5", ".msh.h5")):
|
|
660
|
-
print("Invalid
|
|
723
|
+
if not args.file_path.endswith((".cas.h5", ".msh.h5")) and not args.plot:
|
|
724
|
+
print("Invalid arguments.")
|
|
725
|
+
print("Please provide a .cas.h5 or .msh.h5 file or add --plot argument to plot outfile")
|
|
661
726
|
return
|
|
662
727
|
|
|
663
728
|
if args.version:
|
|
@@ -667,13 +732,14 @@ A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening
|
|
|
667
732
|
elif args.file_path.endswith(".msh.h5"):
|
|
668
733
|
show_mesh(args.file_path)
|
|
669
734
|
elif args.file_path.endswith(".cas.h5"):
|
|
670
|
-
if args.
|
|
735
|
+
if args.mesh:
|
|
671
736
|
show_mesh(args.file_path)
|
|
672
737
|
else:
|
|
673
738
|
from .utils import print_colored_dict
|
|
674
739
|
keys = [
|
|
675
740
|
'solver', 'mat', 'bd', 'ne', 'disc', 'rd',
|
|
676
|
-
'plotsets', 'monitorsets', 'residuals', 'iter',
|
|
741
|
+
'plotsets', 'monitorsets', 'residuals', 'iter',
|
|
742
|
+
'contours', 'vectors', 'xy',
|
|
677
743
|
]
|
|
678
744
|
kwargs = {k: getattr(args, k) for k in keys}
|
|
679
745
|
output = read_case(args.file_path, **kwargs)
|
|
@@ -683,3 +749,5 @@ A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening
|
|
|
683
749
|
import json
|
|
684
750
|
with open(f"{args.file_path}.json", "w", encoding="utf-8") as f:
|
|
685
751
|
json.dump(output, f, ensure_ascii=False, indent=4)
|
|
752
|
+
elif args.plot:
|
|
753
|
+
plot_outfile(args.file_path)
|
|
@@ -162,3 +162,10 @@ def print_colored_dict(data) -> None:
|
|
|
162
162
|
else:
|
|
163
163
|
# Number / boolean / null inside an array
|
|
164
164
|
print(f"{indent}{PRIM_COLOR}{content}{RESET}")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def stringify_nested_list(lst):
|
|
168
|
+
return [
|
|
169
|
+
stringify_nested_list(item) if isinstance(item, list) else str(item)
|
|
170
|
+
for item in lst
|
|
171
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cffview
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A Python CLI tool to inspect Ansys Fluent .cas.h5/.msh.h5 files without opening Fluent
|
|
5
5
|
Author-email: preamer <1524477126@qq.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -35,8 +35,6 @@ A command-line tool for inspecting Ansys Fluent `.cas.h5` / `.msh.h5` files **wi
|
|
|
35
35
|
- Read solver settings, materials, boundary conditions, discretisation schemes, and more directly from the HDF5 file.
|
|
36
36
|
- Visualise the mesh with [PyVista](https://pyvista.org).
|
|
37
37
|
|
|
38
|
-
---
|
|
39
|
-
|
|
40
38
|
## Installation
|
|
41
39
|
|
|
42
40
|
### PyPI
|
|
@@ -53,8 +51,6 @@ cd cffview
|
|
|
53
51
|
pip install .
|
|
54
52
|
```
|
|
55
53
|
|
|
56
|
-
---
|
|
57
|
-
|
|
58
54
|
## Usage
|
|
59
55
|
|
|
60
56
|
> [!IMPORTANT]
|
|
@@ -70,7 +66,7 @@ cffview <file> [options]
|
|
|
70
66
|
|---|---|
|
|
71
67
|
| `--version` | Print the Fluent version of file |
|
|
72
68
|
| `--extract` | Dump raw Scheme settings to `general.scm` and `boundary.scm` |
|
|
73
|
-
| `--showmesh` | Visualise the mesh interactively with PyVista |
|
|
69
|
+
| `--mesh`, `--showmesh` | Visualise the mesh interactively with PyVista |
|
|
74
70
|
| `--solver` | Solver type, time, dimension, precision, turbulence model, energy, radiation, gravity |
|
|
75
71
|
| `--mat`, `--materials` | Material properties |
|
|
76
72
|
| `--bd`, `--boundary` | Boundary condition settings |
|
|
@@ -83,7 +79,9 @@ cffview <file> [options]
|
|
|
83
79
|
| `--iter` | Iteration / time-step settings |
|
|
84
80
|
| `--contours` | Graphics contours settings |
|
|
85
81
|
| `--vectors` | Graphics vectors settings |
|
|
82
|
+
| `--xy`, `--xy-plot` | Graphics xy-plot settings |
|
|
86
83
|
| `--save` | Save the output to `<file>.json` |
|
|
84
|
+
| `--plot` | Plot the outfile with matplotlib |
|
|
87
85
|
|
|
88
86
|
Multiple flags can be combined freely. Case settings flags (`--solver`, `--mat`, etc.) apply to `.cas.h5` files only.
|
|
89
87
|
|
|
@@ -111,8 +109,6 @@ cffview case.cas.h5 --extract
|
|
|
111
109
|
|
|
112
110
|
[demo.webm](https://github.com/user-attachments/assets/9047a354-b5cb-475e-b030-97609e2a1274)
|
|
113
111
|
|
|
114
|
-
---
|
|
115
|
-
|
|
116
112
|
## License
|
|
117
113
|
|
|
118
114
|
[BSD-3-Clause](LICENSE)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|