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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cffview
3
- Version: 0.3.2
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.2"
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
- sources_terms: str = ''
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('sources_terms', None)
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
- sources_terms: str = ''
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('sources_terms', None)
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
- match data['direction_spec']:
239
- case 'Direction Vector':
240
- pass
241
- case 'Normal to Boundary':
242
- data.pop('coordinate_system', None)
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) -> 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
- SexpdataList: TypeAlias = list[Union[sexpdata.Symbol, 'SexpdataList']]
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: SexpdataList = sexpdata.loads(materials)
163
+ materials: NestedStrList = stringify_nested_list(sexpdata.loads(materials))
163
164
  for material in materials[1]:
164
- name = str(material[0])
165
+ name = material[0]
165
166
  data['materials'][name] = {}
166
- data['materials'][name]['type'] = str(material[1])
167
+ data['materials'][name]['type'] = material[1]
167
168
  for property_ in material[2:]:
168
- property_name = str(property_[0])
169
- if property_[1] == sexpdata.Symbol('.'):
170
- data['materials'][name][property_name] = str(property_[2])
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] == sexpdata.Symbol('.'):
173
+ if property_value_list[1] == '.':
173
174
  data['materials'][name][property_name] = f'{property_value_list[0]}/{property_value_list[2]}'
174
- elif str(property_value_list[1]) == 'piecewise-linear':
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 str(property_value_list[1]) in ['piecewise-polynomial', 'nasa-9-piecewise-polynomial']:
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 str(property_value_list[0]) == 'orthotropic':
185
+ elif property_value_list[0] == 'orthotropic':
185
186
  value = {}
186
187
  for p in property_value_list[1:]:
187
- orth_property_name = str(p[0])
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[SexpdataList] = sexpdata.parse(boundary_info, true=None)
202
+ boundaries: list[NestedStrList] = stringify_nested_list(sexpdata.parse(boundary_info, true=None))
203
203
  for boundary_info in boundaries:
204
- id_, type_, name, _ = [str(_) for _ in boundary_info[1]]
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 = str(property_[0]).replace('-', '_').replace('?', '').replace('/', '_')
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 len(property_) > 1:
212
- if property_[1] == sexpdata.Symbol('.'):
213
- setattr(new_boundary, property_name, str(property_[2]))
214
- elif isinstance(property_[1], list):
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: SexpdataList = sexpdata.loads(nes, true=None)[1]
236
+ nes: NestedStrList = stringify_nested_list(sexpdata.loads(nes, true=None)[1])
228
237
  for ne in nes:
229
238
  ne_dict = {
230
- str(property_[0]): str(property_[2])
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: SexpdataList = sexpdata.loads(rds, true=None)[1]
288
+ rds: NestedStrList = stringify_nested_list(sexpdata.loads(rds, true=None)[1])
280
289
  for rd in rds:
281
- name = str(rd[0][2])
282
- type_ = str(rd[1][1])
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'] = str(rd[1][2][2])
286
- data['report-definitions'][name]['zones'] = [str(zone) for zone in rd[1][6][1:]]
287
- data['report-definitions'][name]['per-zone?'] = str(rd[1][-5][2])
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'] = str(rd[1][2][2])
290
- data['report-definitions'][name]['surfaces'] = [str(surface) for surface in rd[1][5][1:]]
291
- data['report-definitions'][name]['per-surface?'] = str(rd[1][-5][2])
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'] = [str(zone) for zone in rd[1][3][1:]]
294
- data['report-definitions'][name]['per-zone?'] = str(rd[1][-5][2])
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: SexpdataList = sexpdata.loads(plotsets, true=None)[1]
312
+ plotsets: NestedStrList = stringify_nested_list(sexpdata.loads(plotsets, true=None)[1])
304
313
  for plotset in plotsets:
305
314
  plotset_dict = {
306
- str(property_[0]): str(property_[2])
315
+ property_[0]: property_[2]
307
316
  for property_ in plotset
308
- if str(property_[0]) not in ['old-props', 'report-defs']
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: SexpdataList = sexpdata.loads(monitorsets, true=None)[1]
329
+ monitorsets: NestedStrList = stringify_nested_list(sexpdata.loads(monitorsets, true=None)[1])
321
330
  for monitorset in monitorsets:
322
331
  monitorset_dict = {
323
- str(property_[0]): str(property_[2])
332
+ property_[0]: property_[2]
324
333
  for property_ in monitorset
325
- if str(property_[0]) not in ['old-props', 'report-defs']
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: SexpdataList = sexpdata.loads(res, true=None)[1]
372
+ res: NestedStrList = stringify_nested_list(sexpdata.loads(res, true=None)[1])
364
373
  for eq in res:
365
- data['residuals'][str(eq[0])] = {
366
- 'monitor': str(eq[1]),
367
- 'check-convergence': str(eq[3]),
368
- 'absolute-criteria': str(eq[4]),
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: SexpdataList = sexpdata.loads(contours, true=None)[1]
423
+ contours: NestedStrList = stringify_nested_list(sexpdata.loads(contours, true=None)[1])
415
424
  for contour in contours:
416
425
  contour_dict = {
417
- str(property_[0]): str(property_[2])
426
+ property_[0]: property_[2]
418
427
  for property_ in contour
419
- if str(property_[0]) not in [
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
- str(range_option[0]): str(range_option[2])
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
- str(color_map[0]): str(color_map[2])
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: SexpdataList = sexpdata.loads(vectors, true=None)[1]
452
+ vectors: NestedStrList = stringify_nested_list(sexpdata.loads(vectors, true=None)[1])
444
453
  for vector in vectors:
445
454
  vector_dict = {
446
- str(property_[0]): str(property_[2])
455
+ property_[0]: property_[2]
447
456
  for property_ in vector
448
- if str(property_[0]) not in [
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
- str(range_option[0]): str(range_option[2])
465
+ range_option[0]: range_option[2]
457
466
  for range_option in vector[7][1:]
458
467
  }
459
468
  vector_dict['scale'] = {
460
- str(scale[0]): str(scale[2])
469
+ scale[0]: scale[2]
461
470
  for scale in vector[9][1:]
462
471
  }
463
472
  vector_dict['vector-opt'] = {
464
- str(vector_opt[0]): str(vector_opt[2])
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
- str(color_map[0]): str(color_map[2])
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 file path. Please provide a .cas.h5 or .msh.h5 file.")
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.showmesh:
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', 'contours', 'vectors',
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.2
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