def test_cut_bucket_distribution(self): '''Tests functionality of the cut-bucket matchor ''' nparticles = 100 h1 = 4620 h2 = 4*4620 V1 = 10e6 V2 = 1e6 dphi1 = 0 dphi2 = 0 alpha = 0.00308 p_increment = 0 long_map = RFSystems(self.circumference, [h1, h2], [V1, V2], [dphi1, dphi2], [alpha], self.gamma, p_increment, charge=self.charge, mass=self.mass) bucket = long_map.get_bucket(gamma=self.gamma) is_accepted_fn = bucket.make_is_accepted(margin=0.) bunch = gf.ParticleGenerator( nparticles, 11, constants.e, constants.m_p, self.circumference, self.gamma, distribution_z=gf.cut_distribution( is_accepted=is_accepted_fn, distribution=gf.gaussian2D(0.01))).generate() self.assertEqual(nparticles, len(bunch.z), 'bucket_cut_distribution loses particles') self.assertTrue(np.sum(is_accepted_fn(bunch.z, bunch.dp)) == nparticles, 'not all particles generated with the cut RF matcher' + ' lie inside the specified separatrix')
def generate_6D_Gaussian_bunch(self, n_macroparticles, intensity, epsn_x, epsn_y, sigma_z): '''Generate a 6D Gaussian distribution of particles which is transversely matched to the Synchrotron. Longitudinally, the distribution is matched only in terms of linear focusing. For a non-linear bucket, the Gaussian distribution is cut along the separatrix (with some margin). It will gradually filament into the bucket. This will change the specified bunch length. ''' if self.longitudinal_mode == 'linear': check_inside_bucket = lambda z,dp : np.array(len(z)*[True]) elif self.longitudinal_mode == 'non-linear': check_inside_bucket = self.longitudinal_map.get_bucket( gamma=self.gamma).make_is_accepted(margin=0.05) else: raise NotImplementedError( 'Something wrong with self.longitudinal_mode') eta = self.longitudinal_map.alpha_array[0] - self.gamma**-2 beta_z = np.abs(eta)*self.circumference/2./np.pi/self.longitudinal_map.Qs sigma_dp = sigma_z/beta_z epsx_geo = epsn_x/self.betagamma epsy_geo = epsn_y/self.betagamma injection_optics = self.transverse_map.get_injection_optics() bunch = gen.ParticleGenerator(macroparticlenumber=n_macroparticles, intensity=intensity, charge=self.charge, mass=self.mass, circumference=self.circumference, gamma=self.gamma, distribution_x = gen.gaussian2D(epsx_geo), alpha_x=injection_optics['alpha_x'], beta_x=injection_optics['beta_x'], D_x=injection_optics['D_x'], distribution_y = gen.gaussian2D(epsy_geo), alpha_y=injection_optics['alpha_y'], beta_y=injection_optics['beta_y'], D_y=injection_optics['D_y'], distribution_z = gen.cut_distribution(gen.gaussian2D_asymmetrical(sigma_u=sigma_z, sigma_up=sigma_dp),is_accepted=check_inside_bucket), ).generate() return bunch
def generate_6D_Gaussian_bunch( self, n_macroparticles, intensity, epsn_x, epsn_y, sigma_z ): """Generate a 6D Gaussian distribution of particles which is transversely matched to the Synchrotron. Longitudinally, the distribution is matched only in terms of linear focusing. For a non-linear bucket, the Gaussian distribution is cut along the separatrix (with some margin). It will gradually filament into the bucket. This will change the specified bunch length. """ if self.longitudinal_mode == "linear": check_inside_bucket = lambda z, dp: np.array(len(z) * [True]) Q_s = self.longitudinal_map.Q_s elif self.longitudinal_mode == "non-linear": bucket = self.longitudinal_map.get_bucket( gamma=self.gamma, mass=self.mass, charge=self.charge ) check_inside_bucket = bucket.make_is_accepted(margin=0.05) Q_s = bucket.Q_s else: raise NotImplementedError("Something wrong with self.longitudinal_mode") eta = self.longitudinal_map.alpha_array[0] - self.gamma ** -2 beta_z = np.abs(eta) * self.circumference / 2.0 / np.pi / Q_s sigma_dp = sigma_z / beta_z epsx_geo = epsn_x / self.betagamma epsy_geo = epsn_y / self.betagamma injection_optics = self.transverse_map.get_injection_optics() bunch = generators.ParticleGenerator( macroparticlenumber=n_macroparticles, intensity=intensity, charge=self.charge, mass=self.mass, circumference=self.circumference, gamma=self.gamma, distribution_x=generators.gaussian2D(epsx_geo), alpha_x=injection_optics["alpha_x"], beta_x=injection_optics["beta_x"], D_x=injection_optics["D_x"], distribution_y=generators.gaussian2D(epsy_geo), alpha_y=injection_optics["alpha_y"], beta_y=injection_optics["beta_y"], D_y=injection_optics["D_y"], distribution_z=generators.cut_distribution( generators.gaussian2D_asymmetrical(sigma_u=sigma_z, sigma_up=sigma_dp), is_accepted=check_inside_bucket, ), ).generate() return bunch
def generate_6D_Gaussian_bunch(self, n_macroparticles, intensity, epsn_x, epsn_y, sigma_z): '''Generate a 6D Gaussian distribution of particles which is transversely matched to the Synchrotron. Longitudinally, the distribution is matched only in terms of linear focusing. For a non-linear bucket, the Gaussian distribution is cut along the separatrix (with some margin). It will gradually filament into the bucket. This will change the specified bunch length. ''' if self.longitudinal_focusing == 'linear': check_inside_bucket = lambda z, dp: np.array(len(z) * [True]) elif self.longitudinal_focusing == 'non-linear': check_inside_bucket = self.longitudinal_map.get_bucket( gamma=self.gamma).make_is_accepted(margin=0.05) else: raise NotImplementedError( 'Something wrong with self.longitudinal_focusing') beta_z = np.abs(self.eta) * self.circumference / 2. / np.pi / self.Q_s sigma_dp = sigma_z / beta_z epsx_geo = epsn_x / self.betagamma epsy_geo = epsn_y / self.betagamma bunch = gen.ParticleGenerator( macroparticlenumber=n_macroparticles, intensity=intensity, charge=self.charge, mass=self.mass, circumference=self.circumference, gamma=self.gamma, distribution_x=gen.gaussian2D(epsx_geo), alpha_x=self.alpha_x[0], beta_x=self.beta_x[0], D_x=self.D_x[0], distribution_y=gen.gaussian2D(epsy_geo), alpha_y=self.alpha_y[0], beta_y=self.beta_y[0], D_y=self.D_y[0], distribution_z=gen.cut_distribution( gen.gaussian2D_asymmetrical(sigma_u=sigma_z, sigma_up=sigma_dp), is_accepted=check_inside_bucket)).generate() return bunch
def prepareDis(self, twiss, closed_orbit): if closed_orbit is not None: x_co = twiss[0]['x'] y_co = twiss[0]['y'] else: x_co = 0 y_co = 0 np.random.seed(0) D_x_0 = twiss[0]['dx'] * self.beta D_y_0 = twiss[0]['dy'] * self.beta Dp_x_0 = twiss[0]['dpx'] * self.beta Dp_y_0 = twiss[0]['dpy'] * self.beta bx_0 = twiss[0]['betx'] by_0 = twiss[0]['bety'] s0 = twiss[-1]['s'] circumference = s0 alfx_0 = twiss[0]['alfx'] alfy_0 = twiss[0]['alfy'] pyht_beam = generators.generate_Gaussian6DTwiss( self.npart, 1, self.charge, self.mass, s0, self.gamma, alfx_0, alfy_0, bx_0, by_0, 1, self.epsn_x, self.epsn_y, 1, dispersion_x=None, dispersion_y=None, limit_n_rms_x=self.limit_n_rms_x**2, limit_n_rms_y=self.limit_n_rms_y**2, limit_n_rms_z=self.limit_n_rms_z**2, ) distribution_z_uncut = generators.gaussian2D(self.sig_z**2) is_accepted = generators.make_is_accepted_within_n_sigma( epsn_rms=self.sig_z, limit_n_rms=2.5, ) distribution_z_cut = generators.cut_distribution( distribution_z_uncut, is_accepted) z, dp = distribution_z_cut(self.npart) pyht_beam.z, pyht_beam.dp = z, dp / self.beta_z # recentre on 0 to avoid dipolar motion: pyht_beam.x -= pyht_beam.mean_x() pyht_beam.xp -= pyht_beam.mean_xp() pyht_beam.y -= pyht_beam.mean_y() pyht_beam.yp -= pyht_beam.mean_yp() pyht_beam.z -= pyht_beam.mean_z() pyht_beam.dp -= pyht_beam.mean_dp() # PyHT generates around 0, need to offset with closed orbit: pyht_beam.x += x_co pyht_beam.y += y_co # add dispersive contribution to coordinates: pyht_beam.x += D_x_0 * pyht_beam.dp pyht_beam.y += D_y_0 * pyht_beam.dp # also need to add D'_{x,y} to momenta: pyht_beam.xp += Dp_x_0 * pyht_beam.dp pyht_beam.yp += Dp_y_0 * pyht_beam.dp return pyht_beam
def characterize_impedances(wake_dipolar_element, wake_quadrupolar_element, n_samples_hh_kk, test_amplitude, intensity, sigma_z, circumference, particle_charge, particle_mass, particle_gamma, z_cut, n_tail_cut, detuning_fit_order): # Build response matrix for dipolar impedance slicer_for_harmonicresponse = UniformBinSlicer(n_samples_hh_kk, z_cuts=(-z_cut, z_cut)) # Make a test bunch (I need only longitudinal profile and energy to be correct) bunch = generators.ParticleGenerator( macroparticlenumber=n_samples_hh_kk * 1000, intensity=intensity, charge=particle_charge, mass=particle_mass, circumference=circumference, gamma=particle_gamma, distribution_x=generators.gaussian2D(1e-9), # Dummy not really used alpha_x=0, beta_x=100, D_x=0., distribution_y=generators.gaussian2D(1e-9), alpha_y=0., beta_y=100., D_y=0., distribution_z=generators.cut_distribution( generators.gaussian2D_asymmetrical(sigma_u=sigma_z, sigma_up=1e-4), is_accepted=(lambda z, dp: np.array(len(z) * [True]))), ).generate() bunch.x *= 0 bunch.xp *= 0 bunch.y *= 0 bunch.yp *= 0 # Generate configurations assert (n_samples_hh_kk % 2 == 0) cos_ampl_list = [] sin_ampl_list = [] n_osc_list = [] for ii in range(n_samples_hh_kk // 2): cos_ampl_list.append(test_amplitude) sin_ampl_list.append(0.) n_osc_list.append(ii) cos_ampl_list.append(0.) sin_ampl_list.append(test_amplitude) n_osc_list.append(ii + 1) # cos_ampl_list = [100*1e-4] # sin_ampl_list = [0] # n_osc_list = [3] # Measure responses x_meas_mat = [] x_mat = [] dpx_mat = [] y_meas_mat = [] y_mat = [] dpy_mat = [] for itest in range(len(cos_ampl_list)): N_oscillations = n_osc_list[itest] sin_amplitude = sin_ampl_list[itest] cos_amplitude = cos_ampl_list[itest] # Recenter all slices slices_set = bunch.get_slices(slicer_for_harmonicresponse, statistics=True) for ii in range(slices_set.n_slices): ix = slices_set.particle_indices_of_slice(ii) if len(ix) > 0: bunch.x[ix] -= np.mean(bunch.x[ix]) bunch.xp[ix] -= np.mean(bunch.xp[ix]) bunch.y[ix] -= np.mean(bunch.y[ix]) bunch.yp[ix] -= np.mean(bunch.yp[ix]) # Get slice centers z_slices = slices_set.z_centers N_slices = len(z_slices) # Get z_step beween slices and define z_range z_step = z_slices[1] - z_slices[0] z_range = z_slices[-1] - z_slices[0] + z_step # Last term is to make # sinusoids numerically # orthogonal # Generate ideal sinusoidal distortion x_y_ideal = (sin_amplitude * np.sin(2 * np.pi * N_oscillations * z_slices / z_range) + cos_amplitude * np.cos(2 * np.pi * N_oscillations * z_slices / z_range)) # Add sinusoidal distortion to particles bunch.x += sin_amplitude * np.sin( 2 * np.pi * N_oscillations * bunch.z / z_range) bunch.x += cos_amplitude * np.cos( 2 * np.pi * N_oscillations * bunch.z / z_range) bunch.y += sin_amplitude * np.sin( 2 * np.pi * N_oscillations * bunch.z / z_range) bunch.y += cos_amplitude * np.cos( 2 * np.pi * N_oscillations * bunch.z / z_range) # Measure bunch.clean_slices() slices_set = bunch.get_slices(slicer_for_harmonicresponse, statistics=True) x_slices = slices_set.mean_x y_slices = slices_set.mean_y int_slices = slices_set.lambda_bins() / qe bunch.clean_slices() # Apply impedance wake_dipolar_element.track(bunch) # Measure kicks bunch.clean_slices() slices_set = bunch.get_slices(slicer_for_harmonicresponse, statistics=True) dpx_slices = slices_set.mean_xp dpy_slices = slices_set.mean_yp # Store results x_mat.append(x_y_ideal.copy()) x_meas_mat.append(x_slices.copy()) dpx_mat.append(dpx_slices.copy()) y_mat.append(x_y_ideal.copy()) y_meas_mat.append(y_slices.copy()) dpy_mat.append(dpy_slices.copy()) x_mat = np.array(x_mat) x_meas_mat = np.array(x_meas_mat) dpx_mat = np.array(dpx_mat) y_mat = np.array(y_mat) y_meas_mat = np.array(y_meas_mat) dpy_mat = np.array(dpy_mat) HH_x = x_mat KK_x = dpx_mat HH_y = y_mat KK_y = dpy_mat if n_tail_cut > 0: KK_x[:, :n_tail_cut] = 0. KK_x[:, -n_tail_cut:] = 0. KK_y[:, :n_tail_cut] = 0. KK_y[:, -n_tail_cut:] = 0. ############################# # Detuning characterization # ############################# if wake_quadrupolar_element is not None: bunch.x *= 0 bunch.xp *= 0 bunch.y *= 0 bunch.yp *= 0 bunch.x += test_amplitude bunch.y += test_amplitude bunch.clean_slices() wake_quadrupolar_element.track(bunch) bunch.clean_slices() slices_set = bunch.get_slices(slicer_for_harmonicresponse, statistics=True) dpx_slices = slices_set.mean_xp dpy_slices = slices_set.mean_yp k_quadx = slices_set.mean_xp / test_amplitude k_quady = slices_set.mean_yp / test_amplitude px = np.polyfit(z_slices, k_quadx, deg=detuning_fit_order) py = np.polyfit(z_slices, k_quady, deg=detuning_fit_order) alpha_Nx = px[::-1] alpha_Ny = py[::-1] wake_characterization = { 'HH_x': HH_x, 'KK_x': KK_x, 'HH_y': HH_y, 'KK_y': KK_y, 'z_slices': z_slices, 'alpha_Nx': alpha_Nx, 'alpha_Ny': alpha_Ny } return wake_characterization