Beispiel #1
0
 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')
Beispiel #2
0
    def generate_6D_Gaussian_bunch_matched(
            self, n_macroparticles, intensity, epsn_x, epsn_y,
            sigma_z=None, epsn_z=None, margin=0):
        '''Generate a 6D Gaussian distribution of particles which is
        transversely as well as longitudinally matched.
        The distribution is found iteratively to exactly yield the
        given bunch length while at the same time being stationary in
        the non-linear bucket. Thus, the bunch length should amount
        to the one specificed and should not change significantly
        during the synchrotron motion.

        Requires self.longitudinal_focusing == 'non-linear'
        for the bucket.
        '''
        assert self.longitudinal_focusing == 'non-linear'
        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.RF_bucket_distribution(
                    rfbucket=self.longitudinal_map.get_bucket(gamma=self.gamma),
                    sigma_z=sigma_z, epsn_z=epsn_z, margin=margin,
                    printer=self._printer)
                ).generate()

        return bunch
 def test_rf_bucket_distribution(self):
     '''Tests the functionality of the rf-bucket matchor'''
     #SPS Q20 flattop
     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)
     bunch = gf.ParticleGenerator(nparticles,
                                  1e11,
                                  constants.e,
                                  constants.m_p,
                                  self.circumference,
                                  self.gamma,
                                  distribution_z=gf.RF_bucket_distribution(
                                      bucket,
                                      epsn_z=0.002,
                                      printer=SilentPrinter())).generate()
Beispiel #4
0
 def test_import_distribution_raises_error(self):
     '''Tests whether the generation fails when the number of particles
     and the size of the specified distribution list do not match
     '''
     nparticles = 10
     coords = [np.linspace(-2, 2, nparticles+1),
               np.linspace(-3, 3, nparticles+1)]
     import_generator = gf.ParticleGenerator(
             nparticles, 1e11, constants.e, constants.m_p, 100, 10,
             distribution_y=gf.import_distribution2D(coords))
     with self.assertRaises(AssertionError):
         beam = import_generator.generate()
    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
Beispiel #6
0
 def test_import_distribution(self):
     '''Tests whether import_distribution produces coordinate arrays of the
     correct size'''
     nparticles = 5
     coords = [np.linspace(-2, 2, nparticles),
               np.linspace(-3, 3, nparticles)]
     import_generator = gf.ParticleGenerator(
             nparticles, 1e11, constants.e, constants.m_p, 100, 10,
             distribution_y=gf.import_distribution2D(coords))
     beam = import_generator.generate()
     self.assertEqual(len(beam.y), nparticles,
             'import_generator produces coords with the wrong length')
     self.assertEqual(len(beam.yp), nparticles,
             'import_generator produces coords with the wrong length')
    def generate_6D_Gaussian_bunch_matched(
        self, n_macroparticles, intensity, epsn_x, epsn_y, sigma_z=None, epsn_z=None
    ):
        """Generate a 6D Gaussian distribution of particles which is
        transversely as well as longitudinally matched.
        The distribution is found iteratively to exactly yield the
        given bunch length while at the same time being stationary in
        the non-linear bucket. Thus, the bunch length should amount
        to the one specificed and should not change significantly
        during the synchrotron motion.

        Requires self.longitudinal_mode == 'non-linear'
        for the bucket.
        """
        if self.longitudinal_mode == 'linear':
            assert(sigma_z is not None)
            bunch = self.generate_6D_Gaussian_bunch(n_macroparticles, intensity,
                    epsn_x, epsn_y, sigma_z)
        elif self.longitudinal_mode == "non-linear":
            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.RF_bucket_distribution(
                    self.longitudinal_map.get_bucket(gamma=self.gamma),
                    sigma_z=sigma_z,
                    epsn_z=epsn_z,
                ),
            ).generate()
        else:
            raise ValueError('Unknown longitudinal mode!')

        return bunch
Beispiel #8
0
 def setUp(self):
     np.random.seed(0)
     self.nparticles = 1000
     self.epsx = 0.5
     self.intensity = 1e11
     self.charge = constants.e
     self.mass = constants.m_p
     self.circumference = 99
     self.gamma = 27.1
     self.generator = gf.ParticleGenerator(
         self.nparticles, self.intensity,
         self.charge, self.mass, self.circumference, self.gamma,
         distribution_x=gf.gaussian2D(0.5),alpha_x=-0.7, beta_x=4, D_x=0,
         distribution_z=gf.gaussian2D(3.0),
         printer=SilentPrinter())
     self.beam = self.generator.generate()
Beispiel #9
0
 def test_update_beam_with_new_coords(self):
     '''Tests whether adding new coordinates to the beam
     works as expected
     '''
     x_copy = self.beam.x.copy()
     longitudinal_generator = gf.ParticleGenerator(
         self.nparticles, self.intensity, self.charge,
         self.mass, self.circumference, self.gamma,
         distribution_z=gf.gaussian2D(3.0))
     longitudinal_generator.update(self.beam)
     self.assertEqual(self.beam.dp.size, self.nparticles,
                      'Updating the beam with new coordinates leads to' +
                      'faulty coordinates')
     for n in range(self.nparticles):
         self.assertAlmostEqual(x_copy[n], self.beam.x[n],
             msg='Updating the beam with new coordinates invalidates' +
             'existing coordinates')
Beispiel #10
0
    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
Beispiel #11
0
    def generate_6D_Gaussian_bunch_matched_parabolic(self,
                                                     n_macroparticles,
                                                     intensity,
                                                     epsn_x,
                                                     epsn_y,
                                                     sigma_z=None,
                                                     epsn_z=None,
                                                     margin=0):
        '''
        Modified version to use parabolic longitudinal profile
        instead of the gaussian one

        Added 06.04.2018
        '''
        assert self.longitudinal_focusing == 'non-linear'
        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.RF_bucket_distribution(
                rfbucket=self.longitudinal_map.get_bucket(gamma=self.gamma),
                distribution_type=ParabolicDistribution,
                sigma_z=sigma_z,
                epsn_z=epsn_z,
                margin=margin,
                printer=self._printer)).generate()

        return bunch
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