Peano
Loading...
Searching...
No Matches
VTUUnstructuredGrid.py
Go to the documentation of this file.
1# This file is part of the Peano project. For conditions of distribution and
2# use, please see the copyright notice at www.peano-framework.org
3from .Visualiser import Visualiser
4from ..input.PatchFileParser import unknown_names_from_description
5
6import re
7import vtk
8import time
9import traceback
10import multiprocessing
11from concurrent.futures import ProcessPoolExecutor
12
13
15 cell_data,
16 dof,
17 unknowns,
18 depth_scaling,
19 description,
20 is_data_associated_to_cell,
21 mapping,
22):
23 """
24 Prepare 2D patches in a unstructured grid for rendering
25
26 Parameters:
27 ----------
28 cell_data: list of patches
29 List of Patches with file data
30
31 dof: Integer
32 Number of degrees of freedom per axis
33
34 unknowns: Integer
35 Number of unknowns per patch volume
36
37 Returns:
38 ----------
39 grid: vtk.vtkUnstructuredGrid
40 Grid cells
41 """
42
43 points = vtk.vtkPoints()
44 cells = vtk.vtkCellArray()
45 grid = vtk.vtkUnstructuredGrid()
46
47 data = vtk.vtkDoubleArray()
48 data.SetNumberOfComponents(unknowns + 1)
49 if description != "":
50 data.SetName("Unknowns (" + description + ", subdomain)")
51 else:
52 data.SetName("Unknowns (..., subdomain)")
53
54 # Adjust dof per axis based on association type
55 dofs_in_patch_per_axis = dof - 1 if not is_data_associated_to_cell else dof
56
57 points.SetNumberOfPoints(len(cell_data) * (dofs_in_patch_per_axis + 1) ** 2)
58 cells.SetNumberOfCells(len(cell_data) * dofs_in_patch_per_axis**2)
59 point_counter = 0
60
61 for p in range(len(cell_data)):
62 patch_x_0, patch_y_0 = cell_data[p].offset
63 patch_width, patch_height = cell_data[p].size
64 z = patch_width * depth_scaling
65
66 mapping_count = 0
67 for k in range(dofs_in_patch_per_axis + 1):
68 for l in range(dofs_in_patch_per_axis + 1):
69 x = patch_x_0 + mapping[mapping_count] * patch_width
70 mapping_count += 1
71 y = patch_y_0 + mapping[mapping_count] * patch_height
72 mapping_count += 1
73 points.SetPoint(point_counter, x, y, z)
74 point_counter += 1
75
76 for k in range(dofs_in_patch_per_axis):
77 for l in range(dofs_in_patch_per_axis):
78 cells.InsertNextCell(4)
79 cells.InsertCellPoint(
80 p * (dofs_in_patch_per_axis + 1) * (dofs_in_patch_per_axis + 1)
81 + (k + 0) * (dofs_in_patch_per_axis + 1)
82 + l
83 + 0
84 )
85 cells.InsertCellPoint(
86 p * (dofs_in_patch_per_axis + 1) * (dofs_in_patch_per_axis + 1)
87 + (k + 0) * (dofs_in_patch_per_axis + 1)
88 + l
89 + 1
90 )
91 cells.InsertCellPoint(
92 p * (dofs_in_patch_per_axis + 1) * (dofs_in_patch_per_axis + 1)
93 + (k + 1) * (dofs_in_patch_per_axis + 1)
94 + l
95 + 0
96 )
97 cells.InsertCellPoint(
98 p * (dofs_in_patch_per_axis + 1) * (dofs_in_patch_per_axis + 1)
99 + (k + 1) * (dofs_in_patch_per_axis + 1)
100 + l
101 + 1
102 )
103
104 if is_data_associated_to_cell:
105 for k in range(dofs_in_patch_per_axis):
106 for l in range(dofs_in_patch_per_axis):
107 cell_number = k * dof + l
108 new_data = [
109 x
110 for x in cell_data[p].values[
111 cell_number * unknowns : cell_number * unknowns + unknowns
112 ]
113 ]
114 new_data.append(cell_data[p].subdomain_number)
115 data.InsertNextTuple(new_data)
116 else:
117 for k in range(dofs_in_patch_per_axis + 1):
118 for l in range(dofs_in_patch_per_axis + 1):
119 vertex_number = k * (dofs_in_patch_per_axis + 1) + l
120 new_data = [
121 x
122 for x in cell_data[p].values[
123 vertex_number * unknowns : vertex_number * unknowns
124 + unknowns
125 ]
126 ]
127 new_data.append(cell_data[p].subdomain_number)
128 data.InsertNextTuple(new_data)
129
130 grid.SetPoints(points)
131 grid.SetCells(vtk.VTK_PIXEL, cells)
132
133 if is_data_associated_to_cell:
134 grid.GetCellData().SetScalars(data)
135 else:
136 grid.GetPointData().SetScalars(data)
137
138 return grid
139
140
142 cell_data, dof, unknowns, description, is_data_associated_to_cell, mapping
143):
144 """
145 Prepare 3D patches in a unstructured grid for rendering
146
147 Parameters:
148 ----------
149 cell_data: list of patches
150 List of Patches with file data
151
152 dof: Integer
153 Number of degrees of freedom per axis
154
155 unknowns: Integer
156 Number of unknowns per patch volume
157
158 Returns:
159 ----------
160 grid: vtk.vtkUnstructuredGrid
161 Grid cells
162 """
163 dofs_in_patch_per_axis = dof
164 if not is_data_associated_to_cell:
165 dofs_in_patch_per_axis -= 1
166
167 points = vtk.vtkPoints()
168 cells = vtk.vtkCellArray()
169 grid = vtk.vtkUnstructuredGrid()
170
171 data = vtk.vtkDoubleArray()
172 data.SetNumberOfComponents(unknowns + 1)
173 if description != "":
174 data.SetName("Unknowns (" + description + ", subdomain)")
175 else:
176 data.SetName("Unknowns (..., subdomain)")
177
178 point_counter = 0
179 points.SetNumberOfPoints(len(cell_data) * (dofs_in_patch_per_axis + 1) ** 3)
180
181 for p in range(len(cell_data)):
182 patch_x_0 = cell_data[p].offset[0]
183 patch_y_0 = cell_data[p].offset[1]
184 patch_z_0 = cell_data[p].offset[2]
185
186 mapping_count = 0
187 for k in range(dofs_in_patch_per_axis + 1):
188 for l in range(dofs_in_patch_per_axis + 1):
189 for m in range(dofs_in_patch_per_axis + 1):
190 x = patch_x_0 + mapping[mapping_count] * cell_data[p].size[0]
191 mapping_count += 1
192 y = patch_y_0 + mapping[mapping_count] * cell_data[p].size[1]
193 mapping_count += 1
194 z = patch_z_0 + mapping[mapping_count] * cell_data[p].size[2]
195 mapping_count += 1
196 points.SetPoint(point_counter, x, y, z)
197 point_counter += 1
198
199 for k in range(dofs_in_patch_per_axis):
200 for l in range(dofs_in_patch_per_axis):
201 for m in range(dofs_in_patch_per_axis):
202 cells.InsertNextCell(8)
203 cells.InsertCellPoint(
204 p
205 * (dofs_in_patch_per_axis + 1)
206 * (dofs_in_patch_per_axis + 1)
207 * (dofs_in_patch_per_axis + 1)
208 + (k + 0)
209 * (dofs_in_patch_per_axis + 1)
210 * (dofs_in_patch_per_axis + 1)
211 + (l + 0) * (dofs_in_patch_per_axis + 1)
212 + m
213 + 0
214 )
215 cells.InsertCellPoint(
216 p
217 * (dofs_in_patch_per_axis + 1)
218 * (dofs_in_patch_per_axis + 1)
219 * (dofs_in_patch_per_axis + 1)
220 + (k + 0)
221 * (dofs_in_patch_per_axis + 1)
222 * (dofs_in_patch_per_axis + 1)
223 + (l + 0) * (dofs_in_patch_per_axis + 1)
224 + m
225 + 1
226 )
227 cells.InsertCellPoint(
228 p
229 * (dofs_in_patch_per_axis + 1)
230 * (dofs_in_patch_per_axis + 1)
231 * (dofs_in_patch_per_axis + 1)
232 + (k + 0)
233 * (dofs_in_patch_per_axis + 1)
234 * (dofs_in_patch_per_axis + 1)
235 + (l + 1) * (dofs_in_patch_per_axis + 1)
236 + m
237 + 0
238 )
239 cells.InsertCellPoint(
240 p
241 * (dofs_in_patch_per_axis + 1)
242 * (dofs_in_patch_per_axis + 1)
243 * (dofs_in_patch_per_axis + 1)
244 + (k + 0)
245 * (dofs_in_patch_per_axis + 1)
246 * (dofs_in_patch_per_axis + 1)
247 + (l + 1) * (dofs_in_patch_per_axis + 1)
248 + m
249 + 1
250 )
251 cells.InsertCellPoint(
252 p
253 * (dofs_in_patch_per_axis + 1)
254 * (dofs_in_patch_per_axis + 1)
255 * (dofs_in_patch_per_axis + 1)
256 + (k + 1)
257 * (dofs_in_patch_per_axis + 1)
258 * (dofs_in_patch_per_axis + 1)
259 + (l + 0) * (dofs_in_patch_per_axis + 1)
260 + m
261 + 0
262 )
263 cells.InsertCellPoint(
264 p
265 * (dofs_in_patch_per_axis + 1)
266 * (dofs_in_patch_per_axis + 1)
267 * (dofs_in_patch_per_axis + 1)
268 + (k + 1)
269 * (dofs_in_patch_per_axis + 1)
270 * (dofs_in_patch_per_axis + 1)
271 + (l + 0) * (dofs_in_patch_per_axis + 1)
272 + m
273 + 1
274 )
275 cells.InsertCellPoint(
276 p
277 * (dofs_in_patch_per_axis + 1)
278 * (dofs_in_patch_per_axis + 1)
279 * (dofs_in_patch_per_axis + 1)
280 + (k + 1)
281 * (dofs_in_patch_per_axis + 1)
282 * (dofs_in_patch_per_axis + 1)
283 + (l + 1) * (dofs_in_patch_per_axis + 1)
284 + m
285 + 0
286 )
287 cells.InsertCellPoint(
288 p
289 * (dofs_in_patch_per_axis + 1)
290 * (dofs_in_patch_per_axis + 1)
291 * (dofs_in_patch_per_axis + 1)
292 + (k + 1)
293 * (dofs_in_patch_per_axis + 1)
294 * (dofs_in_patch_per_axis + 1)
295 + (l + 1) * (dofs_in_patch_per_axis + 1)
296 + m
297 + 1
298 )
299
300 if is_data_associated_to_cell:
301 for k in range(dofs_in_patch_per_axis):
302 for l in range(dofs_in_patch_per_axis):
303 for m in range(dofs_in_patch_per_axis):
304 cell_number = k * dof * dof + l * dof + m
305 new_data = [
306 x
307 for x in cell_data[p].values[
308 cell_number * unknowns : cell_number * unknowns
309 + unknowns
310 ]
311 ]
312 new_data.append(cell_data[p].subdomain_number)
313 data.InsertNextTuple(new_data)
314 else:
315 for k in range(dofs_in_patch_per_axis + 1):
316 for l in range(dofs_in_patch_per_axis + 1):
317 for m in range(dofs_in_patch_per_axis + 1):
318 vertex_number = (
319 k
320 * (dofs_in_patch_per_axis + 1)
321 * (dofs_in_patch_per_axis + 1)
322 + l * (dofs_in_patch_per_axis + 1)
323 + m
324 )
325 new_data = [
326 x
327 for x in cell_data[p].values[
328 vertex_number * unknowns : vertex_number * unknowns
329 + unknowns
330 ]
331 ]
332 new_data.append(cell_data[p].subdomain_number)
333 data.InsertNextTuple(new_data)
334
335 grid.SetPoints(points)
336 grid.SetCells(vtk.VTK_VOXEL, cells)
337
338 if is_data_associated_to_cell:
339 grid.GetCellData().SetScalars(data)
340 else:
341 grid.GetPointData().SetScalars(data)
342
343 return grid
344
345
346def group_unknown_names(names):
347 """!
348
349 Group the names of the unknowns into the arrays we write
350
351 A name v[i] belongs to the vector v, all other names are scalars. Returns
352 a list of (array name, list of indices of the unknowns), in the order in
353 which the arrays first show up, e.g. ["p", "v[0]", "v[1]"] yields
354 [("p", [0]), ("v", [1, 2])].
355
356 """
357 groups = {}
358 for index, name in enumerate(names):
359 match = re.fullmatch(r"(.+)\[\d+\]", name)
360 groups.setdefault(match.group(1) if match else name, []).append(index)
361 return list(groups.items())
362
363
365 grid, description, unknowns, is_data_associated_to_cell, split_unknowns
366):
367 """!
368
369 Label the unknowns of a grid with their symbolic names
370
371 If the description of the unknowns holds one name per unknown (@see
372 unknown_names_from_description()), we name the components of the data
373 array accordingly, plus the trailing subdomain component. Paraview then
374 offers the names when you colour by the array. Otherwise, we leave the
375 grid alone.
376
377 split_unknowns: Boolean
378 If set, we additionally add one array per variable, so filters such as
379 Paraview's Calculator or Contour can address it by name. A scalar
380 becomes an array with one component. The entries v[0], v[1], ... of a
381 variable with multiplicity become one vector array v. As Paraview only
382 treats arrays with three components as vectors, e.g. for glyphs, we
383 pad a vector with two entries with a zero.
384
385 """
386 names = unknown_names_from_description(description, unknowns)
387 if names is None:
388 return
389
390 field_data = (
391 grid.GetCellData() if is_data_associated_to_cell else grid.GetPointData()
392 )
393 data = field_data.GetScalars()
394 for index, name in enumerate(names):
395 data.SetComponentName(index, name)
396 data.SetComponentName(unknowns, "subdomain")
397
398 if split_unknowns:
399 for array_name, indices in group_unknown_names(names):
400 array = vtk.vtkDoubleArray()
401 array.SetName(array_name)
402 array.SetNumberOfComponents(3 if len(indices) == 2 else len(indices))
403 array.SetNumberOfTuples(data.GetNumberOfTuples())
404 if len(indices) == 2:
405 array.FillComponent(2, 0.0)
406 for component, index in enumerate(indices):
407 array.CopyComponent(component, data, index)
408 field_data.AddArray(array)
409
410
412 """!
413 The visualiser is first and foremost a persistency layer around
414 datasets. It does not work on the command line.
415
416 Use this class by feeding the patch meta file you want to render
417 into render.py, which will call this class.
418
419 Everything happens in either display() or display_single_file().
420 Please see documentation of these functions for more info.
421 """
422
424 self,
425 file_name,
426 output_directory=".",
427 strip_relative_paths=True,
428 start=0,
429 end=0,
430 verbose=False,
431 split_unknowns=False,
432 ):
433 """!
434
435 split_unknowns: Boolean
436 Write one additional array per (named) variable. @see name_unknowns()
437
438 """
439 super(VTUUnstructuredGrid, self).__init__(file_name, start, end, verbose)
440 self.split_unknowns = split_unknowns
441 self._tp = None
442 self._grid = None
443 self.output_directory = output_directory
444 self.display_as_tree = False
445 self._strip_relative_paths = strip_relative_paths
446
447 # This is the standard header that we can put at the top of all pvd files. Don't overwrite it
448 self.pvd_file = """<?xml version="1.0"?>
449<VTKFile type="Collection" version="0.1"
450 byte_order="LittleEndian"
451 compressor="vtkZLibDataCompressor">
452 <Collection>
453"""
454
455 def process_snapshot(self, snapshot, i):
456 """
457 Helper function to process a single snapshot.
458 Calls parse_and_render_snapshot and returns the snapshot timestamp and filenames.
459 """
460 if self.verbose:
461 print("==================================")
462 print(f"Process snapshot {i}")
463 print("==================================")
464 written_filenames = self.parse_and_render_snapshotparse_and_render_snapshot(snapshot, i)
465 return snapshot.timestamp, written_filenames
466
467 def display(self, max_workers=None):
468 """
469 Should be called only once.
470
471 How does this work?
472
473 - First, hand over to the superclass display() function.
474 This reads the metadata file, which should be available
475 as self._file_name. This will populate self.snapshots
476 with a list of the Snapshots helper classes. This helper
477 class just contains timestamp plus a list of all patch
478 files to parse for this timestamp
479
480 - Once the list of snapshots has been populated, we loop
481 over each snapshot, calling parse_and_render_snapshot()
482
483 For each unknown that we see on each cell/vertex, we
484 produce an ordered list of VTUUnstructuredGrid files, and a PVD file
485 to glue them together.
486 """
487
488 super(VTUUnstructuredGrid, self).display()
489
490 # This will be the file name that we output
491 self._file_name_file_name = self._file_name_file_name.replace(".peano-patch-file", "")
492
493 # Per-file progress bars (@see Visualiser.show_progress) only
494 # produce clean output when this process is the only one parsing
495 # files at any given time - true for max_workers==1 (render.py's
496 # own default), but not once snapshots get farmed out to worker
497 # processes below, where several processes would otherwise write
498 # their own \r-updated line to the same shared terminal.
499 self.show_progressshow_progress = max_workers == 1
500
501 start_time = time.time()
502 try:
503 filenames = {}
504 total_snapshots = len(self.snapshotssnapshots)
505 completed_snapshots = 0
506
507 """
508 Here's how this loop works:
509 - call parse_and_render for each dataset
510 - this should write the VTUUnstructuredGrid file and return the filename it wrote
511 - we collect these filenames and then write the PVD files at the end
512 """
513 if max_workers == 1:
514 # A process pool with exactly one worker buys no parallelism,
515 # and spinning one up at all touches Python's multiprocessing
516 # machinery - including its resource-tracker helper process,
517 # which is always forked regardless of start method (see the
518 # comment on the ProcessPoolExecutor branch below). That is
519 # reliably unsafe under pvpython, whose process already has
520 # VTK/ParaView's C++ runtime initialised. So for the common,
521 # default case of a single worker, just process every
522 # snapshot directly in this process - no subprocess at all.
523 workers_used = 1
524 print("Processing snapshots sequentially (1 worker)")
525 results = [
526 self.process_snapshotprocess_snapshot(snapshot, i)
527 for i, snapshot in enumerate(self.snapshotssnapshots)
528 ]
529 else:
530 # Explicitly use "spawn" rather than the platform default
531 # ("fork" on Linux): render.py is frequently run through
532 # pvpython, whose process already has VTK/ParaView's C++
533 # runtime (and possibly MPI) fully initialised before this
534 # script starts. Forking that process to create worker
535 # processes inherits that half-initialised state and
536 # reliably crashes the worker (BrokenProcessPool / heap
537 # corruption). "spawn" starts genuinely fresh interpreters
538 # instead. Note this does not protect against
539 # multiprocessing's own resource-tracker helper process,
540 # which is always forked regardless of context - real
541 # (>1-worker) parallelism may therefore still be unsafe
542 # under some pvpython builds; if so, run render.py through
543 # plain python3 instead (with vtk installed from Peano's
544 # requirements.txt), not through pvpython.
545 with ProcessPoolExecutor(
546 max_workers=max_workers,
547 mp_context=multiprocessing.get_context("spawn"),
548 ) as executor:
549 workers_used = executor._max_workers
550 print(
551 f"Processing snapshots in parallel using {workers_used} workers"
552 )
553 # Loop over all the snapshots we detected whilst reading metadata
554 # Submit all snapshots to be processed in parallel
555 futures = [
556 executor.submit(self.process_snapshotprocess_snapshot, snapshot, i)
557 for i, snapshot in enumerate(self.snapshotssnapshots)
558 ]
559 results = [future.result() for future in futures]
560
561 for timestamp, written_filenames in results:
562 # Update filenames dictionary with the results from each snapshot
563 for unknown_name, file_name in written_filenames.items():
564 if unknown_name not in filenames.keys():
565 # Haven't seen the name of this unknown before
566 filenames[unknown_name] = []
567 # Add the vtu filename we've written to the list for this unknown
568 filenames[unknown_name].append(
569 (timestamp, file_name)
570 ) # Assuming file_name is not a list
571
572 completed_snapshots += 1
573 progress = (completed_snapshots / total_snapshots) * 100
574 print(
575 f"Progress: {completed_snapshots}/{total_snapshots} snapshots processed ({progress:.2f}%)"
576 )
577
578 except Exception as e:
579 print("Got an exception: " + str(e))
580 traceback.print_exc()
581 print(
582 "Aborting: not all snapshots were rendered, so no .pvd file(s) will "
583 "be written. Re-run once the underlying problem above is fixed."
584 )
585 return
586 end_time = time.time()
587
588 print(
589 f"Completed in {end_time - start_time:.2f} seconds with {workers_used} workers"
590 )
591 print(f"Dumped {sum([len(v) for _,v in filenames.items()])} datasets")
592
593 # Next, we need to write a pvd "glue" file for each of these data series
594 any_relative_path_written = False
595 for unknown_name, file_data in filenames.items():
596 # Make a copy of self.pvd file
597 pvd_file = self.pvd_file
598
599 # Now we need to loop over all of the pairs of (timestamp, file_name)
600 for timestamp, file_name in file_data:
601 if self._strip_relative_paths:
602 file_name = file_name.split("/")[-1]
603 elif "/" in file_name:
604 any_relative_path_written = True
605
606 # Add the vtu filename to the pvd file.
607 pvd_file += f"""
608 <DataSet timestep=\"{timestamp}\" group=\"\" part=\"0\" file=\"{file_name}\" />
609"""
610 pvd_file += """
611 </Collection>
612</VTKFile>
613"""
614 # Finally, let's write this pvd file.
615 with open(
616 f"{self.output_directory}/{self._file_name}.{unknown_name}.pvd", "w"
617 ) as meta_file:
618 meta_file.write(pvd_file)
619
620 if any_relative_path_written:
621 print(
622 """
623WARNING: The conversion tool has processed a dataset stored in a different
624directory. If the resulting .pvd file(s)
625contains relative paths, you will have to copy it into the present working
626directory before you load the outcome into ParaView. Otherwise, ParaView
627will not be able to locate your actual data files.
628
629Alternatively, you can rerun the postprocessing and eliminate relative
630paths (see documentation/help of your conversion script).
631"""
632 )
633
635 snapshot = super(VTUUnstructuredGrid, self).display_single_file()
637
638 def write_vtu(self, unknown_name, grid_data, index=None):
639 """!
640 unknown_name should describe which unknown we are
641 writing here.
642
643 Add an index to be placed in the filename
644 """
645 if index is None:
646 # We splice this number into the filename.
647 # So as to avoid another if/else statement,
648 # we make this the empty string if not
649 # supplied any integer.
650 index = ""
651 else:
652 index = f"-{index}" # Add a dash in
653 if self._file_name_file_name.endswith(".vtu"):
654 # Strip the vtu off
656 file_name = (
657 f"{self.output_directory}/{self._file_name}.{unknown_name}{index}.vtu"
658 )
659
660 writer = vtk.vtkXMLUnstructuredGridWriter()
661 writer.SetDataModeToBinary()
662 writer.SetFileName(file_name)
663 writer.SetInputData(grid_data)
664 writer.Write()
665 del writer # Explicitly destroy object to speed up things
666
667 if self.verbose:
668 print("Wrote file " + file_name)
669
670 return file_name
671
672 def parse_and_render_snapshot(self, snapshot, index=0):
673 """!
674 We want to call the method in superclass,
675 and then process this data into VTUUnstructuredGrid.
676
677 Returns the filenames that we wrote, for each
678 unknown. We combine these into vtk at the end.
679 """
680 data_to_be_rendered = super(
681 VTUUnstructuredGrid, self
682 ).parse_and_render_snapshot(snapshot)
683
684 written_filenames = {}
685 grid = None
686
687 for unknown_name, unknown_data in data_to_be_rendered.items():
688 """
689 Need to convert all of this data into 2d/3d patches
690 """
691 if unknown_data._cell_data != []:
692 if unknown_data._dimensions == 2:
694 unknown_data._cell_data,
695 unknown_data._dof,
696 unknown_data._unknowns,
697 1.0 if self.display_as_tree else 0.0,
698 unknown_data._description,
699 unknown_data._is_data_associated_to_cell,
700 unknown_data._mapping,
701 )
702 else:
704 unknown_data._cell_data,
705 unknown_data._dof,
706 unknown_data._unknowns,
707 unknown_data._description,
708 unknown_data._is_data_associated_to_cell,
709 unknown_data._mapping,
710 )
712 grid,
713 unknown_data._description,
714 unknown_data._unknowns,
715 unknown_data._is_data_associated_to_cell,
716 self.split_unknowns,
717 )
718 else:
719 assert False, "Found empty cell data"
720 # Write this data to vtu, return the filename for later processing
721 if grid is not None:
722 written_filenames[unknown_name] = self.write_vtu(
723 unknown_name, grid, index
724 )
725
726 return written_filenames
The visualiser is first and foremost a persistency layer around datasets.
write_vtu(self, unknown_name, grid_data, index=None)
unknown_name should describe which unknown we are writing here.
process_snapshot(self, snapshot, i)
Helper function to process a single snapshot.
parse_and_render_snapshot(self, snapshot, index=0)
We want to call the method in superclass, and then process this data into VTUUnstructuredGrid.
__init__(self, file_name, output_directory=".", strip_relative_paths=True, start=0, end=0, verbose=False, split_unknowns=False)
split_unknowns: Boolean Write one additional array per (named) variable.
display(self, max_workers=None)
Should be called only once.
display_single_file(self)
If we are here we've supplied a single file which is itself a snapshot.
The visualiser is first and foremost a persistency layer around datasets.
parse_and_render_snapshot(self, snapshot)
We pass in a snapshot object here, which should contain time stamp and filenames for everything we wa...
prepare_3D_patches_unstructured_grid(cell_data, dof, unknowns, description, is_data_associated_to_cell, mapping)
Prepare 3D patches in a unstructured grid for rendering.
name_unknowns(grid, description, unknowns, is_data_associated_to_cell, split_unknowns)
Label the unknowns of a grid with their symbolic names.
prepare_2D_patches_unstructured_grid(cell_data, dof, unknowns, depth_scaling, description, is_data_associated_to_cell, mapping)
Prepare 2D patches in a unstructured grid for rendering.