From fa35c44a7246189d76efeac46795132344d17c08 Mon Sep 17 00:00:00 2001 From: Alfred Mishi Date: Thu, 21 Mar 2024 16:17:23 -0700 Subject: [PATCH 01/15] add viz scripts to beam-beam collision example --- .../beam-beam_collision/README.rst | 193 +++++++++++++++++- .../beam-beam_collision/plot_full.py | 118 +++++++++++ .../beam-beam_collision/plot_reduced.py | 49 +++++ 3 files changed, 358 insertions(+), 2 deletions(-) create mode 100644 Examples/Physics_applications/beam-beam_collision/plot_full.py create mode 100644 Examples/Physics_applications/beam-beam_collision/plot_reduced.py diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index 559a81277db..e0ce6eb6a45 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -32,9 +32,8 @@ For `MPI-parallel `__ runs, prefix these lines with ` Visualize --------- -The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). +The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). We present the reduced diagnostics and then further compare different results for the reduced diagnostics with some literature: -We compare different results: * (red) simplified WarpX simulation as the example stored in the directory ``/Examples/Physics_applications/beam-beam_collision``; * (blue) large-scale WarpX simulation (high resolution and ad hoc generated tables ; * (black) literature results from :cite:t:`ex-Yakimenko2019`. @@ -68,3 +67,193 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny :width: 100% Beam-beam collision benchmark against :cite:t:`ex-Yakimenko2019`. + +.. tab-set:: + + .. tab-item:: Full Diagnostics + + This example can be run as a Python file in the same directory: + + - **Python** script: ``python3 Plot_full_.py`` + + .. code-block:: python + + # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_full_.py + # Contents of Plot_full_.py + from openpmd_viewer import OpenPMDTimeSeries + import numpy as np + import matplotlib.pyplot as plt + from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable + + series = OpenPMDTimeSeries('./diags/diag1') + steps = series.iterations + print(steps) + + ylabel = 'y [m]' + xlabel = 'z [m]' + #slice_axis = 'x' + slice_axis = 'y' + + #loop through E,B,Rho + for n in steps: + + fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) + + #E field + Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) + Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + #B field + Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) + By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + # Rho + rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) + rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) + rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) + rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) + rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) + rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) + + xmin = info.z.min() + xmax = info.z.max() + if slice_axis == 'x': + ymin = info.y.min() + ymax = info.y.max() + elif slice_axis == 'y': + ymin = info.x.min() + ymax = info.x.max() + + + #E field plots + im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,0].set_title(f'E$_x$', fontsize=20) + divider1 = make_axes_locatable(ax[0,0]) + cax = divider1.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im1, cax=cax, orientation='vertical') + + + im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,1].set_title(f'E$_y$', fontsize=20) + divider2 = make_axes_locatable(ax[0,1]) + cax = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im2, cax=cax, orientation='vertical') + + im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,2].set_title(f'E$_z$', fontsize=20) + divider3 = make_axes_locatable(ax[0,2]) + cax = divider3.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im3, cax=cax, orientation='vertical') + + #B field plots + im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,0].set_title(f'B$_x$', fontsize=20) + divider4 = make_axes_locatable(ax[1,0]) + cax = divider4.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im4, cax=cax, orientation='vertical') + + + im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,1].set_title(f'B$_y$', fontsize=20) + divider5 = make_axes_locatable(ax[1,1]) + cax = divider5.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im5, cax=cax, orientation='vertical') + + im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,2].set_title(f'B$_z$', fontsize=20) + divider6 = make_axes_locatable(ax[1,2]) + cax = divider6.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im6, cax=cax, orientation='vertical') + + #Rho plots + im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) + divider7 = make_axes_locatable(ax[0,3]) + cax = divider7.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im7, cax=cax, orientation='vertical') + + + im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) + divider8 = make_axes_locatable(ax[1,3]) + cax = divider8.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im8, cax=cax, orientation='vertical') + + + ax[1,0].set_ylabel(ylabel, fontsize=20) + ax[0,0].set_ylabel(ylabel, fontsize=20) + ax[1,1].set_xlabel(xlabel, fontsize=20) + ax[1,2].set_xlabel(xlabel, fontsize=20) + ax[1,3].set_xlabel(xlabel, fontsize=20) + ax[1,0].set_xlabel(xlabel, fontsize=20) + + + fig.suptitle(f'Iteration {n:0}', fontsize=20) + plt.tight_layout() + + image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' + plt.savefig(image_file_name, dpi=100, bbox_inches='tight') + plt.close() + + .. tab-item:: Reduced Diagnostics + + This example can be run as a Python file in the same directory: + + - **Python** script: ``python3 Plot_reduced_.py`` + + .. code-block:: python + + # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_reduced_.py + # Contents of Plot_reduced_.py + import numpy as np + import matplotlib.pyplot as plt + from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano + from matplotlib import use, cm + import matplotlib.colors + import pandas as pd + + r_e = physical_constants['classical electron radius'][0] + my_dpi=300 + sigmaz = 10*nano + + fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) + + rdir= './diags/reducedfiles/' + + df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) + df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) + + + times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() + steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() + + x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() + coll_index = np.argmax(x) + coll_time = times[coll_index] + + # number of photons per beam particle + np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() + np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() + Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] + Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] + + ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) + ax[0].set_title(r'photon number/beam particle') + + # number of NLBW particles per beam particle + e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() + e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() + + ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) + ax[1].set_title(r'NLBW particles/beam particle') + + for a in ax.reshape(-1): + a.set_xlabel(r'time [$\sigma_z/c$]') + image_file_name ='reduced.png' + plt.tight_layout() + plt.savefig(image_file_name,dpi=300, bbox_inches='tight') + plt.close("all") + + diff --git a/Examples/Physics_applications/beam-beam_collision/plot_full.py b/Examples/Physics_applications/beam-beam_collision/plot_full.py new file mode 100644 index 00000000000..8c6d687ec08 --- /dev/null +++ b/Examples/Physics_applications/beam-beam_collision/plot_full.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python3 + +from openpmd_viewer import OpenPMDTimeSeries +import numpy as np +import matplotlib.pyplot as plt +from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable + +series = OpenPMDTimeSeries('./diags/diag1') +steps = series.iterations +print(steps) + +ylabel = 'y [m]' +xlabel = 'z [m]' +#slice_axis = 'x' +slice_axis = 'y' + +#loop through E,B,Rho +for n in steps: + + fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) + + #E field + Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) + Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + #B field + Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) + By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + # Rho + rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) + rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) + rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) + rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) + rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) + rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) + + xmin = info.z.min() + xmax = info.z.max() + if slice_axis == 'x': + ymin = info.y.min() + ymax = info.y.max() + elif slice_axis == 'y': + ymin = info.x.min() + ymax = info.x.max() + + + #E field plots + im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,0].set_title(f'E$_x$', fontsize=20) + divider1 = make_axes_locatable(ax[0,0]) + cax = divider1.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im1, cax=cax, orientation='vertical') + + + im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,1].set_title(f'E$_y$', fontsize=20) + divider2 = make_axes_locatable(ax[0,1]) + cax = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im2, cax=cax, orientation='vertical') + + im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,2].set_title(f'E$_z$', fontsize=20) + divider3 = make_axes_locatable(ax[0,2]) + cax = divider3.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im3, cax=cax, orientation='vertical') + + #B field plots + im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,0].set_title(f'B$_x$', fontsize=20) + divider4 = make_axes_locatable(ax[1,0]) + cax = divider4.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im4, cax=cax, orientation='vertical') + + + im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,1].set_title(f'B$_y$', fontsize=20) + divider5 = make_axes_locatable(ax[1,1]) + cax = divider5.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im5, cax=cax, orientation='vertical') + + im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,2].set_title(f'B$_z$', fontsize=20) + divider6 = make_axes_locatable(ax[1,2]) + cax = divider6.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im6, cax=cax, orientation='vertical') + + #Rho plots + im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) + divider7 = make_axes_locatable(ax[0,3]) + cax = divider7.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im7, cax=cax, orientation='vertical') + + + im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) + divider8 = make_axes_locatable(ax[1,3]) + cax = divider8.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im8, cax=cax, orientation='vertical') + + + ax[1,0].set_ylabel(ylabel, fontsize=20) + ax[0,0].set_ylabel(ylabel, fontsize=20) + ax[1,1].set_xlabel(xlabel, fontsize=20) + ax[1,2].set_xlabel(xlabel, fontsize=20) + ax[1,3].set_xlabel(xlabel, fontsize=20) + ax[1,0].set_xlabel(xlabel, fontsize=20) + + + fig.suptitle(f'Iteration {n:0}', fontsize=20) + plt.tight_layout() + + image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' + plt.savefig(image_file_name, dpi=100, bbox_inches='tight') + plt.close() diff --git a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py new file mode 100644 index 00000000000..19e12b17999 --- /dev/null +++ b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py @@ -0,0 +1,49 @@ + +import numpy as np +import matplotlib.pyplot as plt +from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano +from matplotlib import use, cm +import matplotlib.colors +import pandas as pd + +r_e = physical_constants['classical electron radius'][0] +my_dpi=300 +sigmaz = 10*nano + +fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) + +rdir= './diags/reducedfiles/' + +df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) +df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) + + +times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() +steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() + +x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() +coll_index = np.argmax(x) +coll_time = times[coll_index] + +# number of photons per beam particle +np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() +np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() +Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] +Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] + +ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) +ax[0].set_title(r'photon number/beam particle') + +# number of NLBW particles per beam particle +e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() +e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() + +ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) +ax[1].set_title(r'NLBW particles/beam particle') + +for a in ax.reshape(-1): + a.set_xlabel(r'time [$\sigma_z/c$]') +image_file_name ='reduced.png' +plt.tight_layout() +plt.savefig(image_file_name,dpi=300, bbox_inches='tight') +plt.close("all") From bc146f9c38ef9141c3e59eeb2fe528ea9e385dd3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 21 Mar 2024 23:34:35 +0000 Subject: [PATCH 02/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../beam-beam_collision/README.rst | 306 +++++++++--------- .../beam-beam_collision/plot_full.py | 60 ++-- .../beam-beam_collision/plot_reduced.py | 30 +- 3 files changed, 203 insertions(+), 193 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index e0ce6eb6a45..aa3471ce761 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -80,122 +80,122 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_full_.py # Contents of Plot_full_.py - from openpmd_viewer import OpenPMDTimeSeries - import numpy as np - import matplotlib.pyplot as plt - from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable - - series = OpenPMDTimeSeries('./diags/diag1') - steps = series.iterations - print(steps) - - ylabel = 'y [m]' - xlabel = 'z [m]' - #slice_axis = 'x' - slice_axis = 'y' - - #loop through E,B,Rho - for n in steps: - - fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) - - #E field - Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) - Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) - - #B field - Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) - By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) - - # Rho - rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) - rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) - rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) - rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) - rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) - rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) - - xmin = info.z.min() - xmax = info.z.max() - if slice_axis == 'x': - ymin = info.y.min() - ymax = info.y.max() - elif slice_axis == 'y': - ymin = info.x.min() - ymax = info.x.max() - - - #E field plots - im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,0].set_title(f'E$_x$', fontsize=20) - divider1 = make_axes_locatable(ax[0,0]) - cax = divider1.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im1, cax=cax, orientation='vertical') - - - im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,1].set_title(f'E$_y$', fontsize=20) - divider2 = make_axes_locatable(ax[0,1]) - cax = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im2, cax=cax, orientation='vertical') - - im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,2].set_title(f'E$_z$', fontsize=20) - divider3 = make_axes_locatable(ax[0,2]) - cax = divider3.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im3, cax=cax, orientation='vertical') - - #B field plots - im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,0].set_title(f'B$_x$', fontsize=20) - divider4 = make_axes_locatable(ax[1,0]) - cax = divider4.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im4, cax=cax, orientation='vertical') - - - im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,1].set_title(f'B$_y$', fontsize=20) - divider5 = make_axes_locatable(ax[1,1]) - cax = divider5.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im5, cax=cax, orientation='vertical') - - im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,2].set_title(f'B$_z$', fontsize=20) - divider6 = make_axes_locatable(ax[1,2]) - cax = divider6.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im6, cax=cax, orientation='vertical') - - #Rho plots - im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) - divider7 = make_axes_locatable(ax[0,3]) - cax = divider7.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im7, cax=cax, orientation='vertical') - - - im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) - divider8 = make_axes_locatable(ax[1,3]) - cax = divider8.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im8, cax=cax, orientation='vertical') - - - ax[1,0].set_ylabel(ylabel, fontsize=20) - ax[0,0].set_ylabel(ylabel, fontsize=20) - ax[1,1].set_xlabel(xlabel, fontsize=20) - ax[1,2].set_xlabel(xlabel, fontsize=20) - ax[1,3].set_xlabel(xlabel, fontsize=20) - ax[1,0].set_xlabel(xlabel, fontsize=20) - - - fig.suptitle(f'Iteration {n:0}', fontsize=20) - plt.tight_layout() - - image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' - plt.savefig(image_file_name, dpi=100, bbox_inches='tight') - plt.close() + from openpmd_viewer import OpenPMDTimeSeries + import numpy as np + import matplotlib.pyplot as plt + from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable + + series = OpenPMDTimeSeries('./diags/diag1') + steps = series.iterations + print(steps) + + ylabel = 'y [m]' + xlabel = 'z [m]' + #slice_axis = 'x' + slice_axis = 'y' + + #loop through E,B,Rho + for n in steps: + + fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) + + #E field + Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) + Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + #B field + Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) + By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + # Rho + rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) + rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) + rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) + rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) + rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) + rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) + + xmin = info.z.min() + xmax = info.z.max() + if slice_axis == 'x': + ymin = info.y.min() + ymax = info.y.max() + elif slice_axis == 'y': + ymin = info.x.min() + ymax = info.x.max() + + + #E field plots + im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,0].set_title(f'E$_x$', fontsize=20) + divider1 = make_axes_locatable(ax[0,0]) + cax = divider1.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im1, cax=cax, orientation='vertical') + + + im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,1].set_title(f'E$_y$', fontsize=20) + divider2 = make_axes_locatable(ax[0,1]) + cax = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im2, cax=cax, orientation='vertical') + + im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,2].set_title(f'E$_z$', fontsize=20) + divider3 = make_axes_locatable(ax[0,2]) + cax = divider3.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im3, cax=cax, orientation='vertical') + + #B field plots + im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,0].set_title(f'B$_x$', fontsize=20) + divider4 = make_axes_locatable(ax[1,0]) + cax = divider4.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im4, cax=cax, orientation='vertical') + + + im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,1].set_title(f'B$_y$', fontsize=20) + divider5 = make_axes_locatable(ax[1,1]) + cax = divider5.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im5, cax=cax, orientation='vertical') + + im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,2].set_title(f'B$_z$', fontsize=20) + divider6 = make_axes_locatable(ax[1,2]) + cax = divider6.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im6, cax=cax, orientation='vertical') + + #Rho plots + im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) + divider7 = make_axes_locatable(ax[0,3]) + cax = divider7.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im7, cax=cax, orientation='vertical') + + + im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) + divider8 = make_axes_locatable(ax[1,3]) + cax = divider8.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im8, cax=cax, orientation='vertical') + + + ax[1,0].set_ylabel(ylabel, fontsize=20) + ax[0,0].set_ylabel(ylabel, fontsize=20) + ax[1,1].set_xlabel(xlabel, fontsize=20) + ax[1,2].set_xlabel(xlabel, fontsize=20) + ax[1,3].set_xlabel(xlabel, fontsize=20) + ax[1,0].set_xlabel(xlabel, fontsize=20) + + + fig.suptitle(f'Iteration {n:0}', fontsize=20) + plt.tight_layout() + + image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' + plt.savefig(image_file_name, dpi=100, bbox_inches='tight') + plt.close() .. tab-item:: Reduced Diagnostics @@ -207,53 +207,51 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_reduced_.py # Contents of Plot_reduced_.py - import numpy as np - import matplotlib.pyplot as plt - from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano - from matplotlib import use, cm - import matplotlib.colors - import pandas as pd + import numpy as np + import matplotlib.pyplot as plt + from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano + from matplotlib import use, cm + import matplotlib.colors + import pandas as pd - r_e = physical_constants['classical electron radius'][0] - my_dpi=300 - sigmaz = 10*nano + r_e = physical_constants['classical electron radius'][0] + my_dpi=300 + sigmaz = 10*nano - fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) + fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) - rdir= './diags/reducedfiles/' + rdir= './diags/reducedfiles/' - df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) - df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) + df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) + df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) - times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() - steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() + times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() + steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() - x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() - coll_index = np.argmax(x) - coll_time = times[coll_index] + x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() + coll_index = np.argmax(x) + coll_time = times[coll_index] - # number of photons per beam particle - np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() - np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() - Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] - Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] + # number of photons per beam particle + np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() + np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() + Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] + Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] - ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) - ax[0].set_title(r'photon number/beam particle') + ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) + ax[0].set_title(r'photon number/beam particle') - # number of NLBW particles per beam particle - e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() - e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() + # number of NLBW particles per beam particle + e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() + e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() - ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) - ax[1].set_title(r'NLBW particles/beam particle') - - for a in ax.reshape(-1): - a.set_xlabel(r'time [$\sigma_z/c$]') - image_file_name ='reduced.png' - plt.tight_layout() - plt.savefig(image_file_name,dpi=300, bbox_inches='tight') - plt.close("all") - + ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) + ax[1].set_title(r'NLBW particles/beam particle') + for a in ax.reshape(-1): + a.set_xlabel(r'time [$\sigma_z/c$]') + image_file_name ='reduced.png' + plt.tight_layout() + plt.savefig(image_file_name,dpi=300, bbox_inches='tight') + plt.close("all") diff --git a/Examples/Physics_applications/beam-beam_collision/plot_full.py b/Examples/Physics_applications/beam-beam_collision/plot_full.py index 8c6d687ec08..b33dc8382fe 100644 --- a/Examples/Physics_applications/beam-beam_collision/plot_full.py +++ b/Examples/Physics_applications/beam-beam_collision/plot_full.py @@ -1,9 +1,9 @@ #!/usr/bin/env python3 -from openpmd_viewer import OpenPMDTimeSeries -import numpy as np import matplotlib.pyplot as plt +import numpy as np from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable +from openpmd_viewer import OpenPMDTimeSeries series = OpenPMDTimeSeries('./diags/diag1') steps = series.iterations @@ -18,97 +18,97 @@ for n in steps: fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) - + #E field Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) - + #B field Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) - By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) + By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) - + # Rho rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) - rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) + rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) - + xmin = info.z.min() xmax = info.z.max() if slice_axis == 'x': ymin = info.y.min() - ymax = info.y.max() + ymax = info.y.max() elif slice_axis == 'y': ymin = info.x.min() ymax = info.x.max() - - + + #E field plots im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,0].set_title(f'E$_x$', fontsize=20) + ax[0,0].set_title(f'E$_x$', fontsize=20) divider1 = make_axes_locatable(ax[0,0]) cax = divider1.append_axes('right', size='5%', pad=0.05) fig.colorbar(im1, cax=cax, orientation='vertical') - - + + im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,1].set_title(f'E$_y$', fontsize=20) + ax[0,1].set_title(f'E$_y$', fontsize=20) divider2 = make_axes_locatable(ax[0,1]) cax = divider2.append_axes('right', size='5%', pad=0.05) fig.colorbar(im2, cax=cax, orientation='vertical') im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,2].set_title(f'E$_z$', fontsize=20) + ax[0,2].set_title(f'E$_z$', fontsize=20) divider3 = make_axes_locatable(ax[0,2]) cax = divider3.append_axes('right', size='5%', pad=0.05) fig.colorbar(im3, cax=cax, orientation='vertical') - + #B field plots im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,0].set_title(f'B$_x$', fontsize=20) + ax[1,0].set_title(f'B$_x$', fontsize=20) divider4 = make_axes_locatable(ax[1,0]) cax = divider4.append_axes('right', size='5%', pad=0.05) fig.colorbar(im4, cax=cax, orientation='vertical') - - + + im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,1].set_title(f'B$_y$', fontsize=20) + ax[1,1].set_title(f'B$_y$', fontsize=20) divider5 = make_axes_locatable(ax[1,1]) cax = divider5.append_axes('right', size='5%', pad=0.05) fig.colorbar(im5, cax=cax, orientation='vertical') im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,2].set_title(f'B$_z$', fontsize=20) + ax[1,2].set_title(f'B$_z$', fontsize=20) divider6 = make_axes_locatable(ax[1,2]) cax = divider6.append_axes('right', size='5%', pad=0.05) fig.colorbar(im6, cax=cax, orientation='vertical') - + #Rho plots im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) + ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) divider7 = make_axes_locatable(ax[0,3]) cax = divider7.append_axes('right', size='5%', pad=0.05) fig.colorbar(im7, cax=cax, orientation='vertical') - - + + im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) + ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) divider8 = make_axes_locatable(ax[1,3]) cax = divider8.append_axes('right', size='5%', pad=0.05) fig.colorbar(im8, cax=cax, orientation='vertical') - - + + ax[1,0].set_ylabel(ylabel, fontsize=20) ax[0,0].set_ylabel(ylabel, fontsize=20) ax[1,1].set_xlabel(xlabel, fontsize=20) ax[1,2].set_xlabel(xlabel, fontsize=20) ax[1,3].set_xlabel(xlabel, fontsize=20) ax[1,0].set_xlabel(xlabel, fontsize=20) - + fig.suptitle(f'Iteration {n:0}', fontsize=20) plt.tight_layout() diff --git a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py index 19e12b17999..eb893164d52 100644 --- a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py +++ b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py @@ -1,10 +1,22 @@ -import numpy as np -import matplotlib.pyplot as plt -from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano -from matplotlib import use, cm import matplotlib.colors -import pandas as pd +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from matplotlib import cm, use +from scipy.constants import ( + alpha, + c, + e, + eV, + femto, + m_e, + micron, + milli, + nano, + physical_constants, + pi, +) r_e = physical_constants['classical electron radius'][0] my_dpi=300 @@ -25,7 +37,7 @@ coll_index = np.argmax(x) coll_time = times[coll_index] -# number of photons per beam particle +# number of photons per beam particle np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] @@ -34,16 +46,16 @@ ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) ax[0].set_title(r'photon number/beam particle') -# number of NLBW particles per beam particle +# number of NLBW particles per beam particle e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) ax[1].set_title(r'NLBW particles/beam particle') - + for a in ax.reshape(-1): a.set_xlabel(r'time [$\sigma_z/c$]') image_file_name ='reduced.png' plt.tight_layout() -plt.savefig(image_file_name,dpi=300, bbox_inches='tight') +plt.savefig(image_file_name,dpi=300, bbox_inches='tight') plt.close("all") From 6d6aff38864d18e2a0e9cd8146d92354350c44ec Mon Sep 17 00:00:00 2001 From: Alfred Mishi <140518333+Haavaan@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:09:02 -0700 Subject: [PATCH 03/15] Update plot_reduced.py --- .../beam-beam_collision/plot_reduced.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py index eb893164d52..dc1ed09dd35 100644 --- a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py +++ b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py @@ -1,21 +1,10 @@ - -import matplotlib.colors import matplotlib.pyplot as plt import numpy as np import pandas as pd -from matplotlib import cm, use from scipy.constants import ( - alpha, c, - e, - eV, - femto, - m_e, - micron, - milli, nano, physical_constants, - pi, ) r_e = physical_constants['classical electron radius'][0] From 5dc490d3470142d46fb0fc1dae296426ff6e41fe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Apr 2024 19:09:13 +0000 Subject: [PATCH 04/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../beam-beam_collision/plot_reduced.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py index dc1ed09dd35..e720ab0735f 100644 --- a/Examples/Physics_applications/beam-beam_collision/plot_reduced.py +++ b/Examples/Physics_applications/beam-beam_collision/plot_reduced.py @@ -1,11 +1,7 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd -from scipy.constants import ( - c, - nano, - physical_constants, -) +from scipy.constants import c, nano, physical_constants r_e = physical_constants['classical electron radius'][0] my_dpi=300 From b780627ae9a6879e3f55e440a8f64112722e7f7c Mon Sep 17 00:00:00 2001 From: Alfred Mishi Date: Tue, 23 Apr 2024 09:19:16 -0700 Subject: [PATCH 05/15] Modified pull request --- .../beam-beam_collision/README.rst | 315 +++++++++--------- 1 file changed, 157 insertions(+), 158 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index aa3471ce761..1339ea72a8f 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -10,8 +10,7 @@ We consider a right-propagating electron bunch colliding against a left-propagat We turn on the Quantum Synchrotron QED module for photon emission (also known as beamstrahlung in the collider community) and the Breit-Wheeler QED module for the generation of electron-positron pairs (also known as coherent pair generation in the collider community). -To solve for the electromagnetic field we use the nodal version of the electrostatic relativistic solver. -This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for `warpx.do_electrostatic = relativistic` for more detail). This solver accurately reproduced the subtle cancellation that occur for some component of the ``E + v x B`` terms which are crucial in simulations of relativistic particles. +This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for ``warpx.do_electrostatic = relativistic`` for more details). This solver accurately reproduces the subtle cancellation that occurs for some components of ``E + v x B``, which is crucial in simulations of relativistic particles. This example is based on the following paper :cite:t:`ex-Yakimenko2019`. @@ -72,186 +71,186 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny .. tab-item:: Full Diagnostics - This example can be run as a Python file in the same directory: + This example can be run as a python script to visualize the fields evolution of the collision between two ultra-relativistic particle beams: - - **Python** script: ``python3 Plot_full_.py`` + - **Python** script: ``python3 plot_full_.py`` .. code-block:: python # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_full_.py # Contents of Plot_full_.py - from openpmd_viewer import OpenPMDTimeSeries - import numpy as np - import matplotlib.pyplot as plt - from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable - - series = OpenPMDTimeSeries('./diags/diag1') - steps = series.iterations - print(steps) - - ylabel = 'y [m]' - xlabel = 'z [m]' - #slice_axis = 'x' - slice_axis = 'y' - - #loop through E,B,Rho - for n in steps: - - fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) - - #E field - Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) - Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) - - #B field - Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) - By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) - - # Rho - rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) - rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) - rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) - rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) - rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) - rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) - - xmin = info.z.min() - xmax = info.z.max() - if slice_axis == 'x': - ymin = info.y.min() - ymax = info.y.max() - elif slice_axis == 'y': - ymin = info.x.min() - ymax = info.x.max() - - - #E field plots - im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,0].set_title(f'E$_x$', fontsize=20) - divider1 = make_axes_locatable(ax[0,0]) - cax = divider1.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im1, cax=cax, orientation='vertical') - - - im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,1].set_title(f'E$_y$', fontsize=20) - divider2 = make_axes_locatable(ax[0,1]) - cax = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im2, cax=cax, orientation='vertical') - - im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,2].set_title(f'E$_z$', fontsize=20) - divider3 = make_axes_locatable(ax[0,2]) - cax = divider3.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im3, cax=cax, orientation='vertical') - - #B field plots - im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,0].set_title(f'B$_x$', fontsize=20) - divider4 = make_axes_locatable(ax[1,0]) - cax = divider4.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im4, cax=cax, orientation='vertical') - - - im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,1].set_title(f'B$_y$', fontsize=20) - divider5 = make_axes_locatable(ax[1,1]) - cax = divider5.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im5, cax=cax, orientation='vertical') - - im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,2].set_title(f'B$_z$', fontsize=20) - divider6 = make_axes_locatable(ax[1,2]) - cax = divider6.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im6, cax=cax, orientation='vertical') - - #Rho plots - im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) - divider7 = make_axes_locatable(ax[0,3]) - cax = divider7.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im7, cax=cax, orientation='vertical') - - - im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) - divider8 = make_axes_locatable(ax[1,3]) - cax = divider8.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im8, cax=cax, orientation='vertical') - - - ax[1,0].set_ylabel(ylabel, fontsize=20) - ax[0,0].set_ylabel(ylabel, fontsize=20) - ax[1,1].set_xlabel(xlabel, fontsize=20) - ax[1,2].set_xlabel(xlabel, fontsize=20) - ax[1,3].set_xlabel(xlabel, fontsize=20) - ax[1,0].set_xlabel(xlabel, fontsize=20) - - - fig.suptitle(f'Iteration {n:0}', fontsize=20) - plt.tight_layout() - - image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' - plt.savefig(image_file_name, dpi=100, bbox_inches='tight') - plt.close() + from openpmd_viewer import OpenPMDTimeSeries + import numpy as np + import matplotlib.pyplot as plt + from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable + + series = OpenPMDTimeSeries('./diags/diag1') + steps = series.iterations + print(steps) + + ylabel = 'y [m]' + xlabel = 'z [m]' + #slice_axis = 'x' + slice_axis = 'y' + + #loop through E,B,Rho + for n in steps: + + fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) + + #E field + Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) + Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + #B field + Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) + By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) + + # Rho + rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) + rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) + rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) + rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) + rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) + rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) + + xmin = info.z.min() + xmax = info.z.max() + if slice_axis == 'x': + ymin = info.y.min() + ymax = info.y.max() + elif slice_axis == 'y': + ymin = info.x.min() + ymax = info.x.max() + + + #E field plots + im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,0].set_title(f'E$_x$', fontsize=20) + divider1 = make_axes_locatable(ax[0,0]) + cax = divider1.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im1, cax=cax, orientation='vertical') + + + im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,1].set_title(f'E$_y$', fontsize=20) + divider2 = make_axes_locatable(ax[0,1]) + cax = divider2.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im2, cax=cax, orientation='vertical') + + im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,2].set_title(f'E$_z$', fontsize=20) + divider3 = make_axes_locatable(ax[0,2]) + cax = divider3.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im3, cax=cax, orientation='vertical') + + #B field plots + im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,0].set_title(f'B$_x$', fontsize=20) + divider4 = make_axes_locatable(ax[1,0]) + cax = divider4.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im4, cax=cax, orientation='vertical') + + + im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,1].set_title(f'B$_y$', fontsize=20) + divider5 = make_axes_locatable(ax[1,1]) + cax = divider5.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im5, cax=cax, orientation='vertical') + + im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,2].set_title(f'B$_z$', fontsize=20) + divider6 = make_axes_locatable(ax[1,2]) + cax = divider6.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im6, cax=cax, orientation='vertical') + + #Rho plots + im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) + divider7 = make_axes_locatable(ax[0,3]) + cax = divider7.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im7, cax=cax, orientation='vertical') + + + im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) + ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) + divider8 = make_axes_locatable(ax[1,3]) + cax = divider8.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im8, cax=cax, orientation='vertical') + + + ax[1,0].set_ylabel(ylabel, fontsize=20) + ax[0,0].set_ylabel(ylabel, fontsize=20) + ax[1,1].set_xlabel(xlabel, fontsize=20) + ax[1,2].set_xlabel(xlabel, fontsize=20) + ax[1,3].set_xlabel(xlabel, fontsize=20) + ax[1,0].set_xlabel(xlabel, fontsize=20) + + + fig.suptitle(f'Iteration {n:0}', fontsize=20) + plt.tight_layout() + + image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' + plt.savefig(image_file_name, dpi=100, bbox_inches='tight') + plt.close() .. tab-item:: Reduced Diagnostics This example can be run as a Python file in the same directory: - - **Python** script: ``python3 Plot_reduced_.py`` + - **Python** script: ``python3 plot_reduced_.py`` .. code-block:: python # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_reduced_.py - # Contents of Plot_reduced_.py - import numpy as np - import matplotlib.pyplot as plt - from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano - from matplotlib import use, cm - import matplotlib.colors - import pandas as pd + # Contents of plot_reduced_.py + import numpy as np + import matplotlib.pyplot as plt + from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano + from matplotlib import use, cm + import matplotlib.colors + import pandas as pd - r_e = physical_constants['classical electron radius'][0] - my_dpi=300 - sigmaz = 10*nano + r_e = physical_constants['classical electron radius'][0] + my_dpi=300 + sigmaz = 10*nano - fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) + fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) - rdir= './diags/reducedfiles/' + rdir= './diags/reducedfiles/' - df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) - df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) + df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) + df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) - times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() - steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() + times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() + steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() - x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() - coll_index = np.argmax(x) - coll_time = times[coll_index] + x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() + coll_index = np.argmax(x) + coll_time = times[coll_index] - # number of photons per beam particle - np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() - np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() - Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] - Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] + # number of photons per beam particle + np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() + np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() + Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] + Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] - ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) - ax[0].set_title(r'photon number/beam particle') + ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) + ax[0].set_title(r'photon number/beam particle') - # number of NLBW particles per beam particle - e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() - e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() + # number of NLBW particles per beam particle + e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() + e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() - ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) - ax[1].set_title(r'NLBW particles/beam particle') + ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) + ax[1].set_title(r'NLBW particles/beam particle') - for a in ax.reshape(-1): - a.set_xlabel(r'time [$\sigma_z/c$]') - image_file_name ='reduced.png' - plt.tight_layout() - plt.savefig(image_file_name,dpi=300, bbox_inches='tight') - plt.close("all") + for a in ax.reshape(-1): + a.set_xlabel(r'time [$\sigma_z/c$]') + image_file_name ='reduced.png' + plt.tight_layout() + plt.savefig(image_file_name,dpi=300, bbox_inches='tight') + plt.close("all") From 348dec2b89847451503acac45a26772044b063ae Mon Sep 17 00:00:00 2001 From: Alfred Mishi Date: Tue, 23 Apr 2024 10:45:37 -0700 Subject: [PATCH 06/15] Modified README.rst --- .../beam-beam_collision/README.rst | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index 1339ea72a8f..b619bb0dfec 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -31,7 +31,7 @@ For `MPI-parallel `__ runs, prefix these lines with ` Visualize --------- -The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). We present the reduced diagnostics and then further compare different results for the reduced diagnostics with some literature: +The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). We present the reduced diagnostics using the ``plot_reduced.py`` script and then further compare different results for the reduced diagnostics with some literature: * (red) simplified WarpX simulation as the example stored in the directory ``/Examples/Physics_applications/beam-beam_collision``; * (blue) large-scale WarpX simulation (high resolution and ad hoc generated tables ; @@ -73,12 +73,14 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny This example can be run as a python script to visualize the fields evolution of the collision between two ultra-relativistic particle beams: - - **Python** script: ``python3 plot_full_.py`` + - **Python** script: ``python3 plot_full.py`` + + The python script loads WarpX simulation stored data (diags) using OpenPMDTimeSeries and iterates over each time step ``n = 65``, after which the fields ``E,B,rho`` components in ``x`` and ``y`` directions are extracted. There after, the plots to visualize the evolution of electric field ``E``, magnetic field ``B``, and charge density ``rho`` components of the two ultra-relativistic colliding particle beams are generated. .. code-block:: python # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_full_.py - # Contents of Plot_full_.py + # Contents of plot_full_.py from openpmd_viewer import OpenPMDTimeSeries import numpy as np import matplotlib.pyplot as plt @@ -119,11 +121,11 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny xmin = info.z.min() xmax = info.z.max() if slice_axis == 'x': - ymin = info.y.min() - ymax = info.y.max() + ymin = info.y.min() + ymax = info.y.max() elif slice_axis == 'y': - ymin = info.x.min() - ymax = info.x.max() + ymin = info.x.min() + ymax = info.x.max() #E field plots @@ -200,12 +202,13 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny This example can be run as a Python file in the same directory: - - **Python** script: ``python3 plot_reduced_.py`` + - **Python** script: ``python3 plot_reduced.py`` + The python script below was used to produce the reduced diagnostics which was then further benchmarked with Yakimenko. .. code-block:: python # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_reduced_.py - # Contents of plot_reduced_.py + # Contents of plot_reduced.py import numpy as np import matplotlib.pyplot as plt from scipy.constants import micron, c, pi, m_e, femto, e, milli, eV, physical_constants, alpha, nano From 979577ff3c09a66e722d15207a4701d4e9d6ed20 Mon Sep 17 00:00:00 2001 From: Alfred Mishi Date: Tue, 23 Apr 2024 10:50:41 -0700 Subject: [PATCH 07/15] Modified README.rst --- Examples/Physics_applications/beam-beam_collision/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index b619bb0dfec..a5f3e4ea9ba 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -80,7 +80,7 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny .. code-block:: python # You can copy this file from Examples/Physics_applications/beam-beam_collisions/Plot_full_.py - # Contents of plot_full_.py + # Contents of plot_full.py from openpmd_viewer import OpenPMDTimeSeries import numpy as np import matplotlib.pyplot as plt From 396f7a0fd480db320e8fa3cefe78337602bed635 Mon Sep 17 00:00:00 2001 From: Arianna Formenti Date: Wed, 14 Aug 2024 17:08:53 -0700 Subject: [PATCH 08/15] updated vizs --- .../beam-beam_collision/README.rst | 42 ++++--- .../beam-beam_collision/inputs | 2 +- .../beam-beam_collision/plot_full.py | 118 ------------------ 3 files changed, 23 insertions(+), 139 deletions(-) delete mode 100644 Examples/Physics_applications/beam-beam_collision/plot_full.py diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index d66b58915a5..4ced0bd9140 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -10,8 +10,8 @@ We consider a right-propagating electron bunch colliding against a left-propagat We turn on the Quantum Synchrotron QED module for photon emission (also known as beamstrahlung in the collider community) and the Breit-Wheeler QED module for the generation of electron-positron pairs (also known as coherent pair generation in the collider community). -This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for ``warpx.do_electrostatic = relativistic`` for more details). This solver accurately reproduces the subtle cancellation that occurs for some components of ``E + v x B``, which is crucial in simulations of relativistic particles. - +This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for ``warpx.do_electrostatic = relativistic`` for more details). +This solver accurately reproduces the subtle cancellation that occurs for some components of ``E + v x B``, which is crucial in simulations of relativistic particles. This example is based on the following paper :cite:t:`ex-Yakimenko2019`. @@ -31,13 +31,17 @@ For `MPI-parallel `__ runs, prefix these lines with ` Visualize --------- -The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). We present the reduced diagnostics using the ``plot_reduced.py`` script and then further compare different results for the reduced diagnostics with some literature: +The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). +We compare different results for the reduced diagnostics with some literature: * (red) simplified WarpX simulation as the example stored in the directory ``/Examples/Physics_applications/beam-beam_collision``; * (blue) large-scale WarpX simulation (high resolution and ad hoc generated tables ; * (black) literature results from :cite:t:`ex-Yakimenko2019`. -The small-scale simulation has been performed with a resolution of ``nx = 64, ny = 64, nz = 128`` grid cells, while the large-scale one has a much higher resolution of ``nx = 512, ny = 512, nz = 1024``. Moreover, the large-scale simulation uses dedicated QED lookup tables instead of the builtin tables. To generate the tables within WarpX, the code must be compiled with the flag ``-DWarpX_QED_TABLE_GEN=ON``. For the large-scale simulation we have used the following options: +The small-scale simulation has been performed with a resolution of ``nx = 64, ny = 64, nz = 128`` grid cells, while the large-scale one with a much higher resolution of ``nx = 512, ny = 512, nz = 1024``. +Moreover, the large-scale simulation uses dedicated QED lookup tables instead of the builtin ones. +To generate the tables within WarpX, the code must be compiled with the flag ``-DWarpX_QED_TABLE_GEN=ON``. +For the large-scale simulation we have used the following options: .. code-block:: ini @@ -67,40 +71,38 @@ The small-scale simulation has been performed with a resolution of ``nx = 64, ny Beam-beam collision benchmark against :cite:t:`ex-Yakimenko2019`. -.. tab-set:: - .. tab-item:: Full Diagnostics +Below are two visualizations scripts that provide examples to graph the field and reduced diagnostics. +They are available in the ``Examples/Physics_applications/beam-beam_collision/`` folder and can be run as simply as ``python3 plot_fields.py`` and ``python3 plot_reduced.py``. - This example can be run as a python script to visualize the fields evolution of the collision between two ultra-relativistic particle beams: +.. tab-set:: - - **Python** script: ``python3 plot_full.py`` + .. tab-item:: Field Diagnostics - The python script loads WarpX simulation stored data (diags) using OpenPMDTimeSeries and iterates over each time step ``n = 65``, after which the fields ``E,B,rho`` components in ``x`` and ``y`` directions are extracted. There after, the plots to visualize the evolution of electric field ``E``, magnetic field ``B``, and charge density ``rho`` components of the two ultra-relativistic colliding particle beams are generated. + This script visualizes the evolution of the fields (:math:`|E|, |B|, \rho`) during the collision between the two ultra-relativistic lepton beams. + The magnitude of E and B and the charge densities of the primary beams and of the secondary pairs are sliced along either one of the two transverse coordinates (:math:`x` and `:math:`y`). - .. literalinclude:: plot_full.py + .. literalinclude:: plot_fields.py :language: python3 - :caption: You can copy this file from ``Examples/Physics_applications/beam-beam_collision/plot_full.py``. + :caption: You can copy this file from ``Examples/Physics_applications/beam-beam_collision/plot_fields.py``. - .. figure:: https://gist.github.com/user-attachments/assets/7d41c5af-5347-4145-92c0-59279006a9de - :alt: Beam-beam collision fields at time step 90. + .. figure:: https://gist.github.com/user-attachments/assets/04c9c0ec-b580-446f-a11a-437c1b244a41 + :alt: Slice across :math:`x` of different fields (:math:`|E|, |B|, \rho`) at timestep 45, in the middle of the collision. :width: 100% - Beam-beam collision fields at time step 90. + Slice across :math:`x` of different fields (:math:`|E|, |B|, \rho`) at timestep 45, in the middle of the collision. .. tab-item:: Reduced Diagnostics - This example can be run as a Python file in the same directory: - - - **Python** script: ``python3 plot_reduced.py`` - The python script below was used to produce the reduced diagnostics which was then further benchmarked with Yakimenko. + A similar script to the one below was used to produce the image showing the benchmark against :cite:t:`ex-Yakimenko2019`. .. literalinclude:: plot_reduced.py :language: python3 :caption: You can copy this file from ``Examples/Physics_applications/beam-beam_collision/plot_reduced.py``. .. figure:: https://gist.github.com/user-attachments/assets/c280490a-f1f2-4329-ad3c-46817d245dc1 - :alt: Beam-beam collision photon and pair production. + :alt: Photon and pair production rates in time throughout the collision. :width: 100% - Beam-beam collision photon and pair production. + Photon and pair production rates in time throughout the collision. diff --git a/Examples/Physics_applications/beam-beam_collision/inputs b/Examples/Physics_applications/beam-beam_collision/inputs index 488e997f895..a44991cf861 100644 --- a/Examples/Physics_applications/beam-beam_collision/inputs +++ b/Examples/Physics_applications/beam-beam_collision/inputs @@ -212,7 +212,7 @@ warpx.do_qed_schwinger = 0. # FULL diagnostics.diags_names = diag1 -diag1.intervals = 0 +diag1.intervals = 15 diag1.diag_type = Full diag1.write_species = 1 diag1.fields_to_plot = Ex Ey Ez Bx By Bz rho_beam1 rho_beam2 rho_ele1 rho_pos1 rho_ele2 rho_pos2 diff --git a/Examples/Physics_applications/beam-beam_collision/plot_full.py b/Examples/Physics_applications/beam-beam_collision/plot_full.py deleted file mode 100644 index b33dc8382fe..00000000000 --- a/Examples/Physics_applications/beam-beam_collision/plot_full.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env python3 - -import matplotlib.pyplot as plt -import numpy as np -from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable -from openpmd_viewer import OpenPMDTimeSeries - -series = OpenPMDTimeSeries('./diags/diag1') -steps = series.iterations -print(steps) - -ylabel = 'y [m]' -xlabel = 'z [m]' -#slice_axis = 'x' -slice_axis = 'y' - -#loop through E,B,Rho -for n in steps: - - fig, ax = plt.subplots(ncols=4, nrows=2, figsize=(40, 10), dpi=100., sharex=True, sharey=True) - - #E field - Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) - Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) - - #B field - Bx, info = series.get_field(field='B', coord=slice_axis, iteration=n, plot=False, slice_across=slice_axis) - By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) - - # Rho - rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) - rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) - rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) - rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) - rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) - rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) - - xmin = info.z.min() - xmax = info.z.max() - if slice_axis == 'x': - ymin = info.y.min() - ymax = info.y.max() - elif slice_axis == 'y': - ymin = info.x.min() - ymax = info.x.max() - - - #E field plots - im1 = ax[0,0].imshow(np.transpose(Ex), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,0].set_title(f'E$_x$', fontsize=20) - divider1 = make_axes_locatable(ax[0,0]) - cax = divider1.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im1, cax=cax, orientation='vertical') - - - im2 = ax[0,1].imshow(np.transpose(Ey), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,1].set_title(f'E$_y$', fontsize=20) - divider2 = make_axes_locatable(ax[0,1]) - cax = divider2.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im2, cax=cax, orientation='vertical') - - im3 = ax[0,2].imshow(np.transpose(Ez), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,2].set_title(f'E$_z$', fontsize=20) - divider3 = make_axes_locatable(ax[0,2]) - cax = divider3.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im3, cax=cax, orientation='vertical') - - #B field plots - im4 = ax[1,0].imshow(np.transpose(Bx), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,0].set_title(f'B$_x$', fontsize=20) - divider4 = make_axes_locatable(ax[1,0]) - cax = divider4.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im4, cax=cax, orientation='vertical') - - - im5 = ax[1,1].imshow(np.transpose(By), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,1].set_title(f'B$_y$', fontsize=20) - divider5 = make_axes_locatable(ax[1,1]) - cax = divider5.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im5, cax=cax, orientation='vertical') - - im6 = ax[1,2].imshow(np.transpose(Bz), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,2].set_title(f'B$_z$', fontsize=20) - divider6 = make_axes_locatable(ax[1,2]) - cax = divider6.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im6, cax=cax, orientation='vertical') - - #Rho plots - im7 = ax[0,3].imshow(np.transpose(rho_beam1+rho_beam2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[0,3].set_title(f'rho$_{{\tbeams{{}}}}$', fontsize=20) - divider7 = make_axes_locatable(ax[0,3]) - cax = divider7.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im7, cax=cax, orientation='vertical') - - - im8 = ax[1,3].imshow(np.transpose(rho_ele1+rho_pos1+rho_ele2+rho_pos2), cmap='seismic', extent=[xmin , xmax, ymin, ymax]) - ax[1,3].set_title(f'rho$_{{\tsecondaries{{}}}}$', fontsize=20) - divider8 = make_axes_locatable(ax[1,3]) - cax = divider8.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im8, cax=cax, orientation='vertical') - - - ax[1,0].set_ylabel(ylabel, fontsize=20) - ax[0,0].set_ylabel(ylabel, fontsize=20) - ax[1,1].set_xlabel(xlabel, fontsize=20) - ax[1,2].set_xlabel(xlabel, fontsize=20) - ax[1,3].set_xlabel(xlabel, fontsize=20) - ax[1,0].set_xlabel(xlabel, fontsize=20) - - - fig.suptitle(f'Iteration {n:0}', fontsize=20) - plt.tight_layout() - - image_file_name = 'FULL_'+slice_axis+f'{n:03d}.png' - plt.savefig(image_file_name, dpi=100, bbox_inches='tight') - plt.close() From 06b6636115094392d8a4dcb6155d8f40a23df610 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:21:20 +0000 Subject: [PATCH 09/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../beam-beam_collision/README.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/README.rst b/Examples/Physics_applications/beam-beam_collision/README.rst index 4ced0bd9140..4da727273d7 100644 --- a/Examples/Physics_applications/beam-beam_collision/README.rst +++ b/Examples/Physics_applications/beam-beam_collision/README.rst @@ -10,7 +10,7 @@ We consider a right-propagating electron bunch colliding against a left-propagat We turn on the Quantum Synchrotron QED module for photon emission (also known as beamstrahlung in the collider community) and the Breit-Wheeler QED module for the generation of electron-positron pairs (also known as coherent pair generation in the collider community). -This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for ``warpx.do_electrostatic = relativistic`` for more details). +This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for ``warpx.do_electrostatic = relativistic`` for more details). This solver accurately reproduces the subtle cancellation that occurs for some components of ``E + v x B``, which is crucial in simulations of relativistic particles. This example is based on the following paper :cite:t:`ex-Yakimenko2019`. @@ -31,16 +31,16 @@ For `MPI-parallel `__ runs, prefix these lines with ` Visualize --------- -The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). +The figure below shows the number of photons emitted per beam particle (left) and the number of secondary pairs generated per beam particle (right). We compare different results for the reduced diagnostics with some literature: * (red) simplified WarpX simulation as the example stored in the directory ``/Examples/Physics_applications/beam-beam_collision``; * (blue) large-scale WarpX simulation (high resolution and ad hoc generated tables ; * (black) literature results from :cite:t:`ex-Yakimenko2019`. -The small-scale simulation has been performed with a resolution of ``nx = 64, ny = 64, nz = 128`` grid cells, while the large-scale one with a much higher resolution of ``nx = 512, ny = 512, nz = 1024``. -Moreover, the large-scale simulation uses dedicated QED lookup tables instead of the builtin ones. -To generate the tables within WarpX, the code must be compiled with the flag ``-DWarpX_QED_TABLE_GEN=ON``. +The small-scale simulation has been performed with a resolution of ``nx = 64, ny = 64, nz = 128`` grid cells, while the large-scale one with a much higher resolution of ``nx = 512, ny = 512, nz = 1024``. +Moreover, the large-scale simulation uses dedicated QED lookup tables instead of the builtin ones. +To generate the tables within WarpX, the code must be compiled with the flag ``-DWarpX_QED_TABLE_GEN=ON``. For the large-scale simulation we have used the following options: .. code-block:: ini @@ -72,7 +72,7 @@ For the large-scale simulation we have used the following options: Beam-beam collision benchmark against :cite:t:`ex-Yakimenko2019`. -Below are two visualizations scripts that provide examples to graph the field and reduced diagnostics. +Below are two visualizations scripts that provide examples to graph the field and reduced diagnostics. They are available in the ``Examples/Physics_applications/beam-beam_collision/`` folder and can be run as simply as ``python3 plot_fields.py`` and ``python3 plot_reduced.py``. .. tab-set:: From 6348ed83ca516e70be82ae9770b2edf41e60ebe8 Mon Sep 17 00:00:00 2001 From: Arianna Formenti Date: Wed, 14 Aug 2024 17:43:19 -0700 Subject: [PATCH 10/15] restore plot fields --- .../beam-beam_collision/plot_fields.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Examples/Physics_applications/beam-beam_collision/plot_fields.py diff --git a/Examples/Physics_applications/beam-beam_collision/plot_fields.py b/Examples/Physics_applications/beam-beam_collision/plot_fields.py new file mode 100644 index 00000000000..c038a5aab97 --- /dev/null +++ b/Examples/Physics_applications/beam-beam_collision/plot_fields.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 + +import matplotlib.pyplot as plt +import numpy as np +from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable +from openpmd_viewer import OpenPMDTimeSeries + +plt.rcParams.update({'font.size': 16}) + +series = OpenPMDTimeSeries('./diags/diag1') +steps = series.iterations + + +for slice_axis in ['x', 'y']: # slice the fields along x and y + + for n in steps: # loop through the available timesteps + + fig, ax = plt.subplots(ncols=2, nrows=2, figsize=(10, 6), dpi=300, sharex=True, sharey=True) + + # get E field + Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) + Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) + # get B field + Bx, info = series.get_field(field='B', coord='x', iteration=n, plot=False, slice_across=slice_axis) + By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) + Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) + # get charge densities + rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) + rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) + rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) + rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) + rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) + rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) + + xmin = info.z.min() + xmax = info.z.max() + xlabel = 'z [m]' + + if slice_axis == 'x': + ymin = info.y.min() + ymax = info.y.max() + ylabel = 'y [m]' + elif slice_axis == 'y': + ymin = info.x.min() + ymax = info.x.max() + ylabel = 'x [m]' + + # plot E magnitude + Emag = np.sqrt(Ex**2+Ey**2+Ez**2) + im = ax[0,0].imshow(np.transpose(Emag), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=0, vmax=np.max(np.abs(Emag))) + ax[0,0].set_title('E [V/m]') + divider = make_axes_locatable(ax[0,0]) + cax = divider.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im, cax=cax, orientation='vertical') + + # plot B magnitude + Bmag = np.sqrt(Bx**2+By**2+Bz**2) + im = ax[1,0].imshow(np.transpose(Bmag), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=0, vmax=np.max(np.abs(Bmag))) + ax[1,0].set_title('B [T]') + divider = make_axes_locatable(ax[1,0]) + cax = divider.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im, cax=cax, orientation='vertical') + + # plot beam densities + rho_beams = rho_beam1+rho_beam2 + im = ax[0,1].imshow(np.transpose(rho_beams), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=-np.max(np.abs(rho_beams)), vmax=np.max(np.abs(rho_beams))) + ax[0,1].set_title(r'$\rho$ beams [C/m$^3$]') + divider = make_axes_locatable(ax[0,1]) + cax = divider.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im, cax=cax, orientation='vertical') + + # plot secondary densities + rho2 = rho_ele1+rho_pos1+rho_ele2+rho_pos2 + im = ax[1,1].imshow(np.transpose(rho2), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=-np.max(np.abs(rho2)), vmax=np.max(np.abs(rho2))) + ax[1,1].set_title(r'$\rho$ secondaries [C/m$^3$]') + divider = make_axes_locatable(ax[1,1]) + cax = divider.append_axes('right', size='5%', pad=0.05) + fig.colorbar(im, cax=cax, orientation='vertical') + + for a in ax[-1,:].reshape(-1): + a.set_xlabel(xlabel) + for a in ax[:,0].reshape(-1): + a.set_ylabel(ylabel) + + fig.suptitle(f'Iteration = {n}, time [s] = {series.current_t}', fontsize=20) + plt.tight_layout() + + image_file_name = 'FIELDS_'+slice_axis+f'_{n:03d}.png' + plt.savefig(image_file_name, dpi=100, bbox_inches='tight') + plt.close() From 16c7788316a5497280c4a58a607ab409a815452a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 15 Aug 2024 00:44:16 +0000 Subject: [PATCH 11/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../beam-beam_collision/plot_fields.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Examples/Physics_applications/beam-beam_collision/plot_fields.py b/Examples/Physics_applications/beam-beam_collision/plot_fields.py index c038a5aab97..2965ffd498d 100644 --- a/Examples/Physics_applications/beam-beam_collision/plot_fields.py +++ b/Examples/Physics_applications/beam-beam_collision/plot_fields.py @@ -11,7 +11,7 @@ steps = series.iterations -for slice_axis in ['x', 'y']: # slice the fields along x and y +for slice_axis in ['x', 'y']: # slice the fields along x and y for n in steps: # loop through the available timesteps @@ -25,7 +25,7 @@ Bx, info = series.get_field(field='B', coord='x', iteration=n, plot=False, slice_across=slice_axis) By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) - # get charge densities + # get charge densities rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) @@ -45,7 +45,7 @@ ymin = info.x.min() ymax = info.x.max() ylabel = 'x [m]' - + # plot E magnitude Emag = np.sqrt(Ex**2+Ey**2+Ez**2) im = ax[0,0].imshow(np.transpose(Emag), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=0, vmax=np.max(np.abs(Emag))) @@ -62,7 +62,7 @@ cax = divider.append_axes('right', size='5%', pad=0.05) fig.colorbar(im, cax=cax, orientation='vertical') - # plot beam densities + # plot beam densities rho_beams = rho_beam1+rho_beam2 im = ax[0,1].imshow(np.transpose(rho_beams), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=-np.max(np.abs(rho_beams)), vmax=np.max(np.abs(rho_beams))) ax[0,1].set_title(r'$\rho$ beams [C/m$^3$]') @@ -77,7 +77,7 @@ divider = make_axes_locatable(ax[1,1]) cax = divider.append_axes('right', size='5%', pad=0.05) fig.colorbar(im, cax=cax, orientation='vertical') - + for a in ax[-1,:].reshape(-1): a.set_xlabel(xlabel) for a in ax[:,0].reshape(-1): From fd4bbf9897fe90198a3c9875f5fedd9ecbacf00f Mon Sep 17 00:00:00 2001 From: Arianna Formenti Date: Mon, 16 Sep 2024 17:57:26 -0700 Subject: [PATCH 12/15] rm EOL white spaces --- .../Physics_applications/beam_beam_collision/README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Examples/Physics_applications/beam_beam_collision/README.rst b/Examples/Physics_applications/beam_beam_collision/README.rst index 07e1ea82b0d..c22dde26ee2 100644 --- a/Examples/Physics_applications/beam_beam_collision/README.rst +++ b/Examples/Physics_applications/beam_beam_collision/README.rst @@ -11,7 +11,7 @@ We turn on the Quantum Synchrotron QED module for photon emission (also known as the Breit-Wheeler QED module for the generation of electron-positron pairs (also known as coherent pair generation in the collider community). To solve for the electromagnetic field we use the nodal version of the electrostatic relativistic solver. -This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for `warpx.do_electrostatic = relativistic` for more detail). +This solver computes the average velocity of each species, and solves the corresponding relativistic Poisson equation (see the WarpX documentation for `warpx.do_electrostatic = relativistic` for more detail). This solver accurately reproduces the subtle cancellation that occur for some component of ``E + v x B``, which are crucial in simulations of relativistic particles. @@ -40,9 +40,9 @@ We compare different results for the reduced diagnostics with the literature: * (blue) large-scale WarpX simulation (high resolution and ad hoc generated tables ; * (black) literature results from :cite:t:`ex-Yakimenko2019`. -The small-scale simulation has been performed with a resolution of ``nx = 64, ny = 64, nz = 64`` grid cells, while the large-scale one has a much higher resolution of ``nx = 512, ny = 512, nz = 1024``. -Moreover, the large-scale simulation uses dedicated QED lookup tables instead of the builtin tables. -To generate the tables within WarpX, the code must be compiled with the flag ``-DWarpX_QED_TABLE_GEN=ON``. +The small-scale simulation has been performed with a resolution of ``nx = 64, ny = 64, nz = 64`` grid cells, while the large-scale one has a much higher resolution of ``nx = 512, ny = 512, nz = 1024``. +Moreover, the large-scale simulation uses dedicated QED lookup tables instead of the builtin tables. +To generate the tables within WarpX, the code must be compiled with the flag ``-DWarpX_QED_TABLE_GEN=ON``. For the large-scale simulation we have used the following options: .. code-block:: ini From b79a39c02080d8874e88d69a62ed7ed01b31c73a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 17:35:04 +0000 Subject: [PATCH 13/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../beam_beam_collision/plot_fields.py | 156 ++++++++++++------ .../beam_beam_collision/plot_reduced.py | 48 +++--- 2 files changed, 127 insertions(+), 77 deletions(-) diff --git a/Examples/Physics_applications/beam_beam_collision/plot_fields.py b/Examples/Physics_applications/beam_beam_collision/plot_fields.py index 2965ffd498d..a7ddb2d13e9 100644 --- a/Examples/Physics_applications/beam_beam_collision/plot_fields.py +++ b/Examples/Physics_applications/beam_beam_collision/plot_fields.py @@ -5,87 +5,135 @@ from mpl_toolkits.axes_grid1.axes_divider import make_axes_locatable from openpmd_viewer import OpenPMDTimeSeries -plt.rcParams.update({'font.size': 16}) +plt.rcParams.update({"font.size": 16}) -series = OpenPMDTimeSeries('./diags/diag1') +series = OpenPMDTimeSeries("./diags/diag1") steps = series.iterations -for slice_axis in ['x', 'y']: # slice the fields along x and y - - for n in steps: # loop through the available timesteps - - fig, ax = plt.subplots(ncols=2, nrows=2, figsize=(10, 6), dpi=300, sharex=True, sharey=True) +for slice_axis in ["x", "y"]: # slice the fields along x and y + for n in steps: # loop through the available timesteps + fig, ax = plt.subplots( + ncols=2, nrows=2, figsize=(10, 6), dpi=300, sharex=True, sharey=True + ) # get E field - Ex, info = series.get_field(field='E', coord='x', iteration=n, plot=False, slice_across=slice_axis) - Ey, info = series.get_field(field='E', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Ez, info = series.get_field(field='E', coord='z', iteration=n, plot=False, slice_across=slice_axis) + Ex, info = series.get_field( + field="E", coord="x", iteration=n, plot=False, slice_across=slice_axis + ) + Ey, info = series.get_field( + field="E", coord="y", iteration=n, plot=False, slice_across=slice_axis + ) + Ez, info = series.get_field( + field="E", coord="z", iteration=n, plot=False, slice_across=slice_axis + ) # get B field - Bx, info = series.get_field(field='B', coord='x', iteration=n, plot=False, slice_across=slice_axis) - By, info = series.get_field(field='B', coord='y', iteration=n, plot=False, slice_across=slice_axis) - Bz, info = series.get_field(field='B', coord='z', iteration=n, plot=False, slice_across=slice_axis) + Bx, info = series.get_field( + field="B", coord="x", iteration=n, plot=False, slice_across=slice_axis + ) + By, info = series.get_field( + field="B", coord="y", iteration=n, plot=False, slice_across=slice_axis + ) + Bz, info = series.get_field( + field="B", coord="z", iteration=n, plot=False, slice_across=slice_axis + ) # get charge densities - rho_beam1, info = series.get_field(field='rho_beam1', iteration=n, plot=False, slice_across=slice_axis) - rho_beam2, info = series.get_field(field='rho_beam2', iteration=n, plot=False, slice_across=slice_axis) - rho_ele1, info = series.get_field(field='rho_ele1', iteration=n, plot=False, slice_across=slice_axis) - rho_pos1, info = series.get_field(field='rho_pos1', iteration=n, plot=False, slice_across=slice_axis) - rho_ele2, info = series.get_field(field='rho_ele2', iteration=n, plot=False, slice_across=slice_axis) - rho_pos2, info = series.get_field(field='rho_pos2', iteration=n, plot=False, slice_across=slice_axis) + rho_beam1, info = series.get_field( + field="rho_beam1", iteration=n, plot=False, slice_across=slice_axis + ) + rho_beam2, info = series.get_field( + field="rho_beam2", iteration=n, plot=False, slice_across=slice_axis + ) + rho_ele1, info = series.get_field( + field="rho_ele1", iteration=n, plot=False, slice_across=slice_axis + ) + rho_pos1, info = series.get_field( + field="rho_pos1", iteration=n, plot=False, slice_across=slice_axis + ) + rho_ele2, info = series.get_field( + field="rho_ele2", iteration=n, plot=False, slice_across=slice_axis + ) + rho_pos2, info = series.get_field( + field="rho_pos2", iteration=n, plot=False, slice_across=slice_axis + ) xmin = info.z.min() xmax = info.z.max() - xlabel = 'z [m]' + xlabel = "z [m]" - if slice_axis == 'x': + if slice_axis == "x": ymin = info.y.min() ymax = info.y.max() - ylabel = 'y [m]' - elif slice_axis == 'y': + ylabel = "y [m]" + elif slice_axis == "y": ymin = info.x.min() ymax = info.x.max() - ylabel = 'x [m]' + ylabel = "x [m]" # plot E magnitude - Emag = np.sqrt(Ex**2+Ey**2+Ez**2) - im = ax[0,0].imshow(np.transpose(Emag), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=0, vmax=np.max(np.abs(Emag))) - ax[0,0].set_title('E [V/m]') - divider = make_axes_locatable(ax[0,0]) - cax = divider.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im, cax=cax, orientation='vertical') + Emag = np.sqrt(Ex**2 + Ey**2 + Ez**2) + im = ax[0, 0].imshow( + np.transpose(Emag), + cmap="seismic", + extent=[xmin, xmax, ymin, ymax], + vmin=0, + vmax=np.max(np.abs(Emag)), + ) + ax[0, 0].set_title("E [V/m]") + divider = make_axes_locatable(ax[0, 0]) + cax = divider.append_axes("right", size="5%", pad=0.05) + fig.colorbar(im, cax=cax, orientation="vertical") # plot B magnitude - Bmag = np.sqrt(Bx**2+By**2+Bz**2) - im = ax[1,0].imshow(np.transpose(Bmag), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=0, vmax=np.max(np.abs(Bmag))) - ax[1,0].set_title('B [T]') - divider = make_axes_locatable(ax[1,0]) - cax = divider.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im, cax=cax, orientation='vertical') + Bmag = np.sqrt(Bx**2 + By**2 + Bz**2) + im = ax[1, 0].imshow( + np.transpose(Bmag), + cmap="seismic", + extent=[xmin, xmax, ymin, ymax], + vmin=0, + vmax=np.max(np.abs(Bmag)), + ) + ax[1, 0].set_title("B [T]") + divider = make_axes_locatable(ax[1, 0]) + cax = divider.append_axes("right", size="5%", pad=0.05) + fig.colorbar(im, cax=cax, orientation="vertical") # plot beam densities - rho_beams = rho_beam1+rho_beam2 - im = ax[0,1].imshow(np.transpose(rho_beams), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=-np.max(np.abs(rho_beams)), vmax=np.max(np.abs(rho_beams))) - ax[0,1].set_title(r'$\rho$ beams [C/m$^3$]') - divider = make_axes_locatable(ax[0,1]) - cax = divider.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im, cax=cax, orientation='vertical') + rho_beams = rho_beam1 + rho_beam2 + im = ax[0, 1].imshow( + np.transpose(rho_beams), + cmap="seismic", + extent=[xmin, xmax, ymin, ymax], + vmin=-np.max(np.abs(rho_beams)), + vmax=np.max(np.abs(rho_beams)), + ) + ax[0, 1].set_title(r"$\rho$ beams [C/m$^3$]") + divider = make_axes_locatable(ax[0, 1]) + cax = divider.append_axes("right", size="5%", pad=0.05) + fig.colorbar(im, cax=cax, orientation="vertical") # plot secondary densities - rho2 = rho_ele1+rho_pos1+rho_ele2+rho_pos2 - im = ax[1,1].imshow(np.transpose(rho2), cmap='seismic', extent=[xmin, xmax, ymin, ymax], vmin=-np.max(np.abs(rho2)), vmax=np.max(np.abs(rho2))) - ax[1,1].set_title(r'$\rho$ secondaries [C/m$^3$]') - divider = make_axes_locatable(ax[1,1]) - cax = divider.append_axes('right', size='5%', pad=0.05) - fig.colorbar(im, cax=cax, orientation='vertical') - - for a in ax[-1,:].reshape(-1): + rho2 = rho_ele1 + rho_pos1 + rho_ele2 + rho_pos2 + im = ax[1, 1].imshow( + np.transpose(rho2), + cmap="seismic", + extent=[xmin, xmax, ymin, ymax], + vmin=-np.max(np.abs(rho2)), + vmax=np.max(np.abs(rho2)), + ) + ax[1, 1].set_title(r"$\rho$ secondaries [C/m$^3$]") + divider = make_axes_locatable(ax[1, 1]) + cax = divider.append_axes("right", size="5%", pad=0.05) + fig.colorbar(im, cax=cax, orientation="vertical") + + for a in ax[-1, :].reshape(-1): a.set_xlabel(xlabel) - for a in ax[:,0].reshape(-1): + for a in ax[:, 0].reshape(-1): a.set_ylabel(ylabel) - fig.suptitle(f'Iteration = {n}, time [s] = {series.current_t}', fontsize=20) + fig.suptitle(f"Iteration = {n}, time [s] = {series.current_t}", fontsize=20) plt.tight_layout() - image_file_name = 'FIELDS_'+slice_axis+f'_{n:03d}.png' - plt.savefig(image_file_name, dpi=100, bbox_inches='tight') + image_file_name = "FIELDS_" + slice_axis + f"_{n:03d}.png" + plt.savefig(image_file_name, dpi=100, bbox_inches="tight") plt.close() diff --git a/Examples/Physics_applications/beam_beam_collision/plot_reduced.py b/Examples/Physics_applications/beam_beam_collision/plot_reduced.py index e720ab0735f..3f59f975519 100644 --- a/Examples/Physics_applications/beam_beam_collision/plot_reduced.py +++ b/Examples/Physics_applications/beam_beam_collision/plot_reduced.py @@ -3,44 +3,46 @@ import pandas as pd from scipy.constants import c, nano, physical_constants -r_e = physical_constants['classical electron radius'][0] -my_dpi=300 -sigmaz = 10*nano +r_e = physical_constants["classical electron radius"][0] +my_dpi = 300 +sigmaz = 10 * nano -fig, ax = plt.subplots(ncols=2, nrows=1, figsize=(2000./my_dpi, 1000./my_dpi), dpi=my_dpi) +fig, ax = plt.subplots( + ncols=2, nrows=1, figsize=(2000.0 / my_dpi, 1000.0 / my_dpi), dpi=my_dpi +) -rdir= './diags/reducedfiles/' +rdir = "./diags/reducedfiles/" -df_cr = pd.read_csv(f'{rdir}'+'ColliderRelevant_beam1_beam2.txt', sep=" ", header=0) -df_pn = pd.read_csv(f'{rdir}'+'ParticleNumber.txt', sep=" ", header=0) +df_cr = pd.read_csv(f"{rdir}" + "ColliderRelevant_beam1_beam2.txt", sep=" ", header=0) +df_pn = pd.read_csv(f"{rdir}" + "ParticleNumber.txt", sep=" ", header=0) -times = df_cr[[col for col in df_cr.columns if f']time' in col]].to_numpy() -steps = df_cr[[col for col in df_cr.columns if f']step' in col]].to_numpy() +times = df_cr[[col for col in df_cr.columns if "]time" in col]].to_numpy() +steps = df_cr[[col for col in df_cr.columns if "]step" in col]].to_numpy() -x = df_cr[[col for col in df_cr.columns if f']dL_dt' in col]].to_numpy() +x = df_cr[[col for col in df_cr.columns if "]dL_dt" in col]].to_numpy() coll_index = np.argmax(x) coll_time = times[coll_index] # number of photons per beam particle -np1 = df_pn[[col for col in df_pn.columns if f']pho1_weight' in col]].to_numpy() -np2 = df_pn[[col for col in df_pn.columns if f']pho2_weight' in col]].to_numpy() -Ne = df_pn[[col for col in df_pn.columns if f']beam1_weight' in col]].to_numpy()[0] -Np = df_pn[[col for col in df_pn.columns if f']beam2_weight' in col]].to_numpy()[0] +np1 = df_pn[[col for col in df_pn.columns if "]pho1_weight" in col]].to_numpy() +np2 = df_pn[[col for col in df_pn.columns if "]pho2_weight" in col]].to_numpy() +Ne = df_pn[[col for col in df_pn.columns if "]beam1_weight" in col]].to_numpy()[0] +Np = df_pn[[col for col in df_pn.columns if "]beam2_weight" in col]].to_numpy()[0] -ax[0].plot((times-coll_time)/(sigmaz/c), (np1+np2)/(Ne+Np), lw=2) -ax[0].set_title(r'photon number/beam particle') +ax[0].plot((times - coll_time) / (sigmaz / c), (np1 + np2) / (Ne + Np), lw=2) +ax[0].set_title(r"photon number/beam particle") # number of NLBW particles per beam particle -e1 = df_pn[[col for col in df_pn.columns if f']ele1_weight' in col]].to_numpy() -e2 = df_pn[[col for col in df_pn.columns if f']ele2_weight' in col]].to_numpy() +e1 = df_pn[[col for col in df_pn.columns if "]ele1_weight" in col]].to_numpy() +e2 = df_pn[[col for col in df_pn.columns if "]ele2_weight" in col]].to_numpy() -ax[1].plot((times-coll_time)/(sigmaz/c), (e1+e2)/(Ne+Np), lw=2) -ax[1].set_title(r'NLBW particles/beam particle') +ax[1].plot((times - coll_time) / (sigmaz / c), (e1 + e2) / (Ne + Np), lw=2) +ax[1].set_title(r"NLBW particles/beam particle") for a in ax.reshape(-1): - a.set_xlabel(r'time [$\sigma_z/c$]') -image_file_name ='reduced.png' + a.set_xlabel(r"time [$\sigma_z/c$]") +image_file_name = "reduced.png" plt.tight_layout() -plt.savefig(image_file_name,dpi=300, bbox_inches='tight') +plt.savefig(image_file_name, dpi=300, bbox_inches="tight") plt.close("all") From 69fb00b6948e79ed3170a6c872ad4fb8197316ca Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Tue, 17 Sep 2024 11:01:02 -0700 Subject: [PATCH 14/15] Add missing text back. --- .../beam_beam_collision/README.rst | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Examples/Physics_applications/beam_beam_collision/README.rst b/Examples/Physics_applications/beam_beam_collision/README.rst index c22dde26ee2..1f1c7d63158 100644 --- a/Examples/Physics_applications/beam_beam_collision/README.rst +++ b/Examples/Physics_applications/beam_beam_collision/README.rst @@ -67,8 +67,45 @@ For the large-scale simulation we have used the following options: qed_bw.tab_pair_frac_how_many=512 qed_bw.save_table_in=my_bw_table.txt -.. figure:: https://gist.github.com/user-attachments/assets/2dd43782-d039-4faa-9d27-e3cf8fb17352 + +.. figure:: https://user-images.githubusercontent.com/17280419/291749626-aa61fff2-e6d2-45a3-80ee-84b2851ea0bf.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMwMzQzNTEsIm5iZiI6MTcwMzAzNDA1MSwicGF0aCI6Ii8xNzI4MDQxOS8yOTE3NDk2MjYtYWE2MWZmZjItZTZkMi00NWEzLTgwZWUtODRiMjg1MWVhMGJmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIwVDAxMDA1MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWFiYzY2MGQyYzIyZGIzYzUxOWI3MzNjZTk5ZDM1YzgyNmY4ZDYxOGRlZjAyZTIwNTAyMTc3NTgwN2Q0YjEwNGMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.I96LQpjqmFXirPDVnBlFQIkCuenR6IuOSY0OIIQvtCo :alt: Beam-beam collision benchmark against :cite:t:`ex-Yakimenko2019`. :width: 100% Beam-beam collision benchmark against :cite:t:`ex-Yakimenko2019`. + + +Below are two visualizations scripts that provide examples to graph the field and reduced diagnostics. +They are available in the ``Examples/Physics_applications/beam-beam_collision/`` folder and can be run as simply as ``python3 plot_fields.py`` and ``python3 plot_reduced.py``. + +.. tab-set:: + + .. tab-item:: Field Diagnostics + + This script visualizes the evolution of the fields (:math:`|E|, |B|, \rho`) during the collision between the two ultra-relativistic lepton beams. + The magnitude of E and B and the charge densities of the primary beams and of the secondary pairs are sliced along either one of the two transverse coordinates (:math:`x` and :math:`y`). + + .. literalinclude:: plot_fields.py + :language: python3 + :caption: You can copy this file from ``Examples/Physics_applications/beam-beam_collision/plot_fields.py``. + + .. figure:: https://gist.github.com/user-attachments/assets/04c9c0ec-b580-446f-a11a-437c1b244a41 + :alt: Slice across :math:`x` of different fields (:math:`|E|, |B|, \rho`) at timestep 45, in the middle of the collision. + :width: 100% + + Slice across :math:`x` of different fields (:math:`|E|, |B|, \rho`) at timestep 45, in the middle of the collision. + + + .. tab-item:: Reduced Diagnostics + + A similar script to the one below was used to produce the image showing the benchmark against :cite:t:`ex-Yakimenko2019`. + + .. literalinclude:: plot_reduced.py + :language: python3 + :caption: You can copy this file from ``Examples/Physics_applications/beam-beam_collision/plot_reduced.py``. + + .. figure:: https://gist.github.com/user-attachments/assets/c280490a-f1f2-4329-ad3c-46817d245dc1 + :alt: Photon and pair production rates in time throughout the collision. + :width: 100% + + Photon and pair production rates in time throughout the collision. From 9a9c899de99963d288f2d5da470108a27d5a5eee Mon Sep 17 00:00:00 2001 From: Remi Lehe Date: Tue, 17 Sep 2024 11:05:37 -0700 Subject: [PATCH 15/15] Change figure link --- Examples/Physics_applications/beam_beam_collision/README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Physics_applications/beam_beam_collision/README.rst b/Examples/Physics_applications/beam_beam_collision/README.rst index 1f1c7d63158..28fdc1ee70e 100644 --- a/Examples/Physics_applications/beam_beam_collision/README.rst +++ b/Examples/Physics_applications/beam_beam_collision/README.rst @@ -68,7 +68,7 @@ For the large-scale simulation we have used the following options: qed_bw.save_table_in=my_bw_table.txt -.. figure:: https://user-images.githubusercontent.com/17280419/291749626-aa61fff2-e6d2-45a3-80ee-84b2851ea0bf.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTEiLCJleHAiOjE3MDMwMzQzNTEsIm5iZiI6MTcwMzAzNDA1MSwicGF0aCI6Ii8xNzI4MDQxOS8yOTE3NDk2MjYtYWE2MWZmZjItZTZkMi00NWEzLTgwZWUtODRiMjg1MWVhMGJmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFJV05KWUFYNENTVkVINTNBJTJGMjAyMzEyMjAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjMxMjIwVDAxMDA1MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWFiYzY2MGQyYzIyZGIzYzUxOWI3MzNjZTk5ZDM1YzgyNmY4ZDYxOGRlZjAyZTIwNTAyMTc3NTgwN2Q0YjEwNGMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.I96LQpjqmFXirPDVnBlFQIkCuenR6IuOSY0OIIQvtCo +.. figure:: https://gist.github.com/user-attachments/assets/2dd43782-d039-4faa-9d27-e3cf8fb17352 :alt: Beam-beam collision benchmark against :cite:t:`ex-Yakimenko2019`. :width: 100%