Source code for opal.visualization.ProfilingPlotter

# Copyright (c) 2017 - 2019, Matthias Frey, Paul Scherrer Institut, Villigen PSI, Switzerland
# All rights reserved
#
# Implemented as part of the PhD thesis
# "Precise Simulations of Multibunches in High Intensity Cyclotrons"
#
# This file is part of pyOPALTools.
#
# pyOPALTools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.

# You should have received a copy of the GNU General Public License
# along with pyOPALTools. If not, see <https://www.gnu.org/licenses/>.

from .BasePlotter import *
import numpy as np


[docs]class ProfilingPlotter(BasePlotter):
[docs] def __init__(self): pass
[docs] def plot_lbal_histogram(self, **kwargs): """Particle load balancing. Plots the time series (i.e. simulation time) histogram with the number of cores having the same amount of particles. The user can specify ranges givin the upper and lower boundary, i.e. 'bupper' and, respectively, 'blower'. Those boundaries are given in percent. """ try: grid = kwargs.pop('grid', False) title = kwargs.pop('title', None) yscale = kwargs.pop('yscale', 'linear') xscale = kwargs.pop('xscale', 'linear') blower = kwargs.pop('blower', [0.0, 0.0, 25.0, 50.0, 75.0]) bupper = kwargs.pop('bupper', [0.0, 25.0, 50.0, 75.0, 100.0]) if not len(blower) == len(bupper): raise ValueError('len(blower) != len(bupper)') nTotal = len(self.ds.getVariables()) nCols = sum('processor' in var for var in self.ds.getVariables()) time = self.ds.getData('time') nRows = len(time) # iterate through all steps and do a boxplot colStart = nTotal - nCols colEnd = nCols + 1 # percentages with respect to expected average number p / t # where p is the number of particles per processes and t the total # number of particles stamps = np.empty([nRows, len(blower)], dtype=float) p = 100.0 / nCols # in percent [%] # each row is a time stamp for r in range(0, nRows): stamp = np.empty([nCols,], dtype=float) for c in range(colStart, colEnd): cc = c - colStart stamp[cc] = float(self.ds.getData('processor-' + str(cc))[r]) # total number of particles total = sum(stamp) # percentage [] stamp /= total * 0.01 # in % # check bin for i in range(0, len(blower)): if blower[i] == bupper[i]: stamps[r, i] = ((blower[i] <= stamp) & (stamp <= bupper[i])).sum() else: stamps[r, i] = ((blower[i] < stamp) & (stamp <= bupper[i])).sum() for i in range(0, len(blower)): common = str(blower[i]) + ', ' + str(bupper[i]) + '] %' lab = ']' + common if blower[i] == bupper[i]: lab = '[' + common plt.plot(time, stamps[:, i], label=lab) plt.xlabel(self.ds.getLabelWithUnit('time')) plt.xscale(xscale) plt.ylabel('#cores') plt.yscale(yscale) plt.legend() plt.grid(grid, which='both') if title: plt.title(title) plt.tight_layout() return plt except Exception as ex: opal_logger.exception(ex) return plt.figure()
[docs] def plot_lbal_summary(self, **kwargs): """Particle load balancing. Plot the minimum, maximum and average number of particles per core vs. the simulation time. """ try: grid = kwargs.pop('grid', False) title = kwargs.pop('title', None) yscale = kwargs.pop('yscale', 'linear') xscale = kwargs.pop('xscale', 'linear') nTotal = len(self.ds.getVariables()) nCols = sum('processor' in var for var in self.ds.getVariables()) time = self.ds.getData('time') nRows = len(time) # iterate through all steps colStart = nTotal - nCols colEnd = nCols + 1 # each row is a time stamp minimum = [] maximum = [] mean = [] for r in range(0, nRows): stamp = np.empty([nCols,], dtype=float) for c in range(colStart, colEnd): cc = c - colStart stamp[cc] = self.ds.getData('processor-' + str(cc))[r] minimum.append(min(stamp)) mean.append(np.mean(stamp)) maximum.append(max(stamp)) plt.plot(time, minimum, label='minimum') plt.plot(time, maximum, label='maximum') plt.plot(time, mean, label='mean') plt.xlabel(self.ds.getLabelWithUnit('time')) plt.xscale(xscale) plt.ylabel('#particles') plt.yscale(yscale) plt.legend() plt.grid(grid, which='both') if title: plt.title(title) plt.tight_layout() return plt except Exception as ex: opal_logger.exception(ex) return plt.figure()
[docs] def plot_lbal_boxplot(self, **kwargs): """Particle load balancing. Does a (simulation) time series boxplot of the particle load balancing. """ try: grid = kwargs.pop('grid', False) title = kwargs.pop('title', None) yscale = kwargs.pop('yscale', 'linear') xscale = kwargs.pop('xscale', 'linear') nTotal = len(self.ds.getVariables()) nCols = sum('processor' in var for var in self.ds.getVariables()) time = self.ds.getData('time') nRows = len(time) # iterate through all steps and do a boxplot colStart = nTotal - nCols colEnd = nCols + 1 # each row is a time stamp stamps = [] for r in range(0, nRows): stamp = np.empty([nCols,], dtype=float) for c in range(colStart, colEnd): cc = c - colStart stamp[cc] = float(self.ds.getData('processor-' + str(cc))[r]) stamps.append(stamp) if xscale == 'log': # 24. Dec. 2017 # https://stackoverflow.com/questions/19328537/check-array-for-values-equal-or-very-close-to-zero # https://stackoverflow.com/questions/19141432/python-numpy-machine-epsilon if np.any(np.absolute(time) < np.finfo(float).eps): opal_logger.warning('Entry close to zero. Switching to linear x scale') xscale='linear' plt.boxplot(stamps, 0, '', positions=time) plt.xlabel(self.ds.getLabelWithUnit('time')) plt.xscale(xscale) plt.ylabel('#particles') plt.yscale(yscale) plt.grid(grid, which='both') if title: plt.title(title) plt.tight_layout() return plt except Exception as ex: opal_logger.exception(ex) return plt.figure()