aboutsummaryrefslogtreecommitdiff
path: root/Examples/Modules/qed/breit_wheeler/analysis.py
diff options
context:
space:
mode:
authorGravatar Luca Fedeli <luca.fedeli@cea.fr> 2020-09-21 12:42:02 +0200
committerGravatar GitHub <noreply@github.com> 2020-09-21 12:42:02 +0200
commitf9f3aa6e96e9c7827bef1f449fa2ce3d86505a23 (patch)
treec26ef7e8aa0517e1df521d245fbe6bf2a6809adf /Examples/Modules/qed/breit_wheeler/analysis.py
parent789f0da95e409b035cfffcacbb75dc847243e30a (diff)
downloadWarpX-f9f3aa6e96e9c7827bef1f449fa2ce3d86505a23.tar.gz
WarpX-f9f3aa6e96e9c7827bef1f449fa2ce3d86505a23.tar.zst
WarpX-f9f3aa6e96e9c7827bef1f449fa2ce3d86505a23.zip
Coupling WarpX with an ✨improved✨ version of the QED library (#1198)
* Initial work to couple improved QED module to WarpX * WIP to couple with WarpX the new QED library * Continuing work to couple the new version of the QED library with WarpX * progress towards completing coupling with new version of QED library * WarpX coupled with new version of QED library * default behavior is to display table generation progress * some host device functions are now device only * fixed bug * bugfixing * updating tests * updated test * updated test * added initial version of tests (not working) * added check and updated a comment * fixed bug * added inputfiles and analysis script for new BW tests * test for BW process are ready * modified test * make lgtm happy * removed TABs * initial work to add QS tests (not working) * removed old tests * fixed bug in script * changed position of evolution of optical depth * progress with QSR tests * improved test * very low energy photons are always eliminated * added tests to regression suite * improved test * improved tests * removed redundant parameter * removed trailing white space * updated documentation * fix lgtm warnings * fixed missing check on chi parameter * fixed missing check on chi parameter & bugfixing * improved comments * increased tolerance in tests * updated units in test * now test succeds if the error is extremely small * updated checksums * fixed bug * fixed some unused or uninitialized variables warnings * now using ignore_unused instead of commenting out some variables * fixed warnings * partial fix of a test * fixed test * fixed test * added checksums * fixed tests * fixed benchmark for qed_schwinger2 * removed checksums for tests which do no exist anymore * fixed checksums for several qed tests * fixed checksums for several qed tests * fixed checksums * removed unwanted checksum * fixed checksum * removed files which should have been deleted * add some const * [skip ci] added some docstrings and some const * Update Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.H Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/ElementaryProcess/QEDInternals/BreitWheelerEngineWrapper.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/ElementaryProcess/QEDInternals/QuantumSyncEngineWrapper.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * added some docstrings and some const * replaced ManagedVectors with DeviceVectors * Update Source/Particles/ElementaryProcess/QEDInternals/QedWrapperCommons.H Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * added some const * removed unwanted assert * updated comment * changed position of GPU synchronization directive * Update Docs/source/running_cpp/parameters.rst Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Examples/Modules/qed/quantum_synchrotron/analysis.py Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Examples/Modules/qed/quantum_synchrotron/analysis.py Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Examples/Modules/qed/breit_wheeler/analysis.py Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Examples/Modules/qed/breit_wheeler/analysis.py Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * add do_plot option to some analysis scripts * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * uncomment a line * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * simplified input scripts for BW tests * simplified input scripts for QS tests * removed unwanted files * simplified analysis script * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * reverted modification to schwinger analysis script * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * remove outdated comment * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Source/Particles/MultiParticleContainer.cpp Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * fix warnings * made test more robust * reset benchmark for qed_breit_wheeler_2d * fixed bug in test * make test more robust * made test more robust * Update Examples/Modules/qed/quantum_synchrotron/analysis.py Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update Examples/Modules/qed/quantum_synchrotron/analysis.py Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> * Update run_test.sh Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja> Co-authored-by: Tools <warpx@lbl.gov> Co-authored-by: NeilZaim <49716072+NeilZaim@users.noreply.github.com> Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja>
Diffstat (limited to 'Examples/Modules/qed/breit_wheeler/analysis.py')
-rwxr-xr-xExamples/Modules/qed/breit_wheeler/analysis.py298
1 files changed, 298 insertions, 0 deletions
diff --git a/Examples/Modules/qed/breit_wheeler/analysis.py b/Examples/Modules/qed/breit_wheeler/analysis.py
new file mode 100755
index 000000000..280e8e2a8
--- /dev/null
+++ b/Examples/Modules/qed/breit_wheeler/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 Breit-Wheeler pair production process.
+# Four populations of photons 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 pairs n_pairs is in agreement with theory
+# (the maximum tolerated error is 5*sqrt(n_pairs)
+# - The weight of the generated particles is equal to the weight of the photon
+# - Momenta of the residual photons are still equal to the original momentum
+# - The generated particles are emitted in the right direction
+# - Total energy is conserved in each event
+# - 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 = 2.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_phot = ["p1", "p2", "p3", "p4"]
+spec_names_ele = ["ele1", "ele2", "ele3", "ele4"]
+spec_names_pos = ["pos1", "pos2", "pos3", "pos4"]
+initial_momenta = [
+ np.array([2000.0,0,0])*mec,
+ np.array([0.0,5000.0,0.0])*mec,
+ np.array([0.0,0.0,10000.0])*mec,
+ np.array([57735.02691896, 57735.02691896, 57735.02691896])*mec
+]
+initial_particle_number = 1048576
+
+E_f = np.array([-2433321316961438., 973328526784575., 1459992790176863.])
+B_f = np.array([2857142.85714286, 4285714.28571428, 8571428.57142857])
+
+NNS = [128,128,128,128] #bins for energy distribution comparison.
+#______________
+
+def calc_chi_gamma(p, E, B):
+ pnorm = np.linalg.norm(p)
+ v = c*(p/pnorm)
+ EpvvecB = E + np.cross(v,B)
+ vdotEoverc = np.dot(v,E)/c
+ ff = np.sqrt(np.dot(EpvvecB,EpvvecB) - np.dot(vdotEoverc,vdotEoverc))
+ gamma_phot = pnorm/mec
+ return gamma_phot*ff/E_s
+
+#Auxiliary functions
+@np.vectorize
+def BW_inner(x):
+ return integ.quad(lambda s: np.sqrt(s)*spe.kv(1./3., 2./3. * s**(3./2.)), x, np.inf)[0]
+
+def BW_X(chi_phot, chi_ele):
+ div = (chi_ele*(chi_phot-chi_ele))
+ div = np.where(np.logical_and(chi_phot > chi_ele, chi_ele != 0), div, 1.0);
+ res = np.where(np.logical_and(chi_phot > chi_ele, chi_ele != 0), np.power(chi_phot/div, 2./3.), np.inf)
+ return res
+
+def BW_F(chi_phot, chi_ele):
+ X = BW_X(chi_phot, chi_ele)
+ res = np.where(np.logical_or(chi_phot == chi_ele, chi_ele == 0), 0.0,
+ BW_inner(X) - (2.0 - chi_phot* X**(3./2.))*spe.kv(2./3., 2./3. * X**(3./2.)) )
+ return res
+
+@np.vectorize
+def BW_T(chi_phot):
+ coeff = 1./(np.pi * np.sqrt(3.) * (chi_phot**2))
+ return coeff*integ.quad(lambda chi_ele: BW_F(chi_phot, chi_ele), 0, chi_phot)[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
+#__________________
+
+# Breit-Wheeler total and differential cross sections
+def BW_dN_dt(chi_phot, gamma_phot):
+ coeff_BW = fine_structure * me*c**2/hbar
+ return coeff_BW*BW_T(chi_phot)*(chi_phot/gamma_phot)
+
+def BW_d2N_dt_dchi(chi_phot, gamma_phot, chi_ele):
+ coeff_BW = fine_structure * me*c**2/hbar
+ return coeff_BW*BW_F(chi_phot, chi_ele)*(gamma_phot/gamma_phot)
+#__________________
+
+# 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_pairs(ytdataset, phot_name, ele_name, pos_name, chi_phot, gamma_phot, dt, particle_number):
+ dNBW_dt_theo = BW_dN_dt(chi_phot, gamma_phot)
+ expected_pairs = (1.-np.exp(-dNBW_dt_theo*dt))*particle_number
+ expected_pairs_tolerance = 5.0*np.sqrt(expected_pairs)
+ n_ele = ytdataset.particle_type_counts[ele_name]
+ n_pos = ytdataset.particle_type_counts[pos_name]
+ n_phot = ytdataset.particle_type_counts[phot_name]
+ n_lost = initial_particle_number-n_phot
+ assert((n_ele == n_pos) and (n_ele == n_lost))
+ assert( np.abs(n_ele-expected_pairs) < expected_pairs_tolerance)
+ print(" [OK] generated pair number is within expectations")
+ return n_lost
+
+def check_weights(phot_data, ele_data, pos_data):
+ assert(np.all(phot_data["w"] == phot_data["w"][0]))
+ assert(np.all(ele_data["w"] == phot_data["w"][0]))
+ assert(np.all(pos_data["w"] == phot_data["w"][0]))
+ print(" [OK] particles weights are the expected ones")
+
+def check_momenta(phot_data, ele_data, pos_data, p0, p_ele, p_pos):
+ assert(small_diff(phot_data["px"], p0[0]))
+ assert(small_diff(phot_data["py"], p0[1]))
+ assert(small_diff(phot_data["pz"], p0[2]))
+ print(" [OK] residual photons still have initial momentum")
+
+ pdir = p0/np.linalg.norm(p0)
+ assert(small_diff(ele_data["px"]/p_ele, pdir[0]))
+ assert(small_diff(ele_data["py"]/p_ele, pdir[1]))
+ assert(small_diff(ele_data["pz"]/p_ele, pdir[2]))
+ assert(small_diff(pos_data["px"]/p_pos, pdir[0]))
+ assert(small_diff(pos_data["py"]/p_pos, pdir[1]))
+ assert(small_diff(pos_data["pz"]/p_pos, pdir[2]))
+ print(" [OK] pairs move along the initial photon direction")
+
+def check_energy(energy_phot, energy_ele, energy_pos):
+ # Sorting the arrays is required because electrons and positrons are not
+ # necessarily dumped in the same order.
+ s_energy_ele = np.sort(energy_ele)
+ is_energy_pos = np.sort(energy_pos)[::-1]
+ product_energy = s_energy_ele + is_energy_pos
+ assert(small_diff(product_energy, energy_phot))
+ print(" [OK] energy is conserved in each event")
+
+def check_opt_depths(phot_data, ele_data, pos_data):
+ data = (phot_data, ele_data, pos_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(energy_ele, energy_pos, gamma_phot,
+ chi_phot, n_lost, NN, idx, do_plot=False):
+ gamma_min = 1.0001
+ gamma_max = gamma_phot-1.0001
+ h_gamma_ele, c_gamma = np.histogram(energy_ele/mec2, bins=NN, range=[gamma_min,gamma_max])
+ h_gamma_pos, _ = np.histogram(energy_pos/mec2, bins=NN, range=[gamma_min,gamma_max])
+
+ cchi_part_min = chi_phot*(gamma_min - 1)/(gamma_phot - 2)
+ cchi_part_max = chi_phot*(gamma_max - 1)/(gamma_phot - 2)
+
+ #Rudimentary integration over npoints for each bin
+ npoints= 20
+ aux_chi = np.linspace(cchi_part_min, cchi_part_max, NN*npoints)
+ distrib = BW_d2N_dt_dchi(chi_phot, gamma_phot, aux_chi)
+ distrib = np.sum(distrib.reshape(-1, npoints),1)
+ distrib = n_lost*distrib/np.sum(distrib)
+
+ if do_plot :
+ # Visual comparison of distributions
+ c_gamma_centered = 0.5*(c_gamma[1:]+c_gamma[:-1])
+ plt.clf()
+ plt.xlabel("γ_particle")
+ plt.ylabel("N")
+ plt.title("χ_photon = {:f}".format(chi_phot))
+ plt.plot(c_gamma_centered, distrib,label="theory")
+ plt.plot(c_gamma_centered, h_gamma_ele,label="BW electrons")
+ plt.plot(c_gamma_centered, h_gamma_pos,label="BW positrons")
+ plt.legend()
+ plt.savefig("case_{:d}".format(idx+1))
+
+ discr_ele = np.abs(h_gamma_ele-distrib)
+ discr_pos = np.abs(h_gamma_pos-distrib)
+ max_discr = 5.0 * np.sqrt(distrib)
+ assert(np.all(discr_ele < max_discr))
+ assert(np.all(discr_pos < 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):
+ phot_name = spec_names_phot[idx]
+ ele_name = spec_names_ele[idx]
+ pos_name = spec_names_pos[idx]
+ p0 = initial_momenta[idx]
+
+ p2_phot = p0[0]**2 + p0[1]**2 + p0[2]**2
+ p_phot = np.sqrt(p2_phot)
+ energy_phot = p_phot*c
+ chi_phot = calc_chi_gamma(p0, E_f, B_f)
+ gamma_phot = np.linalg.norm(p0)/mec
+
+ print("** Case {:d} **".format(idx+1))
+ print(" initial momentum: ", p0)
+ print(" quantum parameter: {:f}".format(chi_phot))
+ print(" normalized photon energy: {:f}".format(gamma_phot))
+ print(" timestep: {:f} fs".format(sim_time*1e15))
+
+ phot_data = get_spec(all_data_end, phot_name, is_photon=True)
+ ele_data = get_spec(all_data_end, ele_name, is_photon=False)
+ pos_data = get_spec(all_data_end, pos_name, is_photon=False)
+
+ p2_ele = ele_data["px"]**2 + ele_data["py"]**2 + ele_data["pz"]**2
+ p_ele = np.sqrt(p2_ele)
+ energy_ele = np.sqrt(1.0 + p2_ele/mec**2 )*mec2
+ p2_pos = pos_data["px"]**2 + pos_data["py"]**2 + pos_data["pz"]**2
+ p_pos = np.sqrt(p2_pos)
+ energy_pos = np.sqrt(1.0 + p2_pos/mec**2 )*mec2
+
+ n_lost = check_number_of_pairs(data_set_end,
+ phot_name, ele_name, pos_name,
+ chi_phot, gamma_phot, sim_time,
+ initial_particle_number)
+
+ check_weights(phot_data, ele_data, pos_data)
+
+ check_momenta(phot_data, ele_data, pos_data, p0, p_ele, p_pos)
+
+ check_energy(energy_phot, energy_ele, energy_pos)
+
+ check_energy_distrib(energy_ele, energy_pos, gamma_phot, chi_phot, n_lost, NNS[idx], idx)
+
+ check_opt_depths(phot_data, ele_data, pos_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()