aboutsummaryrefslogtreecommitdiff
path: root/Examples/Modules/qed/quantum_synchrotron
diff options
context:
space:
mode:
Diffstat (limited to 'Examples/Modules/qed/quantum_synchrotron')
-rwxr-xr-xExamples/Modules/qed/quantum_synchrotron/analysis.py298
-rwxr-xr-xExamples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py60
-rw-r--r--Examples/Modules/qed/quantum_synchrotron/inputs_2d224
-rw-r--r--Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init122
-rw-r--r--Examples/Modules/qed/quantum_synchrotron/inputs_3d224
5 files changed, 746 insertions, 182 deletions
diff --git a/Examples/Modules/qed/quantum_synchrotron/analysis.py b/Examples/Modules/qed/quantum_synchrotron/analysis.py
new file mode 100755
index 000000000..5c2f778a1
--- /dev/null
+++ b/Examples/Modules/qed/quantum_synchrotron/analysis.py
@@ -0,0 +1,298 @@
+#! /usr/bin/env python
+
+# Copyright 2019 Luca Fedeli, Maxence Thevenet
+#
+# This file is part of WarpX.
+#
+# License: BSD-3-Clause-LBNL
+
+# -*- coding: utf-8 -*-
+
+import yt
+import numpy as np
+import sys
+import scipy.special as spe
+import scipy.integrate as integ
+import scipy.stats as st
+sys.path.insert(1, '../../../../warpx/Regression/Checksum/')
+import checksumAPI
+
+import matplotlib.pyplot as plt
+
+# This script performs detailed checks of the Quantum Synchrotron photon emission process.
+# Two electron populations and two positron populations are initialized with different momenta in different
+# directions in a background EM field (with non-zero components along each direction).
+# Specifically the script checks that:
+#
+# - The expected number of generated photons n_phot is in agreement with theory.
+# The maximum tolerated error is 5*sqrt(n_phot), except for the last 8 points,
+# for which a higher tolerance is used (this is due to the fact that the resolution
+# of the builtin table is quite limited).
+# - The weight of the generated particles is equal to the weight of the photon
+# - The generated particles are emitted in the right direction
+# - The energy distribution of the generated particles is in agreement with theory
+# - The optical depths of the product species are correctly initialized (QED effects are
+# enabled for product species too).
+#
+# More details on the theoretical formulas used in this script can be found in
+# the Jupyter notebook picsar/src/multi_physics/QED_tests/validation/validation.ipynb
+#
+# References:
+# 1) R. Duclous et al 2011 Plasma Phys. Control. Fusion 53 015009
+# 2) A. Gonoskov et al. 2015 Phys. Rev. E 92, 023305
+# 3) M. Lobet. PhD thesis "Effets radiatifs et d'electrodynamique
+# quantique dans l'interaction laser-matiere ultra-relativiste"
+# URL: https://tel.archives-ouvertes.fr/tel-01314224
+
+
+# Tolerances
+tol = 1.e-8
+tol_red = 1.e-2
+
+# Physical constants (from CODATA 2018, see: https://physics.nist.gov/cuu/Constants/index.html )
+me = 9.1093837015e-31 #electron mass
+c = 299792458 #speed of light
+hbar = 6.62607015e-34/(2*np.pi) #reduced Plank constant
+fine_structure = 7.2973525693e-3 #fine structure constant
+qe = 1.602176634e-19#elementary charge
+E_s = (me**2 * c**3)/(qe * hbar) #Schwinger E field
+B_s = E_s/c #Schwinger B field
+
+mec = me*c
+mec2 = mec*c
+#______________
+
+# Initial parameters
+spec_names = ["p1", "p2", "p3", "p4"]
+spec_names_phot = ["qsp_1", "qsp_2", "qsp_3", "qsp_4"]
+initial_momenta = [
+ np.array([10.0,0,0])*mec,
+ np.array([0.0,100.0,0.0])*mec,
+ np.array([0.0,0.0,1000.0])*mec,
+ np.array([5773.502691896, 5773.502691896, 5773.502691896])*mec
+]
+csign = [-1,-1,1,1]
+initial_particle_number = 1048576
+
+E_f = np.array([-2433321316961438., 973328526784575., 1459992790176863.])
+B_f = np.array([2857142.85714286, 4285714.28571428, 8571428.57142857])
+
+NNS = [64,64,64,64] #bins for energy distribution comparison.
+#______________
+
+def calc_chi_part(p, E, B):
+ gamma_part = np.sqrt(1.0 + np.dot(p,p)/mec**2)
+ v = p/(gamma_part*me)
+ EpvvecB = E + np.cross(v,B)
+ vdotEoverc = np.dot(v,E)/c
+ ff = np.sqrt(np.dot(EpvvecB,EpvvecB) - np.dot(vdotEoverc,vdotEoverc))
+ return gamma_part*ff/E_s
+
+#Auxiliary functions
+@np.vectorize
+def IC_inner_alternative(y):
+ ff = lambda x : np.exp(-y*(1+(4*x**2)/3)*np.sqrt(1+x*x/3))*(9+36*x**2 + 16*x**4)/(3 + 4*x**2)/np.sqrt(1+(x**2)/3)
+ return integ.quad(ff, 0, np.inf)[0]/np.sqrt(3)
+
+def IC_Y(chi_ele, xi):
+ div = (chi_ele*(1-xi))
+ div = np.where(np.logical_and(xi < 1, chi_ele != 0), div, 1.0);
+ res = (2/3)*np.where(np.logical_and(xi < 1, chi_ele != 0), xi/div, np.inf)
+ return res
+
+def IC_S(chi_ele, xi):
+ Y = IC_Y(chi_ele, xi)
+ coeff = np.sqrt(3)/2.0/np.pi
+ first = IC_inner_alternative(Y)
+ div = np.where(xi == 1, 1.0, 1.0/(1-xi) )
+ res = np.where(np.logical_or(xi == 1, xi == 0), 0.0,
+ coeff*xi*( first + (xi**2 * spe.kv(2./3.,Y)*div ) ) )
+ return res
+
+def IC_SXI(chi_ele, xi):
+ div = np.where(xi != 0, xi, 1.0)
+ return np.where(xi != 0, IC_S(chi_ele, xi)/div, np.inf)
+
+@np.vectorize
+def IC_G(chi_ele):
+ return integ.quad(lambda xi: IC_SXI(chi_ele, xi), 0, 1)[0]
+
+def small_diff(vv, val):
+ if(val != 0.0):
+ return np.max(np.abs((vv - val)/val)) < tol
+ else:
+ return np.max(np.abs(vv)) < tol
+
+def boris(pp, dt, charge_sign):
+ econst = 0.5*qe*dt*charge_sign/me
+ u = pp/(me)
+ u += econst*E_f
+ inv_gamma = 1/np.sqrt(1 + np.dot(u,u)/c**2)
+ t = econst*B_f*inv_gamma
+ s = 2*t/(1 + np.dot(t,t))
+ u_p = u + np.cross(u,t)
+ u += np.cross(u_p, s)
+ u += econst*E_f
+ return u *me
+#__________________
+
+# Quantum Synchrotron total and differential cross sections
+def QS_dN_dt(chi_ele, gamma_ele):
+ coeff_IC = (2./3.) * fine_structure * me*c**2/hbar
+ return coeff_IC*IC_G(chi_ele)/gamma_ele
+
+def QS_d2N_dt_dchi(chi, gamma_ele, chi_phot):
+ coeff_IC = (2./3.) * fine_structure * me*c**2/hbar
+ return coeff_IC*IC_S(chi, chi_phot/chi)/chi_phot/gamma_ele
+#__________________
+
+# Get data for a species
+def get_spec(ytdata, specname, is_photon):
+ px = ytdata[specname,"particle_momentum_x"].v
+ pz = ytdata[specname,"particle_momentum_z"].v
+ py = ytdata[specname,"particle_momentum_y"].v
+
+ w = ytdata[specname,"particle_weighting"].v
+
+ if (is_photon):
+ opt = ytdata[specname,"particle_optical_depth_BW"].v
+ else:
+ opt = ytdata[specname,"particle_optical_depth_QSR"].v
+
+ return {"px" : px, "py" : py, "pz" : pz, "w" : w, "opt" : opt}
+
+# Individual tests
+def check_number_of_photons(ytdataset, part_name, phot_name, chi_part, gamma_part, dt, particle_number):
+ dNQS_dt_theo = QS_dN_dt(chi_part, gamma_part)
+ expected_photons = (1.-np.exp(-dNQS_dt_theo*dt))*particle_number
+ expected_photons_tolerance = 5.0*np.sqrt(expected_photons)
+ n_phot = ytdataset.particle_type_counts[phot_name]
+ assert( np.abs(n_phot-expected_photons) < expected_photons_tolerance)
+ print(" [OK] generated photons number is within expectations")
+ return n_phot
+
+def check_weights(part_data, phot_data):
+ assert(np.all(part_data["w"] == part_data["w"][0]))
+ assert(np.all(phot_data["w"] == part_data["w"][0]))
+ print(" [OK] particles weights are the expected ones")
+
+def check_momenta(phot_data, p_phot, p0):
+ pdir = p0/np.linalg.norm(p0)
+ assert(small_diff(phot_data["px"]/p_phot, pdir[0]))
+ assert(small_diff(phot_data["py"]/p_phot, pdir[1]))
+ assert(small_diff(phot_data["pz"]/p_phot, pdir[2]))
+ print(" [OK] photons move along the initial particle direction")
+
+def check_opt_depths(part_data, phot_data):
+ data = (part_data, phot_data)
+ for dd in data:
+ loc, scale = st.expon.fit(dd["opt"])
+ assert( np.abs(loc - 0) < tol_red )
+ assert( np.abs(scale - 1) < tol_red )
+ print(" [OK] optical depth distributions are still exponential")
+
+def check_energy_distrib(gamma_phot, chi_part,
+ gamma_part, n_phot, NN, idx, do_plot=False):
+ gamma_phot_min = 1e-12*gamma_part
+ gamma_phot_max = gamma_part
+ h_log_gamma_phot, c_gamma_phot = np.histogram(gamma_phot, bins=np.logspace(np.log10(gamma_phot_min),np.log10(gamma_phot_max),NN+1))
+
+ cchi_phot_min = chi_part*(gamma_phot_min)/(gamma_part-1)
+ cchi_phot_max = chi_part*(gamma_phot_max)/(gamma_part-1)
+
+ #Rudimentary integration over npoints for each bin
+ npoints= 20
+ aux_chi = np.logspace(np.log10(cchi_phot_min),np.log10(cchi_phot_max), NN*npoints)
+ distrib = QS_d2N_dt_dchi(chi_part, gamma_part, aux_chi)*aux_chi
+ distrib = np.sum(distrib.reshape(-1, npoints),1)
+ distrib = n_phot*distrib/np.sum(distrib)
+
+ if do_plot :
+ # Visual comparison of distributions
+ c_gamma_phot = np.exp(0.5*(np.log(c_gamma_phot[1:])+np.log(c_gamma_phot[:-1])))
+ plt.clf()
+
+ fig, (ax1, ax2) = plt.subplots(1, 2)
+ fig.suptitle("χ_particle = {:f}".format(chi_part))
+ ax1.plot(c_gamma_phot, distrib,label="theory")
+ ax1.loglog(c_gamma_phot, h_log_gamma_phot,label="QSR photons")
+ ax1.set_xlim(1e-12*(gamma_part-1),gamma_part-1)
+ ax1.set_ylim(1,1e5)
+ ax2.plot(c_gamma_phot, distrib,label="theory")
+ ax2.semilogy(c_gamma_phot, h_log_gamma_phot,label="QSR photons")
+ ax2.set_ylim(1,1e5)
+ ax2.set_xlim(1e-12*(gamma_part-1),gamma_part-1)
+ ax1.set_xlabel("γ_photon")
+ ax1.set_xlabel("N")
+ ax2.set_xlabel("γ_photon")
+ ax2.set_xlabel("N")
+ plt.legend()
+ plt.savefig("case_{:d}".format(idx+1))
+
+ discr = np.abs(h_log_gamma_phot-distrib)
+
+ max_discr = np.sqrt(distrib)*5.0
+ # Use a higer tolerance for the last 8 points (this is due to limitations
+ # of the builtin table)
+ max_discr[-8:] *= 2.0
+ assert(np.all( discr < max_discr ))
+
+ print(" [OK] energy distribution is within expectations")
+
+#__________________
+
+def check():
+ filename_end = sys.argv[1]
+ data_set_end = yt.load(filename_end)
+
+ sim_time = data_set_end.current_time.to_value()
+ all_data_end = data_set_end.all_data()
+
+ for idx in range(4):
+ part_name = spec_names[idx]
+ phot_name = spec_names_phot[idx]
+ t_pi = initial_momenta[idx]
+ pm = boris(t_pi,-sim_time*0.5,csign[idx])
+ p0 = boris(pm,sim_time*1.0,csign[idx])
+
+ p2_part = p0[0]**2 + p0[1]**2 + p0[2]**2
+ energy_part = np.sqrt(mec2**2 + p2_part*c**2)
+ gamma_part = energy_part/mec2
+ chi_part = calc_chi_part(p0, E_f, B_f)
+
+ print("** Case {:d} **".format(idx+1))
+ print(" initial momentum: ", t_pi)
+ print(" quantum parameter: {:f}".format(chi_part))
+ print(" normalized particle energy: {:f}".format(gamma_part))
+ print(" timestep: {:f} fs".format(sim_time*1e15))
+
+ part_data_final = get_spec(all_data_end, part_name, is_photon=False)
+ phot_data = get_spec(all_data_end, phot_name, is_photon=True)
+
+ p_phot = np.sqrt(phot_data["px"]**2 + phot_data["py"]**2 + phot_data["pz"]**2)
+ energy_phot = p_phot*c
+ gamma_phot = energy_phot/mec2
+
+ n_phot = check_number_of_photons(data_set_end,
+ part_name, phot_name,
+ chi_part, gamma_part, sim_time,
+ initial_particle_number)
+
+ check_weights(part_data_final, phot_data)
+
+ check_momenta(phot_data, p_phot, p0)
+
+ check_energy_distrib(gamma_phot, chi_part, gamma_part, n_phot, NNS[idx], idx)
+
+ check_opt_depths(part_data_final, phot_data)
+
+ print("*************\n")
+
+ test_name = filename_end[:-9] # Could also be os.path.split(os.getcwd())[1]
+ checksumAPI.evaluate_checksum(test_name, filename_end)
+
+def main():
+ check()
+
+if __name__ == "__main__":
+ main()
diff --git a/Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py b/Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py
deleted file mode 100755
index b21441fb7..000000000
--- a/Examples/Modules/qed/quantum_synchrotron/analysis_2d_tau_init.py
+++ /dev/null
@@ -1,60 +0,0 @@
-#! /usr/bin/env python
-
-# Copyright 2019 Luca Fedeli, Maxence Thevenet
-#
-# This file is part of WarpX.
-#
-# License: BSD-3-Clause-LBNL
-
-import yt
-import numpy as np
-import scipy.stats as st
-import sys
-sys.path.insert(1, '../../../../warpx/Regression/Checksum/')
-import checksumAPI
-
-# This script checks if electrons and positrons initialized with
-# Quantum Synchrotron process enabled
-# do actually have an exponentially distributed optical depth
-
-# Tolerance
-tolerance_rel = 1e-2
-print("tolerance_rel: " + str(tolerance_rel))
-
-def check():
- filename = sys.argv[1]
- data_set = yt.load(filename)
-
- all_data = data_set.all_data()
- res_ele_tau = all_data["electrons", 'particle_optical_depth_QSR']
- res_pos_tau = all_data["positrons", 'particle_optical_depth_QSR']
-
- loc_ele, scale_ele = st.expon.fit(res_ele_tau)
- loc_pos, scale_pos = st.expon.fit(res_pos_tau)
-
- # loc should be very close to 0, scale should be very close to 1
- error_rel = np.abs(loc_ele - 0)
- print("error_rel loc_ele: " + str(error_rel))
- assert( error_rel < tolerance_rel )
-
- error_rel = np.abs(loc_pos - 0)
- print("error_rel loc_pos: " + str(error_rel))
- assert( error_rel < tolerance_rel )
-
- error_rel = np.abs(scale_ele - 1)
- print("error_rel scale_ele: " + str(error_rel))
- assert( error_rel < tolerance_rel )
-
- error_rel = np.abs(scale_pos - 1)
- print("error_rel scale_pos: " + str(error_rel))
- assert( error_rel < tolerance_rel )
-
- test_name = filename[:-9] # Could also be os.path.split(os.getcwd())[1]
- checksumAPI.evaluate_checksum(test_name, filename)
-
-def main():
- check()
-
-if __name__ == "__main__":
- main()
-
diff --git a/Examples/Modules/qed/quantum_synchrotron/inputs_2d b/Examples/Modules/qed/quantum_synchrotron/inputs_2d
new file mode 100644
index 000000000..c57e04793
--- /dev/null
+++ b/Examples/Modules/qed/quantum_synchrotron/inputs_2d
@@ -0,0 +1,224 @@
+#################################
+####### GENERAL PARAMETERS ######
+#################################
+max_step = 1
+amr.n_cell = 32 32
+amr.max_grid_size = 16 # maximum size of each AMReX box, used to decompose the domain
+amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain
+geometry.coord_sys = 0 # 0: Cartesian
+geometry.is_periodic = 1 1 1 # Is periodic?
+geometry.prob_lo = -0.25e-6 -0.25e-6 # physical domain
+geometry.prob_hi = 0.25e-6 0.25e-6
+amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported)
+
+#################################
+############ NUMERICS ###########
+#################################
+algo.current_deposition = esirkepov
+algo.charge_deposition = standard
+algo.field_gathering = energy-conserving
+algo.particle_pusher = boris
+interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z
+interpolation.noy = 3
+interpolation.noz = 3
+warpx.verbose = 1
+warpx.do_dive_cleaning = 0
+warpx.use_filter = 1
+warpx.cfl = 1. # if 1., the time step is set to its CFL limit
+warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition
+warpx.serialize_ics = 1
+
+#################################
+############ PLASMA #############
+#################################
+particles.species_names = p1 p2 p3 p4 qsp_1 qsp_2 qsp_3 qsp_4 dummy_ele dummy_pos
+particles.photon_species = qsp_1 qsp_2 qsp_3 qsp_4
+#################################
+
+p1.species_type = "electron"
+p1.injection_style = "NUniformPerCell"
+p1.profile = "constant"
+p1.num_particles_per_cell_each_dim = 32 32
+p1.density = 1e1
+p1.profile = "constant"
+p1.momentum_distribution_type = "gaussian"
+p1.ux_m = 10.0
+##########QED####################
+p1.do_qed = 1
+p1.do_qed_quantum_sync = 1
+p1.qed_quantum_sync_phot_product_species = qsp_1
+#################################
+
+p2.species_type = "electron"
+p2.injection_style = "NUniformPerCell"
+p2.profile = "constant"
+p2.num_particles_per_cell_each_dim = 32 32
+p2.density = 1e1
+p2.profile = "constant"
+p2.momentum_distribution_type = "gaussian"
+p2.uy_m = 100.0
+##########QED####################
+p2.do_qed = 1
+p2.do_qed_quantum_sync = 1
+p2.qed_quantum_sync_phot_product_species = qsp_2
+#################################
+
+p3.species_type = "positron"
+p3.injection_style = "NUniformPerCell"
+p3.profile = "constant"
+p3.num_particles_per_cell_each_dim = 32 32
+p3.density = 1e1
+p3.profile = "constant"
+p3.momentum_distribution_type = "gaussian"
+p3.uz_m = 1000.0
+##########QED####################
+p3.do_qed = 1
+p3.do_qed_quantum_sync = 1
+p3.qed_quantum_sync_phot_product_species = qsp_3
+#################################
+
+p4.species_type = "positron"
+p4.injection_style = "NUniformPerCell"
+p4.profile = "constant"
+p4.num_particles_per_cell_each_dim = 32 32
+p4.density = 1e1
+p4.profile = "constant"
+p4.momentum_distribution_type = "gaussian"
+p4.ux_m = 5773.502691896
+p4.uy_m = 5773.502691896
+p4.uz_m = 5773.502691896
+##########QED####################
+p4.do_qed = 1
+p4.do_qed_quantum_sync = 1
+p4.qed_quantum_sync_phot_product_species = qsp_4
+#################################
+
+### PRODUCT SPECIES ###
+qsp_1.species_type = "photon"
+qsp_1.injection_style = nuniformpercell
+qsp_1.num_particles_per_cell_each_dim = 0 0
+qsp_1.profile = constant
+qsp_1.density = 0.0
+qsp_1.momentum_distribution_type = "gaussian"
+qsp_1.do_qed = 1
+qsp_1.do_qed_breit_wheeler = 1
+qsp_1.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_1.qed_breit_wheeler_pos_product_species = dummy_pos
+
+qsp_2.species_type = "photon"
+qsp_2.injection_style = nuniformpercell
+qsp_2.num_particles_per_cell_each_dim = 0 0
+qsp_2.profile = constant
+qsp_2.density = 0.0
+qsp_2.momentum_distribution_type = "gaussian"
+qsp_2.do_qed = 1
+qsp_2.do_qed_breit_wheeler = 1
+qsp_2.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_2.qed_breit_wheeler_pos_product_species = dummy_pos
+
+qsp_3.species_type = "photon"
+qsp_3.injection_style = nuniformpercell
+qsp_3.num_particles_per_cell_each_dim = 0 0
+qsp_3.profile = constant
+qsp_3.density = 0.0
+qsp_3.momentum_distribution_type = "gaussian"
+qsp_3.do_qed = 1
+qsp_3.do_qed_breit_wheeler = 1
+qsp_3.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_3.qed_breit_wheeler_pos_product_species = dummy_pos
+
+qsp_4.species_type = "photon"
+qsp_4.injection_style = nuniformpercell
+qsp_4.num_particles_per_cell_each_dim = 0 0
+qsp_4.profile = constant
+qsp_4.density = 0.0
+qsp_4.momentum_distribution_type = "gaussian"
+qsp_4.do_qed = 1
+qsp_4.do_qed_breit_wheeler = 1
+qsp_4.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_4.qed_breit_wheeler_pos_product_species = dummy_pos
+
+#################################
+
+dummy_ele.species_type = "electron"
+dummy_ele.injection_style = nuniformpercell
+dummy_ele.num_particles_per_cell_each_dim = 0 0
+dummy_ele.profile = constant
+dummy_ele.density = 0.0
+dummy_ele.momentum_distribution_type = "gaussian"
+dummy_ele.do_qed = 0
+
+dummy_pos.species_type = "positron"
+dummy_pos.injection_style = nuniformpercell
+dummy_pos.num_particles_per_cell_each_dim = 0 0
+dummy_pos.profile = constant
+dummy_pos.density = 0.0
+dummy_pos.momentum_distribution_type = "gaussian"
+dummy_pos.do_qed = 0
+
+
+#################################
+
+##########QED TABLES####################
+qed_bw.chi_min = 0.001
+
+qed_bw.lookup_table_mode = "builtin"
+
+#qed_bw.lookup_table_mode = "generate"
+#qed_bw.tab_dndt_chi_min = 0.01
+#qed_bw.tab_dndt_chi_max = 1000.0
+#qed_bw.tab_dndt_how_many = 256
+#qed_bw.tab_pair_chi_min = 0.01
+#qed_bw.tab_pair_chi_max = 1000.0
+#qed_bw.tab_pair_chi_how_many = 256
+#qed_bw.tab_pair_frac_how_many = 256
+#qed_bw.save_table_in = "bw_table"
+
+#qed_bw.lookup_table_mode = "load"
+#qed_bw.load_table_from = "bw_table"
+
+qed_qs.chi_min = 0.001
+
+qed_qs.lookup_table_mode = "builtin"
+
+qed_qs.photon_creation_energy_threshold = 0.0
+
+#qed_qs.lookup_table_mode = "generate"
+#qed_qs.tab_dndt_chi_min = 0.001
+#qed_qs.tab_dndt_chi_max = 1000.0
+#qed_qs.tab_dndt_how_many = 256
+#qed_qs.tab_em_chi_min = 0.001
+#qed_qs.tab_em_frac_min = 1.0e-12
+#qed_qs.tab_em_chi_max = 1000.0
+#qed_qs.tab_em_chi_how_many = 256
+#qed_qs.tab_em_frac_how_many = 256
+#qed_qs.save_table_in = "qs_table"
+
+#qed_qs.lookup_table_mode = "load"
+#qed_qs.load_table_from = "qs_table"
+#################################
+
+### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] )
+particles.E_ext_particle_init_style = "constant"
+particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863
+####
+
+### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] )
+particles.B_ext_particle_init_style = "constant"
+particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857
+####
+
+# Diagnostics
+diagnostics.diags_names = diag1
+diag1.period = 1
+diag1.diag_type = Full
+diag1.fields_to_plot = Ex
+diag1.p1.variables = ux uy uz w
+diag1.p2.variables = ux uy uz w
+diag1.p3.variables = ux uy uz w
+diag1.p4.variables = ux uy uz w
+
+diag1.qsp_1.variables = ux uy uz w
+diag1.qsp_2.variables = ux uy uz w
+diag1.qsp_3.variables = ux uy uz w
+diag1.qsp_4.variables = ux uy uz w
diff --git a/Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init b/Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init
deleted file mode 100644
index a29adf287..000000000
--- a/Examples/Modules/qed/quantum_synchrotron/inputs_2d_tau_init
+++ /dev/null
@@ -1,122 +0,0 @@
-#################################
-####### GENERAL PARAMETERS ######
-#################################
-max_step = 1
-amr.n_cell = 128 128
-amr.max_grid_size = 128 # maximum size of each AMReX box, used to decompose the domain
-amr.blocking_factor = 32 # minimum size of each AMReX box, used to decompose the domain
-geometry.coord_sys = 0 # 0: Cartesian
-geometry.is_periodic = 0 0 # Is periodic?
-geometry.prob_lo = -32.e-6 -32.e-6 # physical domain
-geometry.prob_hi = 32.e-6 32.e-6
-amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported)
-
-#################################
-############ NUMERICS ###########
-#################################
-algo.current_deposition = esirkepov
-algo.charge_deposition = standard
-algo.field_gathering = energy-conserving
-algo.particle_pusher = boris
-interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z
-interpolation.noy = 3
-interpolation.noz = 3
-warpx.verbose = 1
-warpx.do_dive_cleaning = 0
-warpx.use_filter = 1
-warpx.cfl = 1. # if 1., the time step is set to its CFL limit
-warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition
-warpx.serialize_ics = 1
-
-
-#################################
-############ PLASMA #############
-#################################
-particles.species_names = electrons positrons qs_phot
-particles.photon_species = qs_phot
-#################################
-
-electrons.species_type = "electron"
-electrons.injection_style = "NUniformPerCell"
-electrons.profile = "constant"
-electrons.xmin = -30e-6
-electrons.ymin = -30e-6
-electrons.zmin = -30e-6
-electrons.xmax = 30e-6
-electrons.ymax = 30e-6
-electrons.zmax = 30e-6
-electrons.num_particles_per_cell_each_dim = 2 2
-electrons.density = 1e19
-electrons.profile = "constant"
-electrons.momentum_distribution_type = "gaussian"
-electrons.ux_m = 0.0
-electrons.uy_m = 0.0
-electrons.uz_m = 0.0
-electrons.ux_th = 100.
-electrons.uy_th = 100.
-electrons.uz_th = 100.
-##########QED####################
-electrons.do_qed = 1
-electrons.do_qed_quantum_sync = 1
-electrons.qed_quantum_sync_phot_product_species = qs_phot
-#################################
-
-positrons.species_type = "positron"
-positrons.injection_style = "NUniformPerCell"
-positrons.profile = "constant"
-positrons.xmin = -30e-6
-positrons.ymin = -30e-6
-positrons.zmin = -30e-6
-positrons.xmax = 30e-6
-positrons.ymax = 30e-6
-positrons.zmax = 30e-6
-positrons.num_particles_per_cell_each_dim = 2 2
-positrons.density = 1e19
-positrons.profile = "constant"
-positrons.momentum_distribution_type = "gaussian"
-positrons.ux_m = 0.0
-positrons.uy_m = 0.0
-positrons.uz_m = 0.0
-positrons.ux_th = 100.
-positrons.uy_th = 100.
-positrons.uz_th = 100.
-##########QED####################
-positrons.do_qed = 1
-positrons.do_qed_quantum_sync = 1
-positrons.qed_quantum_sync_phot_product_species = qs_phot
-#################################
-
-### PRODUCT SPECIES ###
-qs_phot.species_type = "photon"
-qs_phot.injection_style = nuniformpercell
-qs_phot.num_particles_per_cell_each_dim = 1 1
-qs_phot.profile = constant
-qs_phot.xmin = 1 ## Ugly trick to avoid photons at T=0
-qs_phot.xmax = -1 ## Ugly trick to avoid photons at T=0
-qs_phot.density = 0.0
-qs_phot.momentum_distribution_type = "gaussian"
-#################################
-
-#######QED ENGINE PARAMETERS#####
-qed_qs.lookup_table_mode = "dummy_builtin"
-qed_qs.photon_creation_energy_threshold = 1.63742112993e-13
-
-#qed_qs.lookup_table_mode = "generate"
-#qed_qs.chi_min = 0.001
-#qed_qs.tab_dndt_chi_min = 0.001
-#qed_qs.tab_dndt_chi_max = 200
-#qed_qs.tab_dndt_how_many = 32
-#qed_qs.tab_em_chi_min = 0.001
-#qed_qs.tab_em_chi_max = 200
-#qed_qs.tab_em_chi_how_many = 2
-#qed_qs.tab_em_prob_how_many = 2
-#qed_qs.save_table_in = "qs_micro_table"
-
-#qed_qs.lookup_table_mode = "load"
-#qed_qs.load_table_from = "qs_micro_table"
-#################################
-
-# Diagnostics
-diagnostics.diags_names = diag1
-diag1.period = 1
-diag1.diag_type = Full
diff --git a/Examples/Modules/qed/quantum_synchrotron/inputs_3d b/Examples/Modules/qed/quantum_synchrotron/inputs_3d
new file mode 100644
index 000000000..66670b4db
--- /dev/null
+++ b/Examples/Modules/qed/quantum_synchrotron/inputs_3d
@@ -0,0 +1,224 @@
+#################################
+####### GENERAL PARAMETERS ######
+#################################
+max_step = 1
+amr.n_cell = 16 16 16
+amr.max_grid_size = 16 # maximum size of each AMReX box, used to decompose the domain
+amr.blocking_factor = 8 # minimum size of each AMReX box, used to decompose the domain
+geometry.coord_sys = 0 # 0: Cartesian
+geometry.is_periodic = 1 1 1 # Is periodic?
+geometry.prob_lo = -0.25e-6 -0.25e-6 -0.25e-6 # physical domain
+geometry.prob_hi = 0.25e-6 0.25e-6 0.25e-6
+amr.max_level = 0 # Maximum level in hierarchy (1 might be unstable, >1 is not supported)
+
+#################################
+############ NUMERICS ###########
+#################################
+algo.current_deposition = esirkepov
+algo.charge_deposition = standard
+algo.field_gathering = energy-conserving
+algo.particle_pusher = boris
+interpolation.nox = 3 # Particle interpolation order. Must be the same in x, y, and z
+interpolation.noy = 3
+interpolation.noz = 3
+warpx.verbose = 1
+warpx.do_dive_cleaning = 0
+warpx.use_filter = 1
+warpx.cfl = 1. # if 1., the time step is set to its CFL limit
+warpx.do_pml = 1 # use Perfectly Matched Layer as boundary condition
+warpx.serialize_ics = 1
+
+#################################
+############ PLASMA #############
+#################################
+particles.species_names = p1 p2 p3 p4 qsp_1 qsp_2 qsp_3 qsp_4 dummy_ele dummy_pos
+particles.photon_species = qsp_1 qsp_2 qsp_3 qsp_4
+#################################
+
+p1.species_type = "electron"
+p1.injection_style = "NUniformPerCell"
+p1.profile = "constant"
+p1.num_particles_per_cell_each_dim = 8 8 4
+p1.density = 1e1
+p1.profile = "constant"
+p1.momentum_distribution_type = "gaussian"
+p1.ux_m = 10.0
+##########QED####################
+p1.do_qed = 1
+p1.do_qed_quantum_sync = 1
+p1.qed_quantum_sync_phot_product_species = qsp_1
+#################################
+
+p2.species_type = "electron"
+p2.injection_style = "NUniformPerCell"
+p2.profile = "constant"
+p2.num_particles_per_cell_each_dim = 8 8 4
+p2.density = 1e1
+p2.profile = "constant"
+p2.momentum_distribution_type = "gaussian"
+p2.uy_m = 100.0
+##########QED####################
+p2.do_qed = 1
+p2.do_qed_quantum_sync = 1
+p2.qed_quantum_sync_phot_product_species = qsp_2
+#################################
+
+p3.species_type = "positron"
+p3.injection_style = "NUniformPerCell"
+p3.profile = "constant"
+p3.num_particles_per_cell_each_dim = 8 8 4
+p3.density = 1e1
+p3.profile = "constant"
+p3.momentum_distribution_type = "gaussian"
+p3.uz_m = 1000.0
+##########QED####################
+p3.do_qed = 1
+p3.do_qed_quantum_sync = 1
+p3.qed_quantum_sync_phot_product_species = qsp_3
+#################################
+
+p4.species_type = "positron"
+p4.injection_style = "NUniformPerCell"
+p4.profile = "constant"
+p4.num_particles_per_cell_each_dim = 8 8 4
+p4.density = 1e1
+p4.profile = "constant"
+p4.momentum_distribution_type = "gaussian"
+p4.ux_m = 5773.502691896
+p4.uy_m = 5773.502691896
+p4.uz_m = 5773.502691896
+##########QED####################
+p4.do_qed = 1
+p4.do_qed_quantum_sync = 1
+p4.qed_quantum_sync_phot_product_species = qsp_4
+#################################
+
+### PRODUCT SPECIES ###
+qsp_1.species_type = "photon"
+qsp_1.injection_style = nuniformpercell
+qsp_1.num_particles_per_cell_each_dim = 0 0
+qsp_1.profile = constant
+qsp_1.density = 0.0
+qsp_1.momentum_distribution_type = "gaussian"
+qsp_1.do_qed = 1
+qsp_1.do_qed_breit_wheeler = 1
+qsp_1.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_1.qed_breit_wheeler_pos_product_species = dummy_pos
+
+qsp_2.species_type = "photon"
+qsp_2.injection_style = nuniformpercell
+qsp_2.num_particles_per_cell_each_dim = 0 0
+qsp_2.profile = constant
+qsp_2.density = 0.0
+qsp_2.momentum_distribution_type = "gaussian"
+qsp_2.do_qed = 1
+qsp_2.do_qed_breit_wheeler = 1
+qsp_2.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_2.qed_breit_wheeler_pos_product_species = dummy_pos
+
+qsp_3.species_type = "photon"
+qsp_3.injection_style = nuniformpercell
+qsp_3.num_particles_per_cell_each_dim = 0 0
+qsp_3.profile = constant
+qsp_3.density = 0.0
+qsp_3.momentum_distribution_type = "gaussian"
+qsp_3.do_qed = 1
+qsp_3.do_qed_breit_wheeler = 1
+qsp_3.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_3.qed_breit_wheeler_pos_product_species = dummy_pos
+
+qsp_4.species_type = "photon"
+qsp_4.injection_style = nuniformpercell
+qsp_4.num_particles_per_cell_each_dim = 0 0
+qsp_4.profile = constant
+qsp_4.density = 0.0
+qsp_4.momentum_distribution_type = "gaussian"
+qsp_4.do_qed = 1
+qsp_4.do_qed_breit_wheeler = 1
+qsp_4.qed_breit_wheeler_ele_product_species = dummy_ele
+qsp_4.qed_breit_wheeler_pos_product_species = dummy_pos
+
+#################################
+
+dummy_ele.species_type = "electron"
+dummy_ele.injection_style = nuniformpercell
+dummy_ele.num_particles_per_cell_each_dim = 0 0
+dummy_ele.profile = constant
+dummy_ele.density = 0.0
+dummy_ele.momentum_distribution_type = "gaussian"
+dummy_ele.do_qed = 0
+
+dummy_pos.species_type = "positron"
+dummy_pos.injection_style = nuniformpercell
+dummy_pos.num_particles_per_cell_each_dim = 0 0
+dummy_pos.profile = constant
+dummy_pos.density = 0.0
+dummy_pos.momentum_distribution_type = "gaussian"
+dummy_pos.do_qed = 0
+
+
+#################################
+
+##########QED TABLES####################
+qed_bw.chi_min = 0.001
+
+qed_bw.lookup_table_mode = "builtin"
+
+#qed_bw.lookup_table_mode = "generate"
+#qed_bw.tab_dndt_chi_min = 0.01
+#qed_bw.tab_dndt_chi_max = 1000.0
+#qed_bw.tab_dndt_how_many = 256
+#qed_bw.tab_pair_chi_min = 0.01
+#qed_bw.tab_pair_chi_max = 1000.0
+#qed_bw.tab_pair_chi_how_many = 256
+#qed_bw.tab_pair_frac_how_many = 256
+#qed_bw.save_table_in = "bw_table"
+
+#qed_bw.lookup_table_mode = "load"
+#qed_bw.load_table_from = "bw_table"
+
+qed_qs.chi_min = 0.001
+
+qed_qs.lookup_table_mode = "builtin"
+
+qed_qs.photon_creation_energy_threshold = 0.0
+
+#qed_qs.lookup_table_mode = "generate"
+#qed_qs.tab_dndt_chi_min = 0.001
+#qed_qs.tab_dndt_chi_max = 1000.0
+#qed_qs.tab_dndt_how_many = 256
+#qed_qs.tab_em_chi_min = 0.001
+#qed_qs.tab_em_frac_min = 1.0e-12
+#qed_qs.tab_em_chi_max = 1000.0
+#qed_qs.tab_em_chi_how_many = 256
+#qed_qs.tab_em_frac_how_many = 256
+#qed_qs.save_table_in = "qs_table"
+
+#qed_qs.lookup_table_mode = "load"
+#qed_qs.load_table_from = "qs_table"
+#################################
+
+### EXTERNAL E FIELD ### (3e15 * [-0.811107105653813 0.324442842261525 0.486664263392288] )
+particles.E_ext_particle_init_style = "constant"
+particles.E_external_particle = -2433321316961438 973328526784575 1459992790176863
+####
+
+### EXTERNAL B FIELD ### (1e7 * [0.28571429 0.42857143 0.85714286] )
+particles.B_ext_particle_init_style = "constant"
+particles.B_external_particle = 2857142.85714286 4285714.28571428 8571428.57142857
+####
+
+# Diagnostics
+diagnostics.diags_names = diag1
+diag1.period = 1
+diag1.diag_type = Full
+diag1.fields_to_plot = Ex
+diag1.p1.variables = ux uy uz w
+diag1.p2.variables = ux uy uz w
+diag1.p3.variables = ux uy uz w
+diag1.p4.variables = ux uy uz w
+
+diag1.qsp_1.variables = ux uy uz w
+diag1.qsp_2.variables = ux uy uz w
+diag1.qsp_3.variables = ux uy uz w
+diag1.qsp_4.variables = ux uy uz w