|
| 1 | +''' |
| 2 | +Simulation of beam interaction with coupling impedance and damper |
| 3 | +for a single bunch |
| 4 | +''' |
| 5 | + |
| 6 | +# using Python 2.7: |
| 7 | +from __future__ import division, print_function |
| 8 | +range_ = range |
| 9 | +range = xrange |
| 10 | + |
| 11 | +import sys |
| 12 | +# PyHEADTAIL location if it's not already in the PYTHONPATH environment variable |
| 13 | +sys.path.append('../../') |
| 14 | + |
| 15 | + |
| 16 | + |
| 17 | +import time |
| 18 | + |
| 19 | +import numpy as np |
| 20 | +np.random.seed(10000042) |
| 21 | +import h5py |
| 22 | +from scipy.constants import e, m_p, c |
| 23 | + |
| 24 | +from PyHEADTAIL.particles.slicing import UniformBinSlicer |
| 25 | +from PyHEADTAIL.impedances.wakes import WakeTable, WakeField |
| 26 | +from PyHEADTAIL.feedback.feedback import IdealBunchFeedback |
| 27 | +from PyHEADTAIL.monitors.monitors import ( |
| 28 | + BunchMonitor, ParticleMonitor, SliceMonitor) |
| 29 | + |
| 30 | + |
| 31 | +n_macroparticles = 100000 # number of macro-particles to resolve the beam |
| 32 | +n_turns = 6000 # simulation time |
| 33 | +n_turns_slicemon = 64 # recording span of the slice statistics monitor |
| 34 | + |
| 35 | + |
| 36 | +# COMMENT THE NON-WANTED SET-UP: |
| 37 | + |
| 38 | +# # injection |
| 39 | +machine_configuration = 'LHC-injection' |
| 40 | +wakefile = ('./wakeforhdtl_PyZbase_Allthemachine_450GeV' |
| 41 | + '_B1_LHC_inj_450GeV_B1.dat') |
| 42 | + |
| 43 | +# flat-top |
| 44 | +# machine_configuration = 'LHC_6.5TeV_collision_2016' |
| 45 | +# wakefile = ('./wakeforhdtl_PyZbase_Allthemachine_6p5TeV' |
| 46 | +# '_B1_LHC_ft_6.5TeV_B1.dat') |
| 47 | + |
| 48 | +# --- |
| 49 | + |
| 50 | +def get_nonlinear_params(chroma, i_oct, p0=6.5e12*e/c): |
| 51 | + '''Arguments: |
| 52 | + - chroma: first-order chromaticity Q'_{x,y}, identical |
| 53 | + for both transverse planes |
| 54 | + - i_oct: octupole current in A (positive i_oct means |
| 55 | + LOF = i_oct > 0 and LOD = -i_oct < 0) |
| 56 | + ''' |
| 57 | + # factor 2p0 is PyHEADTAIL's convention for d/dJx instead of |
| 58 | + # MAD-X's convention of d/d(2Jx) |
| 59 | + print('p0: ' + str(p0/(e/c))) |
| 60 | + |
| 61 | + app_x = 2 * p0 * 27380.10941 * i_oct / 100. |
| 62 | + app_y = 2 * p0 * 28875.03442 * i_oct / 100. |
| 63 | + app_xy = 2 * p0 * -21766.48714 * i_oct / 100. |
| 64 | + Qpp_x = 4889.00298 * i_oct / 100. |
| 65 | + Qpp_y = -2323.147896 * i_oct / 100. |
| 66 | + return { |
| 67 | + 'app_x': app_x, |
| 68 | + 'app_y': app_y, |
| 69 | + 'app_xy': app_xy, |
| 70 | + 'Qp_x': [chroma,],# Qpp_x], |
| 71 | + 'Qp_y': [chroma,],# Qpp_y], |
| 72 | + # second-order chroma commented out above! |
| 73 | + } |
| 74 | + |
| 75 | + |
| 76 | +def run(intensity, chroma=0, i_oct=0): |
| 77 | + '''Arguments: |
| 78 | + - intensity: integer number of charges in beam |
| 79 | + - chroma: first-order chromaticity Q'_{x,y}, identical |
| 80 | + for both transverse planes |
| 81 | + - i_oct: octupole current in A (positive i_oct means |
| 82 | + LOF = i_oct > 0 and LOD = -i_oct < 0) |
| 83 | + ''' |
| 84 | + |
| 85 | + |
| 86 | + # BEAM AND MACHINE PARAMETERS |
| 87 | + # ============================ |
| 88 | + from LHC import LHC |
| 89 | + # energy set above will enter get_nonlinear_params p0 |
| 90 | + assert machine_configuration == 'LHC-injection' |
| 91 | + machine = LHC(n_segments=1, |
| 92 | + machine_configuration=machine_configuration, |
| 93 | + **get_nonlinear_params(chroma=chroma, i_oct=i_oct,p0=0.45e12*e/c)) |
| 94 | + |
| 95 | + |
| 96 | + # BEAM |
| 97 | + # ==== |
| 98 | + epsn_x = 3.e-6 # normalised horizontal emittance |
| 99 | + epsn_y = 3.e-6 # normalised vertical emittance |
| 100 | + sigma_z = 1.2e-9 * machine.beta*c/4. # RMS bunch length in meters |
| 101 | + |
| 102 | + bunch = machine.generate_6D_Gaussian_bunch( |
| 103 | + n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=sigma_z, matched=True) |
| 104 | + |
| 105 | + print ("\n--> Bunch length and emittance: {:g} m, {:g} eVs.".format( |
| 106 | + bunch.sigma_z(), bunch.epsn_z())) |
| 107 | + |
| 108 | + |
| 109 | + # CREATE BEAM SLICERS |
| 110 | + # =================== |
| 111 | + slicer_for_slicemonitor = UniformBinSlicer( |
| 112 | + 50, z_cuts=(-3*sigma_z, 3*sigma_z)) |
| 113 | + slicer_for_wakefields = UniformBinSlicer( |
| 114 | + 50, z_cuts=(-3*sigma_z, 3*sigma_z), |
| 115 | + circumference=machine.circumference, |
| 116 | + h_bunch=machine.h_bunch) |
| 117 | + print("Slice") |
| 118 | + |
| 119 | + |
| 120 | + |
| 121 | + # CREATE WAKES |
| 122 | + # ============ |
| 123 | + wake_table1 = WakeTable(wakefile, |
| 124 | + ['time', 'dipole_x', 'dipole_y', |
| 125 | + # 'quadrupole_x', 'quadrupole_y', |
| 126 | + 'noquadrupole_x', 'noquadrupole_y', |
| 127 | + # 'dipole_xy', 'dipole_yx', |
| 128 | + 'nodipole_xy', 'nodipole_yx', |
| 129 | + ]) |
| 130 | + wake_field = WakeField(slicer_for_wakefields, wake_table1, mpi=True) |
| 131 | + |
| 132 | + |
| 133 | + # CREATE DAMPER |
| 134 | + # ============= |
| 135 | + dampingtime = 50. |
| 136 | + gain = 2./dampingtime |
| 137 | + damper = IdealBunchFeedback(gain) |
| 138 | + |
| 139 | + |
| 140 | + # CREATE MONITORS |
| 141 | + # =============== |
| 142 | + try: |
| 143 | + bucket = machine.longitudinal_map.get_bucket(bunch) |
| 144 | + except AttributeError: |
| 145 | + bucket = machine.rfbucket |
| 146 | + |
| 147 | + simulation_parameters_dict = { |
| 148 | + 'gamma' : machine.gamma, |
| 149 | + 'intensity': intensity, |
| 150 | + 'Qx' : machine.Q_x, |
| 151 | + 'Qy' : machine.Q_y, |
| 152 | + 'Qs' : bucket.Q_s, |
| 153 | + 'beta_x' : bunch.beta_Twiss_x(), |
| 154 | + 'beta_y' : bunch.beta_Twiss_y(), |
| 155 | + 'beta_z' : bucket.beta_z, |
| 156 | + 'epsn_x' : bunch.epsn_x(), |
| 157 | + 'epsn_y' : bunch.epsn_y(), |
| 158 | + 'sigma_z' : bunch.sigma_z(), |
| 159 | + } |
| 160 | + bunchmonitor = BunchMonitor( |
| 161 | + outputpath+'/bunchmonitor_{:04d}_chroma={:g}'.format(it, chroma), |
| 162 | + n_turns, simulation_parameters_dict, |
| 163 | + write_buffer_to_file_every=512, |
| 164 | + buffer_size=4096) |
| 165 | + slicemonitor = SliceMonitor( |
| 166 | + outputpath+'/slicemonitor_{:04d}_chroma={:g}'.format(it, chroma), |
| 167 | + n_turns_slicemon, |
| 168 | + slicer_for_slicemonitor, simulation_parameters_dict, |
| 169 | + write_buffer_to_file_every=1, buffer_size=n_turns_slicemon) |
| 170 | + |
| 171 | + |
| 172 | + # TRACKING LOOP |
| 173 | + # ============= |
| 174 | + # machine.one_turn_map.append(damper) |
| 175 | + machine.one_turn_map.append(wake_field) |
| 176 | + |
| 177 | + |
| 178 | + # for slice statistics monitoring: |
| 179 | + s_cnt = 0 |
| 180 | + monitorswitch = False |
| 181 | + |
| 182 | + print ('\n--> Begin tracking...\n') |
| 183 | + |
| 184 | + # GO!!! |
| 185 | + for i in range(n_turns): |
| 186 | + |
| 187 | + t0 = time.clock() |
| 188 | + |
| 189 | + # track the beam around the machine for one turn: |
| 190 | + machine.track(bunch) |
| 191 | + |
| 192 | + ex, ey, ez = bunch.epsn_x(), bunch.epsn_y(), bunch.epsn_z() |
| 193 | + mx, my, mz = bunch.mean_x(), bunch.mean_y(), bunch.mean_z() |
| 194 | + |
| 195 | + # monitor the bunch statistics (once per turn): |
| 196 | + bunchmonitor.dump(bunch) |
| 197 | + |
| 198 | + # if the centroid becomes unstable (>1cm motion) |
| 199 | + # then monitor the slice statistics: |
| 200 | + if not monitorswitch: |
| 201 | + if mx > 1e-2 or my > 1e-2 or i > n_turns - n_turns_slicemon: |
| 202 | + print ("--> Activate slice monitor") |
| 203 | + monitorswitch = True |
| 204 | + else: |
| 205 | + if s_cnt < n_turns_slicemon: |
| 206 | + slicemonitor.dump(bunch) |
| 207 | + s_cnt += 1 |
| 208 | + |
| 209 | + # stop the tracking as soon as we have not-a-number values: |
| 210 | + if not all(np.isfinite(c) for c in [ex, ey, ez, mx, my, mz]): |
| 211 | + print ('*** STOPPING SIMULATION: non-finite bunch stats!') |
| 212 | + break |
| 213 | + |
| 214 | + # print status all 1000 turns: |
| 215 | + if i % 100 == 0: |
| 216 | + t1 = time.clock() |
| 217 | + print ('Emittances: ({:.3g}, {:.3g}, {:.3g}) ' |
| 218 | + '& Centroids: ({:.3g}, {:.3g}, {:.3g})' |
| 219 | + '@ turn {:d}, {:g} ms, {:s}'.format( |
| 220 | + ex, ey, ez, mx, my, mz, i, (t1-t0)*1e3, time.strftime( |
| 221 | + "%d/%m/%Y %H:%M:%S", time.localtime())) |
| 222 | + ) |
| 223 | + |
| 224 | + print ('\n*** Successfully completed!') |
| 225 | + |
| 226 | + |
| 227 | +if __name__ == '__main__': |
| 228 | + # iteration, attached to monitor name: |
| 229 | + it = 0 |
| 230 | + # outputpath relative to this file: |
| 231 | + outputpath = './' |
| 232 | + |
| 233 | + # run the simulation: |
| 234 | + run(intensity=1.1e11, chroma=12, i_oct=0) |
0 commit comments