Peano
Loading...
Searching...
No Matches
notebook.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
3"""!
4
5Interactive visualisation of Peano patch files within Jupyter notebooks
6
7The routines here read 2d cell data from a Peano patch file (meta file plus
8one file per snapshot) and render one unknown with matplotlib. Within a
9notebook, show_solution() adds a time slider, a play button and a selector
10for the unknown. plot_snapshot() is the non-interactive counterpart which
11can also be used to write PNG files.
12
13Only the default (equidistant) cell mapping in 2d is supported. Adaptive
14meshes work, as every patch is drawn individually.
15
16"""
17import contextlib
18import functools
19import io
20import os
21import sys
22
23import numpy as np
24import matplotlib.pyplot as plt
25
26from tarch.plotter.api.input.PatchFileParser import PatchFileParser
27from tarch.plotter.api.output.Visualiser import Visualiser
28
29
30def read_snapshots(file_name):
31 """!
32
33 Return list of (time stamp, list of patch files) from a patch meta file
34
35 The visualiser resolves the included patch files relative to the meta
36 file, so we can call the routine from any directory.
37
38 """
39 visualiser = Visualiser(file_name, start=0, end=sys.maxsize)
40 with contextlib.redirect_stdout(io.StringIO()):
41 visualiser.read_metadata()
42 return [
43 (snapshot.timestamp, list(snapshot.filenames))
44 for snapshot in visualiser.snapshots
45 ]
46
47
48@functools.lru_cache(maxsize=256)
49def read_patches(patch_file_name):
50 """!
51
52 Return list of (offset, size, values) for all patches of one patch file
53
54 values is a numpy array of shape (dof, dof, unknowns), i.e. we index it
55 with values[y,x,unknown].
56
57 """
58 parser = PatchFileParser(patch_file_name, "", 0)
59 parser.parse_file()
60 result = []
61 for attributes in parser.unknown_attributes.values():
62 assert attributes.dimensions == 2, "Only 2d data is supported"
63 assert attributes.is_data_associated_to_cell, "Only cell data is supported"
64 for patch in attributes.cell_data:
65 result.append(
66 (
67 patch.offset,
68 patch.size,
69 patch.values.reshape(
70 attributes.dof, attributes.dof, attributes.unknowns
71 ),
72 )
73 )
74 return result
75
76
77def read_unknown_names(file_name):
78 """!
79
80 Names of the unknowns as stored in the description of a patch file
81
82 Plotters such as exahype2.api.fv.PlotAsVTKFile write the names of the
83 unknowns as comma-separated list into the description. Returns this list,
84 or None if the description does not hold one name per unknown.
85
86 """
87 _, patch_files = read_snapshots(file_name)[0]
88 parser = PatchFileParser(patch_files[0], "", 0)
89 with contextlib.redirect_stdout(io.StringIO()):
90 parser.parse_file()
91 for attributes in parser.unknown_attributes.values():
92 names = attributes.unknown_names()
93 if names is not None:
94 return names
95 return None
96
97
99 file_name, snapshot=-1, unknown=0, clim=None, ax=None, cmap="viridis", label=None
100):
101 """!
102
103 Plot one unknown of one snapshot
104
105 file_name: String
106 Patch meta file as written by the Peano/ExaHyPE plotters.
107
108 snapshot: Integer
109 Index of snapshot. Negative values count from the end.
110
111 unknown: Integer
112 Index of unknown to plot.
113
114 clim: (float,float) or None
115 Colour range. If None, we use the range of the plotted data.
116
117 Returns the matplotlib axes.
118
119 """
120 snapshots = read_snapshots(file_name)
121 time_stamp, patch_files = snapshots[snapshot]
122
123 if ax is None:
124 _, ax = plt.subplots(figsize=(6, 5))
125
126 patches = [p for f in patch_files for p in read_patches(f)]
127 if clim is None:
128 clim = (
129 min(np.min(values[:, :, unknown]) for _, _, values in patches),
130 max(np.max(values[:, :, unknown]) for _, _, values in patches),
131 )
132
133 for offset, size, values in patches:
134 image = ax.imshow(
135 values[:, :, unknown],
136 origin="lower",
137 extent=(offset[0], offset[0] + size[0], offset[1], offset[1] + size[1]),
138 vmin=clim[0],
139 vmax=clim[1],
140 cmap=cmap,
141 interpolation="nearest",
142 )
143 ax.set_xlim(
144 min(o[0] for o, _, _ in patches), max(o[0] + s[0] for o, s, _ in patches)
145 )
146 ax.set_ylim(
147 min(o[1] for o, _, _ in patches), max(o[1] + s[1] for o, s, _ in patches)
148 )
149 ax.set_aspect("equal")
150 ax.locator_params(nbins=5)
151 ax.set_xlabel("x")
152 ax.set_ylabel("y")
153 name = label if label is not None else "Q[{}]".format(unknown)
154 ax.set_title("{} at t={:.4f}".format(name, time_stamp))
155 ax.figure.colorbar(image, ax=ax)
156 return ax
157
158
159def show_solution(file_name, unknown=0, unknown_names=None, clim=None):
160 """!
161
162 Interactive viewer for a patch file within a Jupyter notebook
163
164 Adds a slider over all snapshots, a play button and - if unknown_names
165 is given - a dropdown to pick the unknown.
166
167 unknown_names: List of strings or None
168 Names of the unknowns in the order they are stored. If None, we take
169 the names from the patch file (@see read_unknown_names()), if it
170 holds any.
171
172 clim: (float,float) or None
173 Fixed colour range. If None, the colour range per unknown is taken
174 from the first snapshot, so you see the solution evolve against a
175 fixed scale.
176
177 """
178 import ipywidgets
179
180 snapshots = read_snapshots(file_name)
181 if unknown_names is None:
182 unknown_names = read_unknown_names(file_name)
183 if unknown_names is None:
184 unknown_names = ["Q[{}]".format(unknown)]
185 unknown_indices = {unknown_names[0]: unknown}
186 else:
187 unknown_indices = {name: i for i, name in enumerate(unknown_names)}
188
189 @functools.lru_cache(maxsize=None)
190 def colour_range(index):
191 if clim is not None:
192 return clim
193 patches = [p for f in snapshots[0][1] for p in read_patches(f)]
194 low = min(np.min(values[:, :, index]) for _, _, values in patches)
195 high = max(np.max(values[:, :, index]) for _, _, values in patches)
196 if low == high:
197 low, high = low - 0.5, high + 0.5
198 return (low, high)
199
200 def draw(snapshot, name):
201 index = unknown_indices[name]
202 plot_snapshot(file_name, snapshot, index, clim=colour_range(index), label=name)
203 plt.show()
204
205 slider = ipywidgets.IntSlider(
206 value=0, min=0, max=len(snapshots) - 1, description="snapshot"
207 )
208 play = ipywidgets.Play(
209 value=0, min=0, max=len(snapshots) - 1, interval=300, description="play"
210 )
211 ipywidgets.jslink((play, "value"), (slider, "value"))
212 selector = ipywidgets.Dropdown(
213 options=list(unknown_indices.keys()),
214 value=[n for n, i in unknown_indices.items() if i == unknown][0],
215 description="unknown",
216 )
217 output = ipywidgets.interactive_output(draw, {"snapshot": slider, "name": selector})
218 return ipywidgets.VBox([ipywidgets.HBox([play, slider, selector]), output])
The visualiser is first and foremost a persistency layer around datasets.
show_solution(file_name, unknown=0, unknown_names=None, clim=None)
Interactive viewer for a patch file within a Jupyter notebook.
Definition notebook.py:159
plot_snapshot(file_name, snapshot=-1, unknown=0, clim=None, ax=None, cmap="viridis", label=None)
Plot one unknown of one snapshot.
Definition notebook.py:100
read_patches(patch_file_name)
Return list of (offset, size, values) for all patches of one patch file.
Definition notebook.py:49
read_unknown_names(file_name)
Names of the unknowns as stored in the description of a patch file.
Definition notebook.py:77