def test_wakefield_platesresonator(self): ''' Track through a ParallelPlatesResonator wakefield ''' Dx = np.append(np.linspace(0., 20., self.nsegments), [0]) # add some dispersion/alpha lhc = m.LHC(n_segments=self.nsegments, machine_configuration='450GeV', app_x=1e-9, app_y=2e-9, app_xy=-1.5e-11, chromaticity_on=False, amplitude_detuning_on=True, alpha_x=1.2 * np.ones(self.nsegments), D_x=Dx, printer=SilentPrinter()) self.n_macroparticles = 200000 bunch_cpu = self.create_lhc_bunch(lhc) #self.create_gaussian_bunch() bunch_gpu = self.create_lhc_bunch(lhc) #self.create_gaussian_bunch() n_slices = 50 #5 frequency = 8e8 #1e9 R_shunt = 23e3 # [Ohm] Q = 1. unif_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=1) #res = CircularResonator(R_shunt=R_shunt, frequency=frequency, Q=Q) res = ParallelPlatesResonator(R_shunt=R_shunt, frequency=frequency, Q=Q, printer=SilentPrinter()) wake_field = WakeField(unif_bin_slicer, res) self.assertTrue( self._track_cpu_gpu([wake_field], bunch_cpu, bunch_gpu), 'Tracking Wakefield CircularResonator CPU/GPU differs')
def test_wakefield_wakefile(self): ''' Track an LHC bunch and a LHC wakefield ''' wakefile = 'autoruntests/wake_table.dat' #'./wakeforhdtl_PyZbase_Allthemachine_450GeV_B1_LHC_inj_450GeV_B1.dat' Qp_x, Qp_y = 1., 1. Q_s = 0.0049 n_macroparticles = 10 intensity = 1e11 longitudinal_focusing = 'linear' machine = m.LHC(n_segments=1, machine_configuration='450GeV', longitudinal_focusing=longitudinal_focusing, Qp_x=[Qp_x], Qp_y=[Qp_y], Q_s=Q_s, beta_x=[65.9756], beta_y=[71.5255], printer=SilentPrinter()) epsn_x = 3.5e-6 epsn_y = 3.5e-6 sigma_z = 1.56e-9 * c / 4. np.random.seed(0) bunch_cpu = machine.generate_6D_Gaussian_bunch(n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=sigma_z) np.random.seed(0) bunch_gpu = machine.generate_6D_Gaussian_bunch(n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=sigma_z) n_slices_wakefields = 55 n_sigma_z_wakefields = 3 slicer_for_wakefields_cpu = UniformBinSlicer( n_slices_wakefields, n_sigma_z=n_sigma_z_wakefields) wake_components = [ 'time', 'dipole_x', 'dipole_y', 'no_quadrupole_x', 'no_quadrupole_y', 'no_dipole_xy', 'no_dipole_yx' ] wake_table_cpu = WakeTable(wakefile, wake_components, printer=SilentPrinter()) wake_field_cpu = WakeField(slicer_for_wakefields_cpu, wake_table_cpu) # also checked for 100 turns! self.assertTrue( self._track_cpu_gpu([wake_field_cpu], bunch_cpu, bunch_gpu, nturns=2), 'Tracking through WakeField(waketable) differs')
) ww1 = WakeTable( wakefile1, ['time', 'dipole_x', 'dipole_y', 'quadrupole_x', 'quadrupole_y'], n_turns_wake=n_turns_wake) # only dipolar kick #my_length = len(ww1.wake_table['quadrupole_x']) #ww1.wake_table['quadrupole_x'] = np.zeros(my_length) #ww1.wake_table['quadrupole_y'] = np.zeros(my_length) # only quadrupolar kick #my_length = len(ww1.wake_table['dipole_x']) #ww1.wake_table['dipole_x'] = np.zeros(my_length) #ww1.wake_table['dipole_y'] = np.zeros(my_length) wake_field_complete = WakeField(slicer_for_wakefields, ww1) #, beta_x=beta_x, beta_y=beta_y) # CREATE TRANSVERSE AND LONGITUDINAL MAPS # ======================================= scale_factor = 2 * bunch.p0 # scale the detuning coefficients in pyheadtail units transverse_map = TransverseMap( s, alpha_x, beta_x, D_x, alpha_y, beta_y, D_y, Q_x, Q_y, [ Chromaticity(Qp_x, Qp_y), AmplitudeDetuning(app_x * scale_factor, app_y * scale_factor, app_xy * scale_factor) ]) longitudinal_map = LinearMap([alpha], circumference, Q_s) # ====================================================================== # SET UP ACCELERATOR MAP AND START TRACKING
def run(job_id, accQ_y): it = job_id # SIMULATION PARAMETERS # ===================== # Simulation parameters n_turns = 10000 n_macroparticles = 100000 # per bunch # MACHINE PARAMETERS # ================== intensity = 1e13 # protons # intensity = 2*4e12 # protons E0 = 71e6 # Kinetic energy [eV] p0 = np.sqrt((m_p_MeV + E0)**2 - m_p_MeV**2) * e / c print('Beam kinetic energy: ' + str(E0 * 1e-6) + ' MeV') print('Beam momentum: ' + str(p0 * 1e-6 * c / e) + ' MeV/c') accQ_x = 4.31 # Horizontal tune # accQ_y = 3.80 # Vertical tune is an input argument chroma = -1.4 # Chromaticity alpha = 5.034**-2 # momentum compaction factor circumference = 160. # [meters] # Approximated average beta functions (lumped wake normalizations) beta_x = circumference / (2. * np.pi * accQ_x) beta_y = circumference / (2. * np.pi * accQ_y) # Harmonic number for RF h_RF = [2] # a list of harmonic number for RF V_RF = [5e3 * 2] # a list of RF voltages p_increment = 0. dphi_RF = [np.pi] # a list of RF phases # dphi_RF = 0. # non-linear longitudinal mode includes RF, otherwise linear needs synhotron tune Q_s longitudinal_mode = 'non-linear' # Q_s=0.02 # Longitudinal tune optics_mode = 'smooth' n_segments = 1 s = None alpha_x = None alpha_y = None D_x = 0 D_y = 0 charge = e mass = m_p name = None app_x = 0 app_y = 0 app_xy = 0 # Creates PyHEADTAIL object for the synchotron machine = Synchrotron(optics_mode=optics_mode, circumference=circumference, n_segments=n_segments, s=s, name=name, alpha_x=alpha_x, beta_x=beta_x, D_x=D_x, alpha_y=alpha_y, beta_y=beta_y, D_y=D_y, accQ_x=accQ_x, accQ_y=accQ_y, Qp_x=chroma, Qp_y=chroma, app_x=app_x, app_y=app_y, app_xy=app_xy, alpha_mom_compaction=alpha, longitudinal_mode=longitudinal_mode, h_RF=np.atleast_1d(h_RF), V_RF=np.atleast_1d(V_RF), dphi_RF=np.atleast_1d(dphi_RF), p0=p0, p_increment=p_increment, charge=charge, mass=mass) print('') print('machine.beta: ') print(machine.beta) print('') print('') print('machine.gamma: ') print(machine.gamma) print('') epsn_x = 300e-6 epsn_y = 300e-6 sigma_z = 15 # bunch length in meters to be matched to the bucket # Creates transverse macroparticle distribution allbunches = machine.generate_6D_Gaussian_bunch(n_macroparticles, intensity, epsn_x, epsn_y, sigma_z) # Creates longitudinal macroparticle distribution rfb = RFBucket(circumference, machine.gamma, m_p, e, [alpha], 0., h_RF, V_RF, dphi_RF) rfb_matcher = RFBucketMatcher(rfb, WaterbagDistribution, sigma_z=sigma_z) rfb_matcher.integrationmethod = 'cumtrapz' z, dp, _, _ = rfb_matcher.generate(n_macroparticles) np.copyto(allbunches.z, z) np.copyto(allbunches.dp, dp) # Slicer object, which used for wakefields and slice monitors slicer = UniformBinSlicer(75, z_cuts=(-2. * sigma_z, 2. * sigma_z)) # WAKE FIELDS # =========== # Length of the wake function in turns, wake n_turns_wake = 150 # Parameters for a resonator # frequency is in the units of (mode-Q_frac), where # mode: integer number of coupled bunch mode (1 matches to the observations) # Q_frac: resonance fractional tune f_r = (1 - 0.83) * 1. / (circumference / (c * machine.beta)) Q = 15 R = 1.0e6 # Renator wake object, which is added to the one turn map wakes = CircularResonator(R, f_r, Q, n_turns_wake=n_turns_wake) wake_field = WakeField(slicer, wakes) machine.one_turn_map.append(wake_field) # CREATE MONITORS # =============== simulation_parameters_dict = {'gamma' : machine.gamma,\ 'intensity' : intensity,\ 'Qx' : accQ_x,\ 'Qy' : accQ_y,\ # 'Qs' : Q_s,\ 'beta_x' : beta_x,\ 'beta_y' : beta_y,\ # 'beta_z' : bucket.beta_z,\ 'epsn_x' : epsn_x,\ 'epsn_y' : epsn_y,\ 'sigma_z' : sigma_z,\ } # Bunch monitor strores bunch average positions for all the bunches bunchmonitor = BunchMonitor(outputpath + '/bunchmonitor_{:04d}'.format(it), n_turns, simulation_parameters_dict, write_buffer_every=32, buffer_size=32) # Slice monitors saves slice-by-slice data for each bunch slicemonitor = SliceMonitor(outputpath + '/slicemonitor_{:01d}_{:04d}'.format(0, it), 60, slicer, simulation_parameters_dict, write_buffer_every=60, buffer_size=60) # Counter for a number of turns stored to slice monitors s_cnt = 0 # TRACKING LOOP # ============= monitor_active = False print('\n--> Begin tracking...\n') for i in range(n_turns): t0 = time.clock() # Tracks beam through the one turn map simulation map machine.track(allbunches) # Stores bunch mean coordinate values bunchmonitor.dump(allbunches) # If the total oscillation amplitude of bunches exceeds the threshold # or the simulation is running on the last turns, triggers the slice # monitors for headtail motion data if (allbunches.mean_x() > 1e-1 or allbunches.mean_y() > 1e-1 or i > (n_turns - 64)): monitor_active = True # saves slice monitor data if monitors are activated and less than # 64 turns have been stored if monitor_active and s_cnt < 64: slicemonitor.dump(allbunches) s_cnt += 1 elif s_cnt == 64: break # If this script is runnin on the first processor, prints the current # bunch coordinates and emittances if (i % 100 == 0): print( '{:4d} \t {:+3e} \t {:+3e} \t {:+3e} \t {:3e} \t {:3e} \t {:3f} \t {:3f} \t {:3f} \t {:3s}' .format(i, allbunches.mean_x(), allbunches.mean_y(), allbunches.mean_z(), allbunches.epsn_x(), allbunches.epsn_y(), allbunches.epsn_z(), allbunches.sigma_z(), allbunches.sigma_dp(), str(time.clock() - t0)))
# ========== n_turns_wake = 1 # for the moment we consider that the wakefield decays after 1 turn #wakefile1 = ('/afs/cern.ch/work/n/natriant/private/pyheadtail_example_crabcavity/wakefields/newkickers_Q26_2018_modified.txt') wakefile1 = ('/afs/cern.ch/work/n/natriant/private/pyheadtail_example_crabcavity/wakefields/SPS_complete_wake_model_2018_Q26.txt') ww1 = WakeTable(wakefile1, ['time', 'dipole_x', 'dipole_y', 'quadrupole_x', 'quadrupole_y'], n_turns_wake=n_turns_wake) # only dipolar kick #my_length = len(ww1.wake_table['quadrupole_x']) #ww1.wake_table['quadrupole_x'] = np.zeros(my_length) #ww1.wake_table['quadrupole_y'] = np.zeros(my_length) # only quadrupolar kick #my_length = len(ww1.wake_table['dipole_x']) #ww1.wake_table['dipole_x'] = np.zeros(my_length) #ww1.wake_table['dipole_y'] = np.zeros(my_length) wake_field_kicker = WakeField(slicer_for_wakefields, ww1)#, beta_x=beta_x, beta_y=beta_y) # CREATE TRANSVERSE AND LONGITUDINAL MAPS # ======================================= scale_factor = 2*bunch.p0 # scale the detuning coefficients in pyheadtail units transverse_map = TransverseMap(s, alpha_x, beta_x, D_x, alpha_y, beta_y, D_y, Q_x, Q_y, [Chromaticity(Qp_x, Qp_y), AmplitudeDetuning(app_x*scale_factor, app_y*scale_factor, app_xy*scale_factor)]) longitudinal_map = LinearMap([alpha], circumference, Q_s) # ====================================================================== # SET UP ACCELERATOR MAP AND START TRACKING # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
def run(intensity, chroma=0, i_oct=0): '''Arguments: - intensity: integer number of charges in beam - chroma: first-order chromaticity Q'_{x,y}, identical for both transverse planes - i_oct: octupole current in A (positive i_oct means LOF = i_oct > 0 and LOD = -i_oct < 0) ''' # BEAM AND MACHINE PARAMETERS # ============================ from LHC import LHC # energy set above will enter get_nonlinear_params p0 assert machine_configuration == 'LHC-injection' machine = LHC(n_segments=1, machine_configuration=machine_configuration, **get_nonlinear_params(chroma=chroma, i_oct=i_oct, p0=0.45e12 * e / c)) # BEAM # ==== epsn_x = 3.e-6 # normalised horizontal emittance epsn_y = 3.e-6 # normalised vertical emittance sigma_z = 1.2e-9 * machine.beta * c / 4. # RMS bunch length in meters bunch = machine.generate_6D_Gaussian_bunch(n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=sigma_z, matched=True) print("\n--> Bunch length and emittance: {:g} m, {:g} eVs.".format( bunch.sigma_z(), bunch.epsn_z())) # CREATE BEAM SLICERS # =================== slicer_for_slicemonitor = UniformBinSlicer(50, z_cuts=(-3 * sigma_z, 3 * sigma_z)) slicer_for_wakefields = UniformBinSlicer( 50, z_cuts=(-3 * sigma_z, 3 * sigma_z), circumference=machine.circumference, h_bunch=machine.h_bunch) print("Slice") # CREATE WAKES # ============ wake_table1 = WakeTable( wakefile, [ 'time', 'dipole_x', 'dipole_y', # 'quadrupole_x', 'quadrupole_y', 'noquadrupole_x', 'noquadrupole_y', # 'dipole_xy', 'dipole_yx', 'nodipole_xy', 'nodipole_yx', ]) wake_field = WakeField(slicer_for_wakefields, wake_table1, mpi=True) # CREATE DAMPER # ============= dampingtime = 50. gain = 2. / dampingtime damper = IdealBunchFeedback(gain) # CREATE MONITORS # =============== try: bucket = machine.longitudinal_map.get_bucket(bunch) except AttributeError: bucket = machine.rfbucket simulation_parameters_dict = { 'gamma': machine.gamma, 'intensity': intensity, 'Qx': machine.Q_x, 'Qy': machine.Q_y, 'Qs': bucket.Q_s, 'beta_x': bunch.beta_Twiss_x(), 'beta_y': bunch.beta_Twiss_y(), 'beta_z': bucket.beta_z, 'epsn_x': bunch.epsn_x(), 'epsn_y': bunch.epsn_y(), 'sigma_z': bunch.sigma_z(), } bunchmonitor = BunchMonitor( outputpath + '/bunchmonitor_{:04d}_chroma={:g}'.format(it, chroma), n_turns, simulation_parameters_dict, write_buffer_to_file_every=512, buffer_size=4096) slicemonitor = SliceMonitor( outputpath + '/slicemonitor_{:04d}_chroma={:g}'.format(it, chroma), n_turns_slicemon, slicer_for_slicemonitor, simulation_parameters_dict, write_buffer_to_file_every=1, buffer_size=n_turns_slicemon) # TRACKING LOOP # ============= # machine.one_turn_map.append(damper) machine.one_turn_map.append(wake_field) # for slice statistics monitoring: s_cnt = 0 monitorswitch = False print('\n--> Begin tracking...\n') # GO!!! for i in range(n_turns): t0 = time.clock() # track the beam around the machine for one turn: machine.track(bunch) ex, ey, ez = bunch.epsn_x(), bunch.epsn_y(), bunch.epsn_z() mx, my, mz = bunch.mean_x(), bunch.mean_y(), bunch.mean_z() # monitor the bunch statistics (once per turn): bunchmonitor.dump(bunch) # if the centroid becomes unstable (>1cm motion) # then monitor the slice statistics: if not monitorswitch: if mx > 1e-2 or my > 1e-2 or i > n_turns - n_turns_slicemon: print("--> Activate slice monitor") monitorswitch = True else: if s_cnt < n_turns_slicemon: slicemonitor.dump(bunch) s_cnt += 1 # stop the tracking as soon as we have not-a-number values: if not all(np.isfinite(c) for c in [ex, ey, ez, mx, my, mz]): print('*** STOPPING SIMULATION: non-finite bunch stats!') break # print status all 1000 turns: if i % 100 == 0: t1 = time.clock() print('Emittances: ({:.3g}, {:.3g}, {:.3g}) ' '& Centroids: ({:.3g}, {:.3g}, {:.3g})' '@ turn {:d}, {:g} ms, {:s}'.format( ex, ey, ez, mx, my, mz, i, (t1 - t0) * 1e3, time.strftime("%d/%m/%Y %H:%M:%S", time.localtime()))) print('\n*** Successfully completed!')
def run(): def track_n_save(bunch, map_): mean_x = np.empty(n_turns) mean_y = np.empty(n_turns) sigma_z = np.empty(n_turns) for i in xrange(n_turns): mean_x[i] = bunch.mean_x() mean_y[i] = bunch.mean_y() sigma_z[i] = bunch.sigma_z() for m_ in map_: m_.track(bunch) return mean_x, mean_y, sigma_z def my_fft(data): t = np.arange(len(data)) fft = np.fft.rfft(data) fft_freq = np.fft.rfftfreq(t.shape[-1]) return fft_freq, np.abs(fft.real) def generate_bunch(n_macroparticles, alpha_x, alpha_y, beta_x, beta_y, linear_map): intensity = 1.05e11 sigma_z = 0.059958 gamma = 3730.26 p0 = np.sqrt(gamma**2 - 1) * m_p * c beta_z = (linear_map.eta(dp=0, gamma=gamma) * linear_map.circumference / (2 * np.pi * linear_map.Q_s)) epsn_x = 3.75e-6 # [m rad] epsn_y = 3.75e-6 # [m rad] epsn_z = 4 * np.pi * sigma_z**2 * p0 / (beta_z * e) bunch = generators.generate_Gaussian6DTwiss( macroparticlenumber=n_macroparticles, intensity=intensity, charge=e, gamma=gamma, mass=m_p, circumference=C, alpha_x=alpha_x, beta_x=beta_x, epsn_x=epsn_x, alpha_y=alpha_y, beta_y=beta_y, epsn_y=epsn_y, beta_z=beta_z, epsn_z=epsn_z) # print ('bunch sigma_z=' + bunch.sigma_z()) return bunch def track_n_show(bunch, slicer, map_woWakes, wake_field): fig, ((ax1, ax2)) = plt.subplots(2, 1, figsize=(16, 16)) xp_diff = np.zeros(n_macroparticles) for i in xrange(n_turns): for m_ in map_woWakes: m_.track(bunch) # Dipole X kick. if i == (n_turns - 1): xp_old = bunch.xp.copy() wake_field.track(bunch) if i == (n_turns - 1): xp_diff[:] = bunch.xp[:] - xp_old[:] # Plot bunch.z vs. slice index of particle. Mark particles within # z cuts in green. nsigmaz_lbl = ' (nsigmaz =' + str(n_sigma_z) + ')' slice_set = bunch.get_slices(slicer) pidx = slice_set.particles_within_cuts slidx = slice_set.slice_index_of_particle z_cut_tail, z_cut_head = slice_set.z_cut_tail, slice_set.z_cut_head # In[4]: # Basic parameters. n_turns = 10 n_segments = 1 n_macroparticles = 50 Q_x = 64.28 Q_y = 59.31 Q_s = 0.0020443 C = 26658.883 R = C / (2. * np.pi) alpha_x_inj = 0. alpha_y_inj = 0. beta_x_inj = 66.0064 beta_y_inj = 71.5376 alpha_0 = [0.0003225] #waketable filename fn = os.path.join(os.path.dirname(__file__), 'wake_table.dat') # In[5]: # Parameters for transverse map. s = np.arange(0, n_segments + 1) * C / n_segments alpha_x = alpha_x_inj * np.ones(n_segments) beta_x = beta_x_inj * np.ones(n_segments) D_x = np.zeros(n_segments) alpha_y = alpha_y_inj * np.ones(n_segments) beta_y = beta_y_inj * np.ones(n_segments) D_y = np.zeros(n_segments) # In[6]: # In[7]: # In[8]: # CASE TEST SETUP trans_map = TransverseMap(s, alpha_x, beta_x, D_x, alpha_y, beta_y, D_y, Q_x, Q_y) long_map = LinearMap(alpha_0, C, Q_s) bunch = generate_bunch(n_macroparticles, alpha_x_inj, alpha_y_inj, beta_x_inj, beta_y_inj, long_map) # In[9]: # CASE I # Transverse and long. tracking (linear), and wakes from WakeTable source. # DIPOLE X, UniformBinSlicer n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. wake_file_columns = [ 'time', 'dipole_x', 'no_dipole_y', 'no_quadrupole_x', 'no_quadrupole_y', 'no_dipole_xy', 'no_dipole_yx' ] table = WakeTable(fn, wake_file_columns, printer=SilentPrinter(), warningprinter=SilentPrinter()) wake_field = WakeField(uniform_bin_slicer, table) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[10]: # CASE II # Transverse and long. tracking (linear), and wakes from WakeTable source. # DIPOLE X, UniformChargeSlicer n_sigma_z = 2 n_slices = 15 uniform_charge_slicer = UniformChargeSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. wake_file_columns = [ 'time', 'dipole_x', 'no_dipole_y', 'no_quadrupole_x', 'no_quadrupole_y', 'no_dipole_xy', 'no_dipole_yx' ] table = WakeTable(fn, wake_file_columns, printer=SilentPrinter(), warningprinter=SilentPrinter()) wake_field = WakeField(uniform_charge_slicer, table) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[11]: # CASE III # Transverse and long. tracking (linear), and wakes from WakeTable source. # Quadrupole X, UniformChargeSlicer n_sigma_z = 2 n_slices = 15 uniform_charge_slicer = UniformChargeSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. wake_file_columns = [ 'time', 'no_dipole_x', 'no_dipole_y', 'quadrupole_x', 'no_quadrupole_y', 'no_dipole_xy', 'no_dipole_yx' ] table = WakeTable(fn, wake_file_columns, printer=SilentPrinter(), warningprinter=SilentPrinter()) wake_field = WakeField(uniform_charge_slicer, table) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[12]: # CASE IV # Transverse and long. tracking (linear), and wakes from WakeTable source. # Quadrupole X, UniformBinSlicer n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. wake_file_columns = [ 'time', 'no_dipole_x', 'no_dipole_y', 'quadrupole_x', 'no_quadrupole_y', 'no_dipole_xy', 'no_dipole_yx' ] table = WakeTable(fn, wake_file_columns, printer=SilentPrinter(), warningprinter=SilentPrinter()) wake_field = WakeField(uniform_bin_slicer, table) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[15]: # CASE V # Transverse and long. tracking (linear), # Resonator circular n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. reson_circ = CircularResonator(R_shunt=1e6, frequency=1e8, Q=1) wake_field = WakeField(uniform_bin_slicer, reson_circ) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[16]: # CASE V b. # Transverse and long. tracking (linear), # Several Resonators circular n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. reson_circ = CircularResonator(R_shunt=1e6, frequency=1e8, Q=1) reson_circ2 = CircularResonator(R_shunt=1e6, frequency=1e9, Q=0.8) reson_circ3 = CircularResonator(R_shunt=5e6, frequency=1e6, Q=0.2) wake_field = WakeField(uniform_bin_slicer, reson_circ, reson_circ2, reson_circ3) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[17]: # CASE V c. # Transverse and long. tracking (linear), # Resonator parallel_plates n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. reson_para = ParallelPlatesResonator(R_shunt=1e6, frequency=1e8, Q=1) wake_field = WakeField(uniform_bin_slicer, reson_para) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[18]: # CASE V d. # Transverse and long. tracking (linear), # Resonator w. longitudinal wake n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. reson = Resonator(R_shunt=1e6, frequency=1e8, Q=1, Yokoya_X1=1, Yokoya_X2=1, Yokoya_Y1=1, Yokoya_Y2=1, switch_Z=True) wake_field = WakeField(uniform_bin_slicer, reson) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[19]: # CASE VI # Transverse and long. tracking (linear), # ResistiveWall circular n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. resis_circ = CircularResistiveWall(pipe_radius=5e-2, resistive_wall_length=C, conductivity=1e6, dt_min=1e-3) wake_field = WakeField(uniform_bin_slicer, resis_circ) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[20]: # CASE VI b. # Transverse and long. tracking (linear), # ResistiveWall parallel_plates n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. resis_para = ParallelPlatesResistiveWall(pipe_radius=5e-2, resistive_wall_length=C, conductivity=1e6, dt_min=1e-3) wake_field = WakeField(uniform_bin_slicer, resis_para) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes) # In[21]: # CASE VII. # Transverse and long. tracking (linear), # Pass mixture of WakeSources to define WakeField. n_sigma_z = 2 n_slices = 15 uniform_bin_slicer = UniformBinSlicer(n_slices=n_slices, n_sigma_z=n_sigma_z) # Definition of WakeField as a composition of different sources. resis_circ = CircularResistiveWall(pipe_radius=5e-2, resistive_wall_length=C, conductivity=1e6, dt_min=1e-3) reson_para = ParallelPlatesResonator(R_shunt=1e6, frequency=1e8, Q=1) wake_file_columns = [ 'time', 'dipole_x', 'dipole_y', 'quadrupole_x', 'quadrupole_y', 'dipole_xy', 'dipole_yx' ] table = WakeTable(fn, wake_file_columns, printer=SilentPrinter(), warningprinter=SilentPrinter()) wake_field = WakeField(uniform_bin_slicer, resis_circ, reson_para, table) trans_map = [m for m in trans_map] map_woWakes = trans_map + [long_map] track_n_save(bunch, map_woWakes)
def run(intensity, chroma=0, i_oct=0): '''Arguments: - intensity: integer number of charges in beam - chroma: first-order chromaticity Q'_{x,y}, identical for both transverse planes - i_oct: octupole current in A (positive i_oct means LOF = i_oct > 0 and LOD = -i_oct < 0) ''' # BEAM AND MACHINE PARAMETERS # ============================ from LHC import LHC # energy set above will enter get_nonlinear_params p0 assert machine_configuration == 'LHC-injection' machine = LHC(n_segments=1, machine_configuration=machine_configuration, **get_nonlinear_params(chroma=chroma, i_oct=i_oct, p0=0.45e12 * e / c)) # BEAM # ==== #print(filling_scheme) epsn_x = 3.e-6 # normalised horizontal emittance epsn_y = 3.e-6 # normalised vertical emittance sigma_z = 1.2e-9 * machine.beta * c / 4. # RMS bunch length in meters beam = machine.generate_6D_Gaussian_bunch(n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=sigma_z, matched=True, filling_scheme=filling_scheme) bunch_list = beam.split_to_views() for b in bunch_list: if b.bucket_id[0] < batch_length: b.x += 1e-3 b.y += 1e-3 bunch = bunch_list[0] print("\n--> Bunch length and emittance: {:g} m, {:g} eVs.".format( bunch.sigma_z(), bunch.epsn_z())) # CREATE BEAM SLICERS # =================== slicer_for_slicemonitor = UniformBinSlicer(50, z_cuts=(-3 * sigma_z, 3 * sigma_z)) slicer_for_wakefields = UniformBinSlicer( 50, z_cuts=(-3 * sigma_z, 3 * sigma_z), circumference=machine.circumference, h_bunch=machine.h_bunch) # CREATE WAKES # ============ wake_table1 = WakeTable( wakefile, [ 'time', 'dipole_x', 'dipole_y', # 'quadrupole_x', 'quadrupole_y', 'noquadrupole_x', 'noquadrupole_y', # 'dipole_xy', 'dipole_yx', 'nodipole_xy', 'nodipole_yx', ]) wake_field = WakeField(slicer_for_wakefields, wake_table1, mpi='linear_mpi_full_ring_fft') # CREATE DAMPER # ============= from PyHEADTAIL_feedback.feedback import OneboxFeedback from PyHEADTAIL_feedback.processors.multiplication import ChargeWeighter from PyHEADTAIL_feedback.processors.register import TurnFIRFilter from PyHEADTAIL_feedback.processors.convolution import Lowpass, FIRFilter from PyHEADTAIL_feedback.processors.resampling import DAC, HarmonicADC, BackToOriginalBins, Upsampler from MD4063_filter_functions import calculate_coefficients_3_tap, calculate_hilbert_notch_coefficients # from PyHEADTAIL_feedback.processors.addition import NoiseGenerator dampingtime = 20. gain = 2. / dampingtime lowpass100kHz = [ 1703, 1169, 1550, 1998, 2517, 3108, 3773, 4513, 5328, 6217, 7174, 8198, 9282, 10417, 11598, 12813, 14052, 15304, 16555, 17793, 19005, 20176, 21294, 22345, 23315, 24193, 24969, 25631, 26171, 26583, 26860, 27000, 27000, 26860, 26583, 26171, 25631, 24969, 24193, 23315, 22345, 21294, 20176, 19005, 17793, 16555, 15304, 14052, 12813, 11598, 10417, 9282, 8198, 7174, 6217, 5328, 4513, 3773, 3108, 2517, 1998, 1550, 1169, 1703 ] lowpassEnhanced = [ 490, 177, -478, -820, -370, 573, 1065, 428, -909, -1632, -799, 1015, 2015, 901, -1592, -3053, -1675, 1642, 3670, 1841, -2828, -6010, -3929, 2459, 7233, 4322, -6384, -17305, -18296, -5077, 16097, 32000, 32000, 16097, -5077, -18296, -17305, -6384, 4322, 7233, 2459, -3929, -6010, -2828, 1841, 3670, 1642, -1675, -3053, -1592, 901, 2015, 1015, -799, -1632, -909, 428, 1065, 573, -370, -820, -478, 177, 490 ] lowpass20MHz = [ 38, 118, 182, 112, -133, -389, -385, -45, 318, 257, -259, -665, -361, 473, 877, 180, -996, -1187, 162, 1670, 1329, -954, -2648, -1219, 2427, 4007, 419, -5623, -6590, 2893, 19575, 32700, 32700, 19575, 2893, -6590, -5623, 419, 4007, 2427, -1219, -2648, -954, 1329, 1670, 162, -1187, -996, 180, 877, 473, -361, -665, -259, 257, 318, -45, -385, -389, -133, 112, 182, 118, 38 ] phaseEqualizer = [ 2, 4, 7, 10, 12, 16, 19, 22, 27, 31, 36, 42, 49, 57, 67, 77, 90, 104, 121, 141, 164, 191, 223, 261, 305, 358, 422, 498, 589, 700, 836, 1004, 1215, 1483, 1832, 2301, 2956, 3944, 5600, 9184, 25000, -16746, -4256, -2056, -1195, -769, -523, -372, -271, -202, -153, -118, -91, -71, -56, -44, -34, -27, -20, -15, -11, -7, -4, -1 ] FIR_phase_filter = np.loadtxt( './injection_error_input_data/FIR_Phase_40MSPS.csv') FIR_phase_filter = np.array(phaseEqualizer) FIR_phase_filter = FIR_phase_filter / float(np.sum(FIR_phase_filter)) FIR_gain_filter = np.array(lowpass20MHz) FIR_gain_filter = FIR_gain_filter / float(np.sum(lowpass20MHz)) # Cut-off frequency of the kicker system fc = 1.0e6 ADC_bits = 16 ADC_range = (-1e-3, 1e-3) # signal processing delay in turns before the first measurements is applied delay = 1 extra_adc_bins = 10 # betatron phase advance between the pickup and the kicker. The value 0.25 # corresponds to the 90 deg phase change from from the pickup measurements # in x-plane to correction kicks in xp-plane. additional_phase = 0.25 # Kicker-to-pickup phase advance 0 deg # additional_phase = 0. # Kicker-to-pickup phase advance 90 deg f_RF = 1. / (machine.circumference / c / (float(machine.h_RF))) # turn_phase_filter_x = calculate_hilbert_notch_coefficients(machine.Q_x, delay, additional_phase) # turn_phase_filter_y = calculate_hilbert_notch_coefficients(machine.Q_y, delay, additional_phase) turn_phase_filter_x = calculate_coefficients_3_tap(machine.Q_x, delay, additional_phase) turn_phase_filter_y = calculate_coefficients_3_tap(machine.Q_y, delay, additional_phase) print('f_RF: ' + str(f_RF)) processors_detailed_x = [ Bypass(), ChargeWeighter(normalization='segment_average'), # NoiseGenerator(RMS_noise_level, debug=False), HarmonicADC(1 * f_RF / 10., ADC_bits, ADC_range, n_extras=extra_adc_bins), TurnFIRFilter(turn_phase_filter_x, machine.Q_x, delay=delay), FIRFilter(FIR_phase_filter, zero_tap=40), Upsampler(3, [1.5, 1.5, 0]), FIRFilter(FIR_gain_filter, zero_tap=34), DAC(ADC_bits, ADC_range), Lowpass(fc, f_cutoff_2nd=10 * fc), BackToOriginalBins(), ] processors_detailed_y = [ Bypass(), ChargeWeighter(normalization='segment_average'), # NoiseGenerator(RMS_noise_level, debug=False), HarmonicADC(1 * f_RF / 10., ADC_bits, ADC_range, n_extras=extra_adc_bins), TurnFIRFilter(turn_phase_filter_y, machine.Q_y, delay=delay), FIRFilter(FIR_phase_filter, zero_tap=40), Upsampler(3, [1.5, 1.5, 0]), FIRFilter(FIR_gain_filter, zero_tap=34), DAC(ADC_bits, ADC_range), Lowpass(fc, f_cutoff_2nd=10 * fc), BackToOriginalBins(), ] # Kicker-to-pickup phase advance 0 deg damper = OneboxFeedback(gain, slicer_for_wakefields, processors_detailed_x, processors_detailed_y, pickup_axis='displacement', kicker_axis='divergence', mpi=True, beta_x=machine.beta_x, beta_y=machine.beta_y) # # Kicker-to-pickup phase advance 90 deg # damper = OneboxFeedback(gain,slicer_for_wakefields, # processors_detailed_x,processors_detailed_y, mpi=True, # pickup_axis='displacement', kicker_axis='displacement') # CREATE MONITORS # =============== try: bucket = machine.longitudinal_map.get_bucket(bunch) except AttributeError: bucket = machine.rfbucket simulation_parameters_dict = { 'gamma': machine.gamma, 'intensity': intensity, 'Qx': machine.Q_x, 'Qy': machine.Q_y, 'Qs': bucket.Q_s, 'beta_x': bunch.beta_Twiss_x(), 'beta_y': bunch.beta_Twiss_y(), 'beta_z': bucket.beta_z, 'epsn_x': bunch.epsn_x(), 'epsn_y': bunch.epsn_y(), 'sigma_z': bunch.sigma_z(), } bunchmonitor = BunchMonitor( outputpath + '/bunchmonitor_{:04d}_chroma={:g}'.format(it, chroma), n_turns, simulation_parameters_dict, write_buffer_to_file_every=512, buffer_size=4096, mpi=True, filling_scheme=filling_scheme) # slicemonitor = SliceMonitor( # outputpath+'/slicemonitor_{:04d}_chroma={:g}_bunch_{:04d}'.format(it, chroma, bunch.bucket_id[0]), # n_turns_slicemon, # slicer_for_slicemonitor, simulation_parameters_dict, # write_buffer_to_file_every=1, buffer_size=n_turns_slicemon) # TRACKING LOOP # ============= machine.one_turn_map.append(damper) machine.one_turn_map.append(wake_field) # for slice statistics monitoring: s_cnt = 0 monitorswitch = False print('\n--> Begin tracking...\n') # GO!!! for i in range(n_turns): t0 = time.clock() # track the beam around the machine for one turn: machine.track(beam) bunch_list = beam.split_to_views() bunch = bunch_list[0] ex, ey, ez = bunch.epsn_x(), bunch.epsn_y(), bunch.epsn_z() mx, my, mz = bunch.mean_x(), bunch.mean_y(), bunch.mean_z() # monitor the bunch statistics (once per turn): bunchmonitor.dump(beam) # if the centroid becomes unstable (>1cm motion) # then monitor the slice statistics: if not monitorswitch: if mx > 1e-2 or my > 1e-2 or i > n_turns - n_turns_slicemon: print("--> Activate slice monitor") monitorswitch = True else: if s_cnt < n_turns_slicemon: # slicemonitor.dump(bunch) s_cnt += 1 # stop the tracking as soon as we have not-a-number values: if not all(np.isfinite(c) for c in [ex, ey, ez, mx, my, mz]): print('*** STOPPING SIMULATION: non-finite bunch stats!') break # print status all 1000 turns: if i % 100 == 0: t1 = time.clock() print('Emittances: ({:.3g}, {:.3g}, {:.3g}) ' '& Centroids: ({:.3g}, {:.3g}, {:.3g})' '@ turn {:d}, {:g} ms, {:s}'.format( ex, ey, ez, mx, my, mz, i, (t1 - t0) * 1e3, time.strftime("%d/%m/%Y %H:%M:%S", time.localtime()))) print('\n*** Successfully completed!')
# multiply with a factor 2 #ww1.wake_table['dipole_y'] = 2*ww1.wake_table['dipole_y'] # for the analytical step wake ww2.wake_table['dipole_y'] = 1.034*ww2.wake_table['dipole_y'] # for the analytical step wake ww2.wake_table['dipole_x'] = 1.034*ww2.wake_table['dipole_x'] # for the analytical step wake ww2.wake_table['quadrupole_y'] = 1.034*ww2.wake_table['quadrupole_y'] # for the analytical step wake ww2.wake_table['quadrupole_x'] = 1.034*ww2.wake_table['quadrupole_x'] # for the analytical step wake #wake_field_kicker = WakeField(slicer_for_wakefields, ww1)#, beta_x=beta_x, beta_y=beta_y) #wake_field_1 = WakeField(slicer_for_wakefields, ww1)#, beta_x=beta_x, beta_y=beta_y) wake_field_2 = WakeField(slicer_for_wakefields, ww2)#, beta_x=beta_x, beta_y=beta_y) wake_field_3 = WakeField(slicer_for_wakefields, ww3)#, beta_x=beta_x, beta_y=beta_y) # CREATE TRANSVERSE AND LONGITUDINAL MAPS # ======================================= scale_factor = 2*bunch.p0 # scale the detuning coefficients in pyheadtail units transverse_map = TransverseMap(s, alpha_x, beta_x, D_x, alpha_y, beta_y, D_y, Q_x, Q_y, [Chromaticity(Qp_x, Qp_y), AmplitudeDetuning(app_x*scale_factor, app_y*scale_factor, app_xy*scale_factor)]) longitudinal_map = LinearMap([alpha], circumference, Q_s) # ======================================================================
# 1) Load the SPS imepdance model n_turns_wake = 1 # for the moment we consider that the wakefield decays after 1 turn wakefile1 = ('/afs/cern.ch/work/n/natriant/private/pyheadtail_example_crabcavity/wakefields/SPS_complete_wake_model_2018_Q26.txt') ww1 = WakeTable(wakefile1, ['time', 'dipole_x', 'dipole_y', 'quadrupole_x', 'quadrupole_y'], n_turns_wake=n_turns_wake) # only dipolar kick- uncomment the following 3 lines #my_length = len(ww1.wake_table['quadrupole_x']) #ww1.wake_table['quadrupole_x'] = np.zeros(my_length) #ww1.wake_table['quadrupole_y'] = np.zeros(my_length) # only quadrupolar kick, uncomment the following 3 lines #my_length = len(ww1.wake_table['dipole_x']) #ww1.wake_table['dipole_x'] = np.zeros(my_length) #ww1.wake_table['dipole_y'] = np.zeros(my_length) wake_field_complete_sps = WakeField(slicer_for_wakefields, ww1)#, beta_x=beta_x, beta_y=beta_y) # 2) Definition of wakefield of a circular resonator reson_circ = CircularResonator(R_shunt=2.2e6, frequency=1.92e9, Q=59000) # assuming here that Q is the quality factor in 1e4 wake_field_reson_circ = WakeField(slicer_for_wakefields, reson_circ) # CREATE TRANSVERSE AND LONGITUDINAL MAPS # ======================================= scale_factor = 2*bunch.p0 # scale the detuning coefficients in pyheadtail units transverse_map = TransverseMap(s, alpha_x, beta_x, D_x, alpha_y, beta_y, D_y, Q_x, Q_y, [Chromaticity(Qp_x, Qp_y), AmplitudeDetuning(app_x*scale_factor, app_y*scale_factor, app_xy*scale_factor)]) longitudinal_map = LinearMap([alpha], circumference, Q_s)