示例#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')
示例#2
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_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
示例#3
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_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
示例#4
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
示例#5
0
    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