123def _visualise_compiled_step_sequence(steps, title: str) ->
None:
125 Simplified sibling of peano4.api.taskgraph.Graph.visualise(): the same
126 UML-state-diagram look (rounded-rectangle states, an internal-activity
127 compartment listing "do / <name>" per action set plus "cell / <name>",
128 "face / <name>", "vertex / <name>" per kind of mesh data the step uses,
129 self-loops for repeating steps, initial/final pseudostates), but laid
130 out as a single top-to-bottom chain following steps' list order, since a
131 compiled sequence - unlike a Graph - has no dependency structure left to
132 derive layers from; it already is the order.
134 import matplotlib.pyplot
as plt
135 from matplotlib.patches
import Circle, FancyArrowPatch, FancyBboxPatch
136 from matplotlib.path
import Path
139 peano4.api.Logger().log(f
"Compiled task graph '{title}' is empty - nothing to visualise.")
143 ACTIVITY_LINE_HEIGHT = 0.22
148 SELF_LOOP_WIDTH = 0.4
149 SELF_LOOP_LABEL_CHAR_WIDTH = 0.065
150 PSEUDOSTATE_OFFSET = 0.6
154 def activity_lines(step):
156 for kind, submodels, access
in (
157 (
"cell", step.cell_data, step.cell_data_access),
158 (
"face", step.face_data, step.face_data_access),
159 (
"vertex", step.vertex_data, step.vertex_data_access),
162 lines.append(f
"{kind} / " +
", ".join(
_submodel_label(submodel, access)
for submodel
in submodels))
165 width_of_step, height_of_step = {}, {}
167 lines = activity_lines(step)
168 longest_line = max([len(step.name)] + [len(line)
for line
in lines])
169 width_of_step[step] = max(MIN_WIDTH, CHAR_WIDTH * longest_line + 0.6)
170 height_of_step[step] = HEADER_HEIGHT + (
171 BODY_PADDING + len(lines) * ACTIVITY_LINE_HEIGHT + 0.5 * BODY_PADDING
if lines
else 0
175 position_of_step = {}
178 position_of_step[step] = (0.0, row_y - height_of_step[step] / 2)
179 row_y -= height_of_step[step] + VERTICAL_GAP
180 bottom_y = row_y + VERTICAL_GAP
182 def self_loop_extent(step) -> float:
183 if not step.has_to_repeat:
185 return SELF_LOOP_WIDTH + 0.06 + SELF_LOOP_LABEL_CHAR_WIDTH * len(step.has_to_repeat)
187 min_x = -max(width_of_step.values()) / 2 - PADDING
188 max_x = max(width_of_step[step] / 2 + self_loop_extent(step)
for step
in steps) + PADDING
189 min_y = bottom_y - PSEUDOSTATE_OFFSET - 0.11 - PADDING
190 max_y = PSEUDOSTATE_OFFSET + 0.09 + PADDING
192 LEFT_CHROME, RIGHT_CHROME, TOP_CHROME, BOTTOM_CHROME = 0.1, 0.1, 0.4, 0.15
193 data_width_inches = (max_x - min_x) * INCH_PER_UNIT
194 data_height_inches = (max_y - min_y) * INCH_PER_UNIT
195 figure_width = data_width_inches + LEFT_CHROME + RIGHT_CHROME
196 figure_height = data_height_inches + TOP_CHROME + BOTTOM_CHROME
198 figure = plt.figure(figsize=(figure_width, figure_height))
199 axes = figure.add_axes(
201 LEFT_CHROME / figure_width,
202 BOTTOM_CHROME / figure_height,
203 data_width_inches / figure_width,
204 data_height_inches / figure_height,
207 axes.set_xlim(min_x, max_x)
208 axes.set_ylim(min_y, max_y)
212 x, y = position_of_step[step]
213 w, h = width_of_step[step], height_of_step[step]
214 lines = activity_lines(step)
216 box = FancyBboxPatch(
217 (x - w / 2, y - h / 2),
220 boxstyle=
"round,pad=0,rounding_size=0.09",
227 state_patch[step] = box
229 header_center_y = y + h / 2 - HEADER_HEIGHT / 2
if lines
else y
232 (x, header_center_y),
233 ha=
"center", va=
"center", zorder=3, fontsize=7.5, fontweight=
"bold",
237 separator_y = y + h / 2 - HEADER_HEIGHT
239 [x - w / 2, x + w / 2], [separator_y, separator_y],
240 color=
"black", linewidth=0.8, zorder=3,
242 for index, line
in enumerate(lines):
243 line_y = separator_y - BODY_PADDING - index * ACTIVITY_LINE_HEIGHT - 0.08
246 (x - w / 2 + 0.12, line_y),
247 ha=
"left", va=
"center", zorder=3, fontsize=6.5, color=
"dimgray",
250 for i
in range(len(steps) - 1):
253 position_of_step[steps[i]],
254 position_of_step[steps[i + 1]],
255 patchA=state_patch[steps[i]],
256 patchB=state_patch[steps[i + 1]],
266 if step.has_to_repeat:
267 x, y = position_of_step[step]
268 w, h = width_of_step[step], height_of_step[step]
270 upper_y, lower_y = y + 0.18 * h, y - 0.18 * h
274 (right_x + SELF_LOOP_WIDTH, upper_y),
275 (right_x + SELF_LOOP_WIDTH, lower_y),
278 [Path.MOVETO, Path.CURVE4, Path.CURVE4, Path.CURVE4],
292 (right_x + SELF_LOOP_WIDTH + 0.06, y),
293 ha=
"left", va=
"center", zorder=3, fontsize=6, style=
"italic", color=
"crimson",
296 first_pos = position_of_step[steps[0]]
297 initial_pos = (first_pos[0], PSEUDOSTATE_OFFSET)
298 initial_patch = Circle(initial_pos, 0.09, facecolor=
"black", edgecolor=
"black", zorder=3)
299 axes.add_patch(initial_patch)
302 initial_pos, first_pos,
303 patchA=initial_patch, patchB=state_patch[steps[0]],
304 arrowstyle=
"-|>", mutation_scale=13, color=
"black", linewidth=1.1, zorder=1,
308 last_pos = position_of_step[steps[-1]]
309 final_pos = (last_pos[0], bottom_y - PSEUDOSTATE_OFFSET)
310 final_outer = Circle(final_pos, 0.11, facecolor=
"white", edgecolor=
"black", linewidth=1.3, zorder=3)
311 axes.add_patch(final_outer)
312 axes.add_patch(Circle(final_pos, 0.05, facecolor=
"black", edgecolor=
"none", zorder=4))
316 patchA=state_patch[steps[-1]], patchB=final_outer,
317 arrowstyle=
"-|>", mutation_scale=13, color=
"black", linewidth=1.1, zorder=1,
321 figure.suptitle(title, y=1.0 - 0.5 * TOP_CHROME / figure_height, fontsize=10)
324 axes.set_aspect(
"equal")
329 plt.show(block=
False)