xref: /libCEED/benchmarks/postprocess_plot.py (revision d4cc18453651bd0f94c1a2e078b2646a92dafdcc)
1d13e9b48SJed Brown#!/usr/bin/env python3
25cd6c1fbSSebastian Grimberg
3*9ba83ac0SJeremy L Thompson# Copyright (c) 2017-2026, Lawrence Livermore National Security, LLC and other CEED contributors.
45aed82e4SJeremy L Thompson# All Rights Reserved. See the top-level LICENSE and NOTICE files for details.
5d13e9b48SJed Brown#
65aed82e4SJeremy L Thompson# SPDX-License-Identifier: BSD-2-Clause
7d13e9b48SJed Brown#
85aed82e4SJeremy L Thompson# This file is part of CEED:  http://github.com/ceed
9d13e9b48SJed Brown
10dec49e00SJed Brownfrom pylab import *
11dec49e00SJed Brownfrom matplotlib import use
12dec49e00SJed Brownfrom postprocess_base import read_logs
13dec49e00SJed Brownimport pandas as pd
145cd6c1fbSSebastian Grimberg
155cd6c1fbSSebastian Grimberg# Adjustable plot parameters
16d13e9b48SJed Brownlog_y = 0               # use log scale on the y-axis?
17d13e9b48SJed Brownx_range = (1e1, 4e6)     # plot range for the x-axis; comment out for auto
18d13e9b48SJed Browny_range = (0, 2e9)       # plot range for the y-axis; comment out for auto
19d13e9b48SJed Browndraw_iter_lines = 0     # draw the "iter/s" lines?
20d13e9b48SJed Brownymin_iter_lines = 3e5   # minimal y value for the "iter/s" lines
21d13e9b48SJed Brownymax_iter_lines = 8e8   # maximal y value for the "iter/s" lines
22d13e9b48SJed Brownlegend_ncol = (2 if log_y else 1)   # number of columns in the legend
23d13e9b48SJed Brownwrite_figures = 1       # save the figures to files?
24d13e9b48SJed Brownshow_figures = 1        # display the figures on the screen?
25d13e9b48SJed Brown
26dec49e00SJed Brown# Load the data
27d13e9b48SJed Brownruns = read_logs()
28d13e9b48SJed Brown
29dec49e00SJed Brown# Sample plot output
30d13e9b48SJed Brownif not show_figures:
31d13e9b48SJed Brown    use('pdf')
32d13e9b48SJed Brown
33d13e9b48SJed BrownrcParams['font.sans-serif'].insert(0, 'Noto Sans')
34d13e9b48SJed BrownrcParams['font.sans-serif'].insert(1, 'Open Sans')
35d13e9b48SJed BrownrcParams['figure.figsize'] = [10, 8]  # default: 8 x 6
36d13e9b48SJed Brown
37d13e9b48SJed Browncm_size = 16
38d13e9b48SJed Browncolors = ['dimgrey', 'black', 'saddlebrown', 'firebrick', 'red', 'orange',
39d13e9b48SJed Brown          'gold', 'lightgreen', 'green', 'cyan', 'teal', 'blue', 'navy',
40d13e9b48SJed Brown          'purple', 'magenta', 'pink']
41d13e9b48SJed Brown
42dec49e00SJed Brown# Get test names
43d13e9b48SJed Brownsel_runs = runs
44d13e9b48SJed Browntests = list(sel_runs.test.unique())
45d13e9b48SJed Browntest = tests[0]
46d13e9b48SJed Brown
47dec49e00SJed Brown# Run information
48d13e9b48SJed Brownprint('Using test:', test)
49d13e9b48SJed Brown
50d13e9b48SJed Brownif 'CEED Benchmark Problem' in test:
51d13e9b48SJed Brown    test_short = test.strip().split()[0] + ' BP' + test.strip().split()[-1]
52d13e9b48SJed Brown
53dec49e00SJed Brown# Plot same BP
54d13e9b48SJed Brownsel_runs = sel_runs.loc[sel_runs['test'] == test]
55d13e9b48SJed Brown
56dec49e00SJed Brown# Plot same case (scalar vs vector)
57d13e9b48SJed Browncases = list(sel_runs.case.unique())
58d13e9b48SJed Browncase = cases[0]
59d13e9b48SJed Brownvdim = 1 if case == 'scalar' else 3
60d13e9b48SJed Brownprint('Using case:', case)
61d13e9b48SJed Brownsel_runs = sel_runs.loc[sel_runs['case'] == case]
62d13e9b48SJed Brown
63dec49e00SJed Brown# Plot same 'code'
64d13e9b48SJed Browncodes = list(sel_runs.code.unique())
65d13e9b48SJed Browncode = codes[0]
66d13e9b48SJed Brownsel_runs = sel_runs.loc[sel_runs['code'] == code]
67d13e9b48SJed Brown
68dec49e00SJed Brown# Group plots by backend and number of processes
69981e58aeSvaleriabarrapl_set = sel_runs[['backend',
70981e58aeSvaleriabarra                   'backend_memtype',
71981e58aeSvaleriabarra                   'num_procs',
72981e58aeSvaleriabarra                   'num_procs_node']]
73d13e9b48SJed Brownpl_set = pl_set.drop_duplicates()
74d13e9b48SJed Brown
75dec49e00SJed Brown# Plotting
76d13e9b48SJed Brownfor index, row in pl_set.iterrows():
77d13e9b48SJed Brown    backend = row['backend']
78d13e9b48SJed Brown    backend_memtype = row['backend_memtype']
79d13e9b48SJed Brown    num_procs = float(row['num_procs'])
80d13e9b48SJed Brown    num_procs_node = float(row['num_procs_node'])
81d13e9b48SJed Brown    num_nodes = num_procs / num_procs_node
82d13e9b48SJed Brown    pl_runs = sel_runs[(sel_runs.backend == backend) |
83d13e9b48SJed Brown                       (sel_runs.num_procs == num_procs) |
84d13e9b48SJed Brown                       (sel_runs.num_procs_node == num_procs_node)]
85d13e9b48SJed Brown    if len(pl_runs.index) == 0:
86d13e9b48SJed Brown        continue
87d13e9b48SJed Brown
88d13e9b48SJed Brown    print('backend: %s, compute nodes: %i, number of MPI tasks = %i' % (
89d13e9b48SJed Brown        backend, num_nodes, num_procs))
90d13e9b48SJed Brown
91d13e9b48SJed Brown    figure()
92d13e9b48SJed Brown    i = 0
93d13e9b48SJed Brown    sol_p_set = sel_runs['degree'].drop_duplicates()
94d13e9b48SJed Brown    sol_p_set = sol_p_set.sort_values()
95dec49e00SJed Brown    # Iterate over P
96d13e9b48SJed Brown    for sol_p in sol_p_set:
97d13e9b48SJed Brown        qpts = sel_runs['quadrature_pts'].loc[pl_runs['degree'] == sol_p]
98d13e9b48SJed Brown        qpts = qpts.drop_duplicates().sort_values(ascending=False)
99d13e9b48SJed Brown        qpts = qpts.reset_index(drop=True)
100d13e9b48SJed Brown        print('Degree: %i, quadrature points:' % sol_p, qpts[0])
101d13e9b48SJed Brown        # Generate plot data
102d13e9b48SJed Brown        d = [[run['degree'], run['num_elem'], 1. * run['num_unknowns'] / num_nodes / vdim,
103d13e9b48SJed Brown              run['cg_iteration_dps'] / num_nodes]
104d13e9b48SJed Brown             for index, run in
105d13e9b48SJed Brown             pl_runs.loc[(pl_runs['degree'] == sol_p) |
106d13e9b48SJed Brown                         (pl_runs['quadrature_pts'] == qpts[0])].iterrows()]
107d13e9b48SJed Brown        d = [[e[2], e[3]] for e in d if e[0] == sol_p]
108d13e9b48SJed Brown        # (DOFs/[sec/iter]/node)/(DOFs/node) = iter/sec
109d13e9b48SJed Brown        d = [[nun,
110d13e9b48SJed Brown              min([e[1] for e in d if e[0] == nun]),
111d13e9b48SJed Brown              max([e[1] for e in d if e[0] == nun])]
112d13e9b48SJed Brown             for nun in set([e[0] for e in d])]
113d13e9b48SJed Brown        d = asarray(sorted(d))
114d13e9b48SJed Brown        # Plot
115d13e9b48SJed Brown        plot(d[:, 0], d[:, 2], 'o-', color=colors[i % cm_size],
116d13e9b48SJed Brown             label='p=%i' % sol_p)
117d13e9b48SJed Brown        if list(d[:, 1]) != list(d[:, 2]):
118d13e9b48SJed Brown            plot(d[:, 0], d[:, 1], 'o-', color=colors[i])
119dec49e00SJed Brown            fill_between(d[:, 0], d[:, 1], d[:, 2],
120dec49e00SJed Brown                         facecolor=colors[i], alpha=0.2)
121d13e9b48SJed Brown        # Continue if only 1 set of qpts
122d13e9b48SJed Brown        if len(qpts) == 1:
123d13e9b48SJed Brown            i = i + 1
124d13e9b48SJed Brown            continue
125d13e9b48SJed Brown        # Second set of qpts
126d13e9b48SJed Brown        d = [[run['degree'], run['num_elem'], 1. * run['num_unknowns'] / num_nodes / vdim,
127d13e9b48SJed Brown              run['cg_iteration_dps'] / num_nodes]
128d13e9b48SJed Brown             for index, run in
129d13e9b48SJed Brown             pl_runs.loc[(pl_runs['degree'] == sol_p) |
130d13e9b48SJed Brown                         (pl_runs['quadrature_pts'] == qpts[1])].iterrows()]
131d13e9b48SJed Brown        d = [[e[2], e[3]] for e in d if e[0] == sol_p]
132d13e9b48SJed Brown        if len(d) == 0:
133d13e9b48SJed Brown            i = i + 1
134d13e9b48SJed Brown            continue
135d13e9b48SJed Brown        d = [[nun,
136d13e9b48SJed Brown              min([e[1] for e in d if e[0] == nun]),
137d13e9b48SJed Brown              max([e[1] for e in d if e[0] == nun])]
138d13e9b48SJed Brown             for nun in set([e[0] for e in d])]
139d13e9b48SJed Brown        d = asarray(sorted(d))
140d13e9b48SJed Brown        plot(d[:, 0], d[:, 2], 's--', color=colors[i],
141d13e9b48SJed Brown             label='p=%i' % sol_p)
142d13e9b48SJed Brown        if list(d[:, 1]) != list(d[:, 2]):
143d13e9b48SJed Brown            plot(d[:, 0], d[:, 1], 's--', color=colors[i])
144d13e9b48SJed Brown        ##
145d13e9b48SJed Brown        i = i + 1
146d13e9b48SJed Brown    ##
147d13e9b48SJed Brown    if draw_iter_lines:
148d13e9b48SJed Brown        y0, y1 = ymin_iter_lines, ymax_iter_lines
149d13e9b48SJed Brown        y = asarray([y0, y1]) if log_y else exp(linspace(log(y0), log(y1)))
150d13e9b48SJed Brown        slope1 = 600.
151d13e9b48SJed Brown        slope2 = 6000.
152d13e9b48SJed Brown        plot(y / slope1, y, 'k--', label='%g iter/s' % (slope1 / vdim))
153d13e9b48SJed Brown        plot(y / slope2, y, 'k-', label='%g iter/s' % (slope2 / vdim))
154d13e9b48SJed Brown
155d13e9b48SJed Brown    # Plot information
156d13e9b48SJed Brown    title(r'%i node%s $\times$ %i ranks, %s, %s, %s' % (
157d13e9b48SJed Brown          num_nodes, '' if num_nodes == 1 else 's',
158d13e9b48SJed Brown          num_procs_node, backend, backend_memtype, test_short), fontsize=16)
159d13e9b48SJed Brown    xscale('log')  # subsx=[2,4,6,8]
160d13e9b48SJed Brown    if log_y:
161d13e9b48SJed Brown        yscale('log')
162d13e9b48SJed Brown    if 'x_range' in vars() and len(x_range) == 2:
163d13e9b48SJed Brown        xlim(x_range)
164d13e9b48SJed Brown    if 'y_range' in vars() and len(y_range) == 2:
165d13e9b48SJed Brown        ylim(y_range)
166d13e9b48SJed Brown    grid('on', color='gray', ls='dotted')
167d13e9b48SJed Brown    grid('on', axis='both', which='minor', color='gray', ls='dotted')
168d13e9b48SJed Brown    plt.tick_params(labelsize=14)
169d13e9b48SJed Brown    exptext = gca().yaxis.get_offset_text()
170d13e9b48SJed Brown    exptext.set_size(14)
171d13e9b48SJed Brown    gca().set_axisbelow(True)
172d13e9b48SJed Brown    xlabel('Points per compute node', fontsize=14)
173d13e9b48SJed Brown    ylabel('[DOFs x CG iterations] / [compute nodes x seconds]', fontsize=14)
174d13e9b48SJed Brown    legend(ncol=legend_ncol, loc='best', fontsize=13)
175d13e9b48SJed Brown
176d13e9b48SJed Brown    # Write
177d13e9b48SJed Brown    if write_figures:  # write .pdf file?
178d13e9b48SJed Brown        short_backend = backend.replace('/', '')
179d13e9b48SJed Brown        test_short_save = test_short.replace(' ', '')
180d13e9b48SJed Brown        pdf_file = 'plot_%s_%s_%s_%s_N%03i_pn%i.pdf' % (
181d13e9b48SJed Brown            code, test_short_save, short_backend, backend_memtype, num_nodes, num_procs_node)
182d13e9b48SJed Brown        print('\nsaving figure --> %s' % pdf_file)
183d13e9b48SJed Brown        savefig(pdf_file, format='pdf', bbox_inches='tight')
184d13e9b48SJed Brown
185d13e9b48SJed Brownif show_figures:  # show the figures?
186d13e9b48SJed Brown    print('\nShowing figures ...')
187d13e9b48SJed Brown    show()
188