# Copyright 2019 David Grote, Maxence Thevenet # # This file is part of WarpX. # # License: BSD-3-Clause-LBNL import argparse import glob import os import sys import matplotlib import yt yt.funcs.mylog.setLevel(50) import matplotlib.pyplot as plt import numpy as np ''' This script loops over all WarpX plotfiles in a directory and, for each plotfile, saves an image showing the field and particles. Requires yt>3.5 and Python3 It can be run serial: > python plot_parallel.py --path --serial or parallel > mpirun -np 32 python plot_parallel.py --path When running parallel, the plotfiles are distributed as evenly as possible between MPI ranks. This script also proposes options to plot quantities over all timesteps. That quantity from of all plotfiles is gathered to rank 0, and the evolution is plotted. The default operation is the max of a specified field. For example, " --plot_evolution Ey" will plot the max of Ey. Also, "--plot_particle_evolution species" for the given species, will plot the RMS x versus the average z. To get help, run > python plot_parallel --help ''' # Parse command line for options. parser = argparse.ArgumentParser() parser.add_argument('--path', default=None, help='path to plotfiles, defaults to diags/plotfiles. Plotfiles names must be plt?????') parser.add_argument('--image_dir', default=None, help='path where images are placed, defaults to diags/plotfiles or path if specified.') parser.add_argument('--plotlib', default='yt', choices=['yt','matplotlib'], help='Plotting library to use') parser.add_argument('--field', default='Ez', help='Which field to plot, e.g., Ez, By, jx or rho. The central slice in y is plotted') parser.add_argument('--pjump', default=20, help='When plotlib=matplotlib, we plot every pjump particle') parser.add_argument('--vmax', type=float, default=None, help='If specified, the colormap will have bounds [-vmax, vmax]') parser.add_argument('--slicewidth', default=10.e-6, help='Only particles with -slicewidth/2 1: global_z = np.empty_like(z) global_q = np.empty_like(q) comm_world.Reduce(z, global_z, op=MPI.MAX) comm_world.Reduce(q, global_q, op=MPI.MAX) return global_z, global_q else: return z, q ### Analysis ### # Get list of plotfiles file_list = glob.glob(os.path.join(path, 'plt?????')) file_list.sort() nfiles = len(file_list) # Get list of particle speciess to plot pslist = get_species(file_list); rank = 0 size = 1 if not args.serial: try: from mpi4py import MPI comm_world = MPI.COMM_WORLD rank = comm_world.Get_rank() size = comm_world.Get_size() except ImportError: pass if rank == 0: print('number of MPI ranks: %d'%size) print('Number of plotfiles: %s'%nfiles) print('list of species: ', pslist) if plot_evolution is not None: # Fill with a value less than any possible value zwin = np.full(nfiles, np.finfo(float).min) quantity = np.full(nfiles, np.finfo(float).min) if plot_particle_evolution is not None: # Fill with a value less than any possible value zbar = np.full(nfiles, np.finfo(float).min) xstd = np.full(nfiles, np.finfo(float).min) # Loop over files, splitting plotfile list among MPI ranks # - plot field snapshot # - store window position and field max in arrays for count, filename in enumerate(file_list): if count%size != rank: continue plot_snapshot( filename ) if plot_evolution is not None: zwin[count], quantity[count] = get_evolution_quantity( filename, plot_evolution ) if plot_particle_evolution is not None: zbar[count], xstd[count] = get_particle_evolution_quantity(filename, plot_particle_evolution) if plot_evolution is not None: zwin, quantity = reduce_evolved_quantity(zwin, quantity) if rank == 0: plot_evolved_quantity(zwin, quantity) if plot_particle_evolution is not None: zbar, xstd = reduce_evolved_quantity(zbar, xstd) if rank == 0: plot_particle_evolved_quantity(zbar, xstd)