Peano
Loading...
Searching...
No Matches
35_offline-load-balancing-plan-vs-run.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
5Create the plan-vs-run PNG of the tutorial 35_offline-load-balancing.ipynb
6
7The script computes the offline decomposition of the tutorial's regular mesh
8along Peano's space-filling curve, keeps the cells along the periodic
9boundary on tree 0, and hands out all splits bottom-up. It then builds the
10tutorial's Euler setup with this hardcoded load balancing, runs it with four
11threads, and draws the offline plan next to the subdomains that Peano
12actually builds. Cells which end up in a different subdomain than planned
13are crossed out. Call the script from anywhere:
14
15 python3 tutorials/exahype2/illustrations/35_offline-load-balancing-plan-vs-run.py
16
17All builds and outputs go into a temporary directory (or --work-directory).
18The script needs matplotlib, but no ParaView.
19
20"""
21import argparse
22import importlib.util
23import os
24import tempfile
25
26import numpy as np
27import matplotlib
28
29matplotlib.use("Agg")
30import matplotlib.pyplot as plt
31from matplotlib.colors import ListedColormap
32from matplotlib.patches import Patch
33
34Here = os.path.dirname(os.path.abspath(__file__))
35
36# The file names of the scripts start with a digit, i.e. we cannot import them
37_specification = importlib.util.spec_from_file_location(
38 "split_modes_illustrations",
39 os.path.join(Here, "35_offline-load-balancing-split-modes.py"),
40)
41split_modes = importlib.util.module_from_spec(_specification)
42_specification.loader.exec_module(split_modes)
43domain_decomposition = split_modes.domain_decomposition
44
46import toolbox.loadbalancing.api.output.Hardcoded as Hardcoded
47import toolbox.loadbalancing.api.strategies.Decomposition as Decomposition
48import toolbox.loadbalancing.api.strategies.SFC as SFC
49import toolbox.loadbalancing.api.strategies.SplitTrees as SplitTrees
50
51PlanVsRunPicture = os.path.join(Here, "35_offline-load-balancing-plan-vs-run.png")
52
53CellsPerAxis = split_modes.CellsPerAxis
54TreeColours = split_modes.TreeColours
55
56
58 """! Split tree and Hardcoded arguments of the tutorial's offline decomposition"""
59 tree = RegularGridGenerator.RegularGridGenerator(max_level=3, dimensions=2)
61 tree,
63 peano_tree=tree, number_of_subtrees=domain_decomposition.NumberOfThreads
64 ),
65 periodic_BC=[True, True],
66 )
67 destination_ranks = Hardcoded.assign_trees_to_ranks_round_Robin(
68 number_of_subtrees=tree.number_of_subtrees, max_ranks=1
69 )
70 configuration, mode = Hardcoded.create_constructor_initialisation_string(
71 tree, destination_ranks, modes=Hardcoded.BottomUp
72 )
73 owner = np.zeros((CellsPerAxis, CellsPerAxis), dtype=int)
74 for x in range(CellsPerAxis):
75 for y in range(CellsPerAxis):
76 owner[y, x] = tree._tree_arrays[2][x][y][1]
77 return owner, configuration
78
79
80def match_subdomains(planned, built):
81 """!
82
83 Number the subdomains of a run like the planned subdomains
84
85 The run does not tell us Peano's tree numbers (see
86 split_modes.read_subdomains()). We therefore give each subdomain of the
87 run the number of the planned subdomain with which it overlaps most.
88
89 """
90 result = -np.ones_like(built)
91 for subdomain in range(domain_decomposition.NumberOfThreads):
92 mask = built == subdomain
93 overlaps = [
94 int((planned[mask] == tree).sum())
95 for tree in range(domain_decomposition.NumberOfThreads)
96 ]
97 result[mask] = int(np.argmax(overlaps))
98 return result
99
100
101def draw(axis, owner, title, show_curve=False, crosses=None):
102 axis.imshow(
103 owner,
104 origin="lower",
105 cmap=ListedColormap(TreeColours),
106 vmin=0,
107 vmax=3,
108 extent=(0, CellsPerAxis, 0, CellsPerAxis),
109 )
110 for k in range(0, CellsPerAxis + 1, 3):
111 width = 1.0 if k % 9 == 0 else 0.3
112 axis.axhline(k, color="black", lw=width)
113 axis.axvline(k, color="black", lw=width)
114 if show_curve:
115 curve = [
116 SFC.Peano_to_Cartesian_2d(i, CellsPerAxis)
117 for i in range(CellsPerAxis * CellsPerAxis)
118 ]
119 axis.plot(
120 [x + 0.5 for x, y in curve],
121 [y + 0.5 for x, y in curve],
122 color="white",
123 lw=0.7,
124 )
125 if crosses is not None:
126 ys, xs = np.nonzero(crosses)
127 axis.scatter(xs + 0.5, ys + 0.5, marker="x", color="black", s=14, lw=0.9)
128 counts = [
129 int((owner == tree).sum())
130 for tree in range(domain_decomposition.NumberOfThreads)
131 ]
132 axis.set_title(
133 title + "\ncells per subdomain: " + " / ".join(str(c) for c in counts),
134 fontsize=10,
135 )
136 axis.set_xticks([])
137 axis.set_yticks([])
138
139
140def render(run_directory, picture=PlanVsRunPicture):
141 planned, configuration = offline_plan()
142 built = match_subdomains(planned, split_modes.read_subdomains(run_directory))
143 differences = planned != built
144
145 figure, axes = plt.subplots(1, 2, figsize=(9.0, 5.6))
146 draw(axes[0], planned, "Offline plan along Peano's curve", show_curve=True)
147 draw(
148 axes[1],
149 built,
150 "Run with bottom-up splits: {} cell(s) differ".format(int(differences.sum())),
151 crosses=differences,
152 )
153 figure.legend(
154 handles=[Patch(color=TreeColours[0], label="tree 0 (keeps the periodic frame)")]
155 + [Patch(color=c, label="") for c in TreeColours[1:-1]]
156 + [Patch(color=TreeColours[-1], label="trees split off from tree 0")],
157 loc="lower center",
158 ncol=4,
159 frameon=False,
160 fontsize=9,
161 columnspacing=0.4,
162 )
163 plt.tight_layout(rect=(0, 0.06, 1, 0.97))
164 plt.savefig(picture, dpi=130, bbox_inches="tight")
165 return int(differences.sum())
166
167
168if __name__ == "__main__":
169 parser = argparse.ArgumentParser(
170 description="Create the plan-vs-run PNG of the tutorial 35_offline-load-balancing.ipynb"
171 )
172 parser.add_argument(
173 "--work-directory",
174 default=None,
175 help="Directory for builds and outputs (default: temporary directory)",
176 )
177 parser.add_argument(
178 "--peano-root",
179 default=domain_decomposition.PeanoRoot,
180 help="Peano installation with the compiled libraries (default: repository holding this script)",
181 )
182 args = parser.parse_args()
183
184 work_directory = args.work_directory or tempfile.mkdtemp(
185 prefix="35_offline-load-balancing-plan-vs-run-"
186 )
187 print("build and run in {}".format(work_directory))
188 planned, configuration = offline_plan()
189 domain_decomposition.build_and_run(
190 work_directory,
191 os.path.abspath(args.peano_root),
192 tutorial="35_offline-load-balancing",
193 namespace="offlineloadbalancing",
194 load_balancer="toolbox::loadbalancing::strategies::Hardcoded",
195 load_balancer_arguments=configuration,
196 )
197 differences = render(work_directory)
198 print("wrote {} ({} cell(s) differ)".format(PlanVsRunPicture, differences))
draw(axis, owner, title, show_curve=False, crosses=None)
offline_plan()
Split tree and Hardcoded arguments of the tutorial's offline decomposition.
match_subdomains(planned, built)
Number the subdomains of a run like the planned subdomains.
equidistant_splitting(Tree peano_tree, int number_of_subtrees, verbose=False)
RegularGridGenerator(name=None, max_level=None, dimensions=None)
Generate an artificial regular grid with uniform refinement for testing purposes.
tuple[int, int] Peano_to_Cartesian_2d(int peano_index, int size)
Inverse of Cartesian_to_Peano_2d.
Definition SFC.py:160
tuple[str, str] split_tree(Tree peano_tree, decomposition, str traversal="bfs_finest_first", bool enable_propagate_bottom_up=False, bool enable_optimize_hierarchy=False, bool require_all_children_for_hierarchy_optimization=True, list[bool]|None periodic_BC=None, bool verbose=False)
Split a tree using a staged load-balancing workflow.
Definition SplitTrees.py:23