|
| 1 | +#!/usr/bin/env python3 |
| 2 | +# |
| 3 | +# Copyright 2022-2023 ImpactX contributors |
| 4 | +# Authors: Axel Huebl, Ji Qiang |
| 5 | +# License: BSD-3-Clause-LBNL |
| 6 | +# |
| 7 | + |
| 8 | +import numpy as np |
| 9 | +import openpmd_api as io |
| 10 | +from scipy.stats import moment |
| 11 | + |
| 12 | + |
| 13 | +def get_moments(beam): |
| 14 | + """Calculate standard deviations of beam position & momenta |
| 15 | + and emittance values |
| 16 | +
|
| 17 | + Returns |
| 18 | + ------- |
| 19 | + sigx, sigy, sigt, emittance_x, emittance_y, emittance_t |
| 20 | + """ |
| 21 | + sigx = moment(beam["position_x"], moment=2) ** 0.5 # variance -> std dev. |
| 22 | + sigpx = moment(beam["momentum_x"], moment=2) ** 0.5 |
| 23 | + sigy = moment(beam["position_y"], moment=2) ** 0.5 |
| 24 | + sigpy = moment(beam["momentum_y"], moment=2) ** 0.5 |
| 25 | + sigt = moment(beam["position_t"], moment=2) ** 0.5 |
| 26 | + sigpt = moment(beam["momentum_t"], moment=2) ** 0.5 |
| 27 | + |
| 28 | + epstrms = beam.cov(ddof=0) |
| 29 | + emittance_x = (sigx**2 * sigpx**2 - epstrms["position_x"]["momentum_x"] ** 2) ** 0.5 |
| 30 | + emittance_y = (sigy**2 * sigpy**2 - epstrms["position_y"]["momentum_y"] ** 2) ** 0.5 |
| 31 | + emittance_t = (sigt**2 * sigpt**2 - epstrms["position_t"]["momentum_t"] ** 2) ** 0.5 |
| 32 | + |
| 33 | + return (sigx, sigy, sigt, emittance_x, emittance_y, emittance_t) |
| 34 | + |
| 35 | + |
| 36 | +# initial/final beam |
| 37 | +series = io.Series("diags/openPMD/monitor.h5", io.Access.read_only) |
| 38 | +last_step = list(series.iterations)[-1] |
| 39 | +initial = series.iterations[1].particles["beam"].to_df() |
| 40 | +beam_final = series.iterations[last_step].particles["beam"] |
| 41 | +final = beam_final.to_df() |
| 42 | + |
| 43 | +# compare number of particles |
| 44 | +num_particles = 10000 |
| 45 | +assert num_particles == len(initial) |
| 46 | +assert num_particles == len(final) |
| 47 | + |
| 48 | +print("Initial Beam:") |
| 49 | +sig_xi, sig_yi, sig_ti, emittance_xi, emittance_yi, emittance_ti = get_moments(initial) |
| 50 | +print(f" sigx={sig_xi:e} sigy={sig_yi:e} sigt={sig_ti:e}") |
| 51 | +print( |
| 52 | + f" emittance_x={emittance_xi:e} emittance_y={emittance_yi:e} emittance_t={emittance_ti:e}" |
| 53 | +) |
| 54 | + |
| 55 | +atol = 0.0 # ignored |
| 56 | +rtol = 2.2 * num_particles**-0.5 # from random sampling of a smooth distribution |
| 57 | +print(f" rtol={rtol} (ignored: atol~={atol})") |
| 58 | + |
| 59 | +assert np.allclose( |
| 60 | + [sig_xi, sig_yi, sig_ti, emittance_xi, emittance_yi, emittance_ti], |
| 61 | + [7.515765e-05, 7.511883e-05, 9.997395e-4, 2.001510e-09, 1.999755e-09, 1.999289e-06], |
| 62 | + rtol=rtol, |
| 63 | + atol=atol, |
| 64 | +) |
| 65 | + |
| 66 | + |
| 67 | +print("") |
| 68 | +print("Final Beam:") |
| 69 | +sig_xf, sig_yf, sig_tf, emittance_xf, emittance_yf, emittance_tf = get_moments(initial) |
| 70 | +print(f" sigx={sig_xf:e} sigy={sig_yf:e} sigt={sig_tf:e}") |
| 71 | +print( |
| 72 | + f" emittance_x={emittance_xf:e} emittance_y={emittance_yf:e} emittance_t={emittance_tf:e}" |
| 73 | +) |
| 74 | + |
| 75 | +atol = 0.0 # ignored |
| 76 | +rtol = 2.2 * num_particles**-0.5 # from random sampling of a smooth distribution |
| 77 | +print(f" rtol={rtol} (ignored: atol~={atol})") |
| 78 | + |
| 79 | +assert np.allclose( |
| 80 | + [sig_xf, sig_yf, sig_tf, emittance_xf, emittance_yf, emittance_tf], |
| 81 | + [ |
| 82 | + 7.51576586332169e-05, |
| 83 | + 7.511883208451813e-05, |
| 84 | + 0.0009997395499750136, |
| 85 | + 2.0015106608723994e-09, |
| 86 | + 1.999755254276969e-09, |
| 87 | + 1.9992898444562777e-06, |
| 88 | + ], |
| 89 | + rtol=rtol, |
| 90 | + atol=atol, |
| 91 | +) |
0 commit comments