aboutsummaryrefslogtreecommitdiff
path: root/Docs/source/usage/workflows/plot_distribution_mapping.rst
blob: 7e22ff6ba19c1069c5f7d89c6f32b6dc95b99143 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
Visualizing a distribution mapping
==================================

WarpX provides via :ref:`reduced diagnostics <dataanalysis-reduced-diagnostics>` an output
:ref:`LoadBalanceCosts <running-cpp-parameters-diagnostics>`, which
allows for visualization of a simulation's distribution mapping and computational
costs. Here we demonstrate the workflow for generating this data and using it to
plot distribution mappings and load balance costs.


Generating the data
-------------------

To generate 'Load Balance Costs' reduced diagnostics output, WarpX should be run
with the following lines added to the input file (the name of the reduced diagnostics
file, `LBC`, and interval in steps to output reduced diagnostics data, `100`, may
be changed as needed):

.. code-block:: python

    warpx.reduced_diags_names = LBC
    LBC.type = LoadBalanceCosts
    LBC.intervals = 100

The line `warpx.reduced_diags_names = LBC` sets the name of the reduced diagnostics
output file to `LBC`.  The next line `LBC.type = LoadBalanceCosts` tells WarpX
that the reduced diagnostics is a `LoadBalanceCosts` diagnostic, and instructs
WarpX to record costs and rank layouts.  The final line, `LBC.intervals = 100`,
controls the interval for output of this reduced diagnostic's data.

Loading and plotting the data
-----------------------------

After generating data (called `LBC_knapsack.txt` and `LBC_sfc.txt` in the example
below), the following Python code, along with a helper class in
:download:`plot_distribution_mapping.py <../../../../Tools/PostProcessing/plot_distribution_mapping.py>`
can be used to read the data:

.. code-block:: python

    # Math
    import numpy as np
    import random

    # Plotting
    import matplotlib.pyplot as plt
    import matplotlib as mpl
    from matplotlib.colors import ListedColormap
    from mpl_toolkits.axes_grid1 import make_axes_locatable

    # Data handling
    import plot_distribution_mapping as pdm

    sim_knapsack = pdm.SimData('LBC_knapsack.txt', # Data directory
                               [2800],             # Files to process
                               is_3D=False         # if this is a 2D sim
                              )
    sim_sfc = pdm.SimData('LBC_sfc.txt', [2800])

    # Set reduced diagnostics data for step 2800
    for sim in [sim_knapsack, sim_sfc]: sim(2800)


For 2D data, the following function can be used for visualization of distribution
mappings:

.. code-block:: python

    # Plotting -- we know beforehand the data is 2D
    def plot(sim):
        """
        Plot MPI rank layout for a set of `LoadBalanceCosts` reduced diagnostics
        (2D) data.

        Arguments:
        sim -- SimData class with data (2D) loaded for desired iteration
        """
        # Make first cmap
        cmap = plt.cm.nipy_spectral
        cmaplist = [cmap(i) for i in range(cmap.N)][::-1]
        unique_ranks = np.unique(sim.rank_arr)
        sz = len(unique_ranks)
        cmap = mpl.colors.LinearSegmentedColormap.from_list(
            'my_cmap', cmaplist, sz) # create the new map

        # Make cmap from 1 --> 96 then randomize
        cmaplist= [cmap(i) for i in range(sz)]
        random.Random(6).shuffle(cmaplist)
        cmap = mpl.colors.LinearSegmentedColormap.from_list(
            'my_cmap', cmaplist, sz) # create the new map

        # Define the bins and normalize
        bounds = np.linspace(0, sz, sz + 1)
        norm = mpl.colors.BoundaryNorm(bounds, sz)

        my, mx = sim.rank_arr.shape
        xcoord, ycoord = np.linspace(0,mx,mx+1), np.linspace(0,my,my+1)
        im = plt.pcolormesh(xcoord, ycoord, sim.rank_arr,
                            cmap=cmap, norm=norm)

        # Grid lines
        plt.ylabel('$j$')
        plt.xlabel('$i$')
        plt.minorticks_on()
        plt.hlines(ycoord, xcoord[0], xcoord[-1],
                   alpha=0.7, linewidth=0.3, color='lightgrey')
        plt.vlines(xcoord, ycoord[0], ycoord[-1],
                   alpha=0.7, linewidth=0.3, color='lightgrey')

        plt.gca().set_aspect('equal')

        # Center rank label
        for j in range(my):
            for i in range(mx):
                text = plt.gca().text(i+0.5, j+0.5, int(sim.rank_arr[j][i]),
                                      ha="center", va="center",
                                      color="w", fontsize=8)

        # Colorbar
        divider = make_axes_locatable(plt.gca())
        cax = divider.new_horizontal(size="5%", pad=0.05)
        plt.gcf().add_axes(cax)
        cb=plt.gcf().colorbar(im, label='rank', cax=cax, orientation="vertical")
        minorticks = np.linspace(0, 1, len(unique_ranks) + 1)
        cb.ax.yaxis.set_ticks(minorticks, minor=True)

The function can be used as follows:

.. code-block:: python

    fig, axs = plt.subplots(1, 2, figsize=(12, 6))
    plt.sca(axs[0])
    plt.title('Knapsack')
    plot(sim_knapsack)
    plt.sca(axs[1])
    plt.title('SFC')
    plot(sim_sfc)
    plt.tight_layout()

This generates plots like in `[fig:knapsack_sfc_distribution_mapping_2D] <#fig:knapsack_sfc_distribution_mapping_2D>`__:

.. raw:: latex

   \centering

.. figure:: knapsack_sfc_distribution_mapping_2D.png
   :alt: Sample distribution mappings from simulations with knapsack (left) and space-filling curve (right) policies for update of the distribution mapping when load balancing.
   :name: fig:knapsack_sfc_distribution_mapping_2D
   :width: 15cm

   Sample distribution mappings from simulations with knapsack (left) and space-filling curve (right) policies for update of the distribution mapping when load balancing.

Similarly, the computational costs per box can be plotted with the following code:

.. code-block:: python

    fig, axs = plt.subplots(1, 2, figsize=(12, 6))
    plt.sca(axs[0])
    plt.title('Knapsack')
    plt.pcolormesh(sim_knapsack.cost_arr)
    plt.sca(axs[1])
    plt.title('SFC')
    plt.pcolormesh(sim_sfc.cost_arr)

    for ax in axs:
        plt.sca(ax)
        plt.ylabel('$j$')
        plt.xlabel('$i$')
        ax.set_aspect('equal')

    plt.tight_layout()

This generates plots like in `[fig:knapsack_sfc_costs_2D] <#fig:knapsack_sfc_costs_2D>`__:

.. raw:: latex

   \centering

.. figure:: knapsack_sfc_costs_2D.png
   :alt: Sample computational cost per box from simulations with knapsack (left) and space-filling curve (right) policies for update of the distribution mapping when load balancing.
   :name: fig:knapsack_sfc_costs_2D
   :width: 15cm

   Sample computational cost per box from simulations with knapsack (left) and space-filling curve (right) policies for update of the distribution mapping when load balancing.

Loading 3D data works the same as loading 2D data, but this time the cost and
rank arrays will be 3 dimensional.  Here we load and plot some example 3D data
(`LBC_3D.txt`) from a simulation run on 4 MPI ranks.  Particles fill the box
from :math:`k=0` to :math:`k=1`.

.. code-block:: python

    sim_3D = pdm.SimData('LBC_3D.txt', [1,2,3])
    sim_3D(1)

    # Plotting -- we know beforehand the data is 3D
    def plot_3D(sim, j0):
        """
        Plot MPI rank layout for a set of `LoadBalanceCosts` reduced diagnostics
        (3D) data.

        Arguments:
        sim -- SimData class with data (3D) loaded for desired iteration
        j0 -- slice along j direction to plot ik slice
        """
        # Make first cmap
        cmap = plt.cm.viridis
        cmaplist = [cmap(i) for i in range(cmap.N)][::-1]
        unique_ranks = np.unique(sim.rank_arr)
        sz = len(unique_ranks)
        cmap = mpl.colors.LinearSegmentedColormap.from_list(
            'my_cmap', cmaplist, sz) # create the new map

        # Make cmap from 1 --> 96 then randomize
        cmaplist= [cmap(i) for i in range(sz)]
        random.Random(6).shuffle(cmaplist)
        cmap = mpl.colors.LinearSegmentedColormap.from_list(
            'my_cmap', cmaplist, sz) # create the new map

        # Define the bins and normalize
        bounds = np.linspace(0, sz, sz + 1)
        norm = mpl.colors.BoundaryNorm(bounds, sz)

        mz, my, mx = sim.rank_arr.shape
        xcoord, ycoord, zcoord = np.linspace(0,mx,mx+1), np.linspace(0,my,my+1),
                                                         np.linspace(0,mz,mz+1)
        im = plt.pcolormesh(xcoord, zcoord, sim.rank_arr[:,j0,:],
                            cmap=cmap, norm=norm)

        # Grid lines
        plt.ylabel('$k$')
        plt.xlabel('$i$')
        plt.minorticks_on()
        plt.hlines(zcoord, xcoord[0], xcoord[-1],
                   alpha=0.7, linewidth=0.3, color='lightgrey')
        plt.vlines(xcoord, zcoord[0], zcoord[-1],
                   alpha=0.7, linewidth=0.3, color='lightgrey')

        plt.gca().set_aspect('equal')

        # Center rank label
        for k in range(mz):
            for i in range(mx):
                text = plt.gca().text(i+0.5, k+0.5, int(sim.rank_arr[k][j0][i]),
                                      ha="center", va="center",
                                      color="red", fontsize=8)

        # Colorbar
        divider = make_axes_locatable(plt.gca())
        cax = divider.new_horizontal(size="5%", pad=0.05)
        plt.gcf().add_axes(cax)
        cb=plt.gcf().colorbar(im, label='rank', cax=cax, orientation="vertical")
        ticks = np.linspace(0, 1, len(unique_ranks)+1)
        cb.ax.yaxis.set_ticks(ticks)
        cb.ax.yaxis.set_ticklabels([0, 1, 2, 3, " "])

    fig, axs = plt.subplots(2, 2, figsize=(8, 8))
    for j,ax in enumerate(axs.flatten()):
        plt.sca(ax)
        plt.title('j={}'.format(j))
        plot_3D(sim_3D, j)
        plt.tight_layout()

This generates plots like in `[fig:distribution_mapping_3D] <#fig:distribution_mapping_3D>`__:

.. raw:: latex

   \centering

.. figure:: distribution_mapping_3D.png
   :alt: Sample distribution mappings from 3D simulations, visualized as slices in the :math:`ik` plane along :math:`j`.
   :name: fig:distribution_mapping_3D
   :width: 15cm

   Sample distribution mappings from 3D simulations, visualized as slices in the :math:`ik` plane along :math:`j`.