|
| 1 | +import numpy as np |
| 2 | +from matplotlib import pyplot as plt |
| 3 | +from scipy import constants as cst |
| 4 | + |
| 5 | +import xobjects as xo |
| 6 | +import xtrack as xt |
| 7 | +import xfields as xf |
| 8 | +import xwakes as xw |
| 9 | + |
| 10 | +context = xo.ContextCpu() |
| 11 | + |
| 12 | +energy = 7E3 |
| 13 | +qx = 62.31 |
| 14 | +qy = 60.32 |
| 15 | +momentumCompaction = 3.48e-04 |
| 16 | +basic_slot_time = 25E-9 |
| 17 | +n_bunch_max = 3000 |
| 18 | +n_bunch = 100 |
| 19 | +bunch_intensity = 5E12 |
| 20 | +emit_norm = 2.5E-6 |
| 21 | +sigma_z = 0.08 |
| 22 | +selected_bunch = n_bunch-1 |
| 23 | +r_over_q = 1806 |
| 24 | +q = 5E3 |
| 25 | +coupled_bunch_number_x = 16 |
| 26 | +coupled_bunch_number_y = 73 |
| 27 | +n_turns_wake = 1 |
| 28 | +num_slices = 11 |
| 29 | + |
| 30 | +assert n_bunch_max%n_bunch == 0 |
| 31 | +circumference = n_bunch_max * basic_slot_time * cst.c |
| 32 | +bunch_spacing_zeta = circumference/n_bunch |
| 33 | +beta_x = circumference/(2.0*np.pi*qx) |
| 34 | +beta_y = circumference/(2.0*np.pi*qy) |
| 35 | +mass0 = cst.value('proton mass energy equivalent in MeV')*1E-3 |
| 36 | +gamma = energy/mass0 |
| 37 | +RF_freq = 10.0/basic_slot_time |
| 38 | +sigma_x = np.sqrt(emit_norm*beta_x/gamma) |
| 39 | +sigma_y = np.sqrt(emit_norm*beta_y/gamma) |
| 40 | +betar = np.sqrt(1 - 1 / gamma ** 2) |
| 41 | +zeta_range=(-3*sigma_z, 3*sigma_z) |
| 42 | +filling_scheme = np.ones(n_bunch,dtype=int) |
| 43 | + |
| 44 | +n_part = num_slices |
| 45 | +slice_intensity = bunch_intensity/num_slices |
| 46 | +x0 = np.ones(n_part) |
| 47 | +z_edges = np.linspace(zeta_range[0],zeta_range[1],num_slices+1) |
| 48 | +z0 = z_edges[:-1]+(z_edges[1]-z_edges[0])/2 |
| 49 | +zeta0 = z0-1*(n_bunch-1)*bunch_spacing_zeta |
| 50 | +particles = xt.Particles( |
| 51 | + _context = context, |
| 52 | + q0 = 1, |
| 53 | + p0c = energy*1E9, |
| 54 | + mass0 = mass0*1E9, |
| 55 | + x = sigma_x*x0, |
| 56 | + y = sigma_y*x0, |
| 57 | + zeta = zeta0, |
| 58 | + weight = bunch_intensity / n_part |
| 59 | + ) |
| 60 | + |
| 61 | +wfx = xw.wit.ComponentResonator(kind = 'dipole_x', |
| 62 | + r=r_over_q*q, q=q, f_r=RF_freq+3E5) |
| 63 | +wfy = xw.wit.ComponentResonator(kind = 'dipole_y', |
| 64 | + r=r_over_q*q, q=q, f_r=RF_freq-3E5) |
| 65 | + |
| 66 | + |
| 67 | +coupled_bunch_phase_x = 2*np.pi*coupled_bunch_number_x/n_bunch |
| 68 | +wfx.configure_for_tracking(zeta_range=zeta_range, |
| 69 | + num_slices=num_slices, |
| 70 | + bunch_spacing_zeta=bunch_spacing_zeta, |
| 71 | + num_turns=n_turns_wake, |
| 72 | + circumference=circumference, |
| 73 | + filling_scheme = np.ones(n_bunch,dtype=int), |
| 74 | + bunch_selection = [selected_bunch], |
| 75 | + fake_coupled_bunch_phase_x = coupled_bunch_phase_x, |
| 76 | + beta_x = beta_x, |
| 77 | + ) |
| 78 | +coupled_bunch_phase_y = 2*np.pi*coupled_bunch_number_y/n_bunch |
| 79 | +wfy.configure_for_tracking(zeta_range=zeta_range, |
| 80 | + num_slices=num_slices, |
| 81 | + bunch_spacing_zeta=bunch_spacing_zeta, |
| 82 | + num_turns=n_turns_wake, |
| 83 | + circumference=circumference, |
| 84 | + filling_scheme = np.ones(n_bunch,dtype=int), |
| 85 | + bunch_selection = [selected_bunch], |
| 86 | + fake_coupled_bunch_phase_y = coupled_bunch_phase_y, |
| 87 | + beta_y = beta_y, |
| 88 | + ) |
| 89 | + |
| 90 | + |
| 91 | +for turn in range(n_turns_wake): |
| 92 | + px0 = np.copy(particles.px) |
| 93 | + wfx.track(particles) |
| 94 | + py0 = np.copy(particles.py) |
| 95 | + wfy.track(particles) |
| 96 | + |
| 97 | +moments_data = wfx._xfields_wf.moments_data |
| 98 | +z,x = moments_data.get_moment_profile('x',0) |
| 99 | +moments_data = wfy._xfields_wf.moments_data |
| 100 | +z,y = moments_data.get_moment_profile('y',0) |
| 101 | +zetas = np.array([]) |
| 102 | +positions_x = np.array([]) |
| 103 | +positions_y = np.array([]) |
| 104 | +for slot in np.arange(n_bunch): |
| 105 | + zetas = np.hstack([zetas,z0-bunch_spacing_zeta*slot]) |
| 106 | + positions_x = np.hstack([positions_x,x0*np.cos(coupled_bunch_phase_x*(selected_bunch-slot))]) |
| 107 | + positions_y = np.hstack([positions_y,x0*np.cos(coupled_bunch_phase_y*(selected_bunch-slot))]) |
| 108 | +indices = np.argsort(zetas) |
| 109 | +zetas = zetas[indices] |
| 110 | +positions_x = positions_x[indices] |
| 111 | +positions_y = positions_y[indices] |
| 112 | + |
| 113 | +assert np.allclose(z,zetas) |
| 114 | +assert np.allclose(x/sigma_x,positions_x) |
| 115 | +assert np.allclose(y/sigma_y,positions_y) |
| 116 | + |
| 117 | + |
| 118 | +for i_slice in range(num_slices): |
| 119 | + zetas_slice = zeta0[i_slice]-zetas |
| 120 | + |
| 121 | + scaling_constant = particles.q0**2 * cst.e**2 / (particles.p0c[0] * particles.beta0[0] * cst.e) |
| 122 | + kicks = positions_x*sigma_x*scaling_constant*slice_intensity*wfx.function_vs_zeta(zetas_slice,beta0=betar,dzeta=moments_data.dz) |
| 123 | + kick_from_track = particles.px[i_slice]-px0[i_slice] |
| 124 | + assert np.isclose(np.sum(kicks),kick_from_track) |
| 125 | + kicks = positions_x*sigma_x*scaling_constant*slice_intensity*wfy.function_vs_zeta(zetas_slice,beta0=betar,dzeta=moments_data.dz) |
| 126 | + kick_from_track = particles.py[i_slice]-py0[i_slice] |
| 127 | + assert np.isclose(np.sum(kicks),kick_from_track) |
| 128 | + |
| 129 | + |
0 commit comments