def reprocess(self, new_slicing): ''' *Reprocess the impedance contributions with respect to the new_slicing.* ''' self.slices = new_slicing time_resolution = (self.slices.bin_centers[1] - self.slices.bin_centers[0]) if self.frequency_resolution_input == None: self.n_fft_sampling = self.slices.n_slices else: if self.freq_res_option is 'round': self.n_fft_sampling = next_regular( int( np.round(1 / (self.frequency_resolution_input * time_resolution)))) elif self.freq_res_option is 'ceil': self.n_fft_sampling = next_regular( int( np.ceil(1 / (self.frequency_resolution_input * time_resolution)))) elif self.freq_res_option is 'floor': self.n_fft_sampling = next_regular( int( np.floor(1 / (self.frequency_resolution_input * time_resolution)))) else: raise RuntimeError( 'The input freq_res_option is not recognized') if self.n_fft_sampling < self.slices.n_slices: print( 'The input frequency resolution step is too big, and the whole \ bunch is not sliced... The number of sampling points for the \ FFT is corrected in order to sample the whole bunch (and \ you might consider changing the input in order to have \ a finer resolution).') self.n_fft_sampling = next_regular(self.slices.n_slices) #: *Real frequency resolution in [Hz], according to the obtained n_fft_sampling.* self.frequency_resolution = 1 / (self.n_fft_sampling * time_resolution) self.slices.beam_spectrum_generation(self.n_fft_sampling, only_rfft=True) #: *Frequency array of the impedance in [Hz]* self.frequency_array = self.slices.beam_spectrum_freq #: *Total impedance array of all sources in* [:math:`\Omega`] self.total_impedance = 0 self.sum_impedances(self.frequency_array)
def reprocess(self, new_slicing): ''' *Reprocess the wake contributions with respect to the new_slicing.* ''' self.slices = new_slicing self.time_array = self.slices.bin_centers - self.slices.bin_centers[0] self.sum_wakes(self.time_array) self.cut = len(self.time_array) + len(self.slices.n_macroparticles) - 1 self.fshape = next_regular(self.cut)
def process(self): ''' *Reprocess the impedance contributions. To be run when slices changes* ''' _InducedVoltage.process(self) # Number of points for the FFT, equal to the length of the induced # voltage array + number of slices -1 to calculate a linear convolution # in the frequency domain. The next regular number is used for speed, # therefore the frequency resolution is always equal or finer than # the input value self.n_fft = next_regular( int(self.n_induced_voltage) + int(self.slices.n_slices) - 1) #: *Time array of the wake in s* self.time = np.arange(0, self.wake_length, self.wake_length / self.n_induced_voltage) # Processing the wakes self.sum_wakes(self.time)
def __init__(self, Slices, wake_source_list, n_turns_memory=0, time_or_freq='freq', main_harmonic_number=1): #: *Copy of the Slices object in order to access the profile info.* self.slices = Slices #: *Wake sources inputed as a list (eg: list of BBResonators objects)* self.wake_source_list = wake_source_list #: *Time array of the wake in [s]* self.time_array = 0 #: *Total wake array of all sources in* [:math:`\Omega / s`] self.total_wake = 0 #: *Induced voltage from the sum of the wake sources in [V]* self.induced_voltage = 0 # Pre-processing the wakes self.time_array = self.slices.bin_centers - self.slices.bin_centers[0] self.sum_wakes(self.time_array) self.cut = len(self.time_array) + len(self.slices.n_macroparticles) - 1 self.fshape = next_regular(self.cut) self.time_or_freq = time_or_freq if n_turns_memory > 0: self.main_harmonic_number = main_harmonic_number delta_t = self.slices.bin_centers[1] - self.slices.bin_centers[0] self.n_points_wake = self.slices.n_slices * main_harmonic_number * ( n_turns_memory + 1) self.time_array_wake_extended = np.linspace( 0, delta_t * (self.n_points_wake - 1), self.n_points_wake) self.sum_wakes(self.time_array_wake_extended) self.time_array_memory = self.time_array_wake_extended + delta_t / 2
def process(self): ''' *Reprocess the impedance contributions. To be run when slices changes* ''' _InducedVoltage.process(self) # Number of points for the FFT. The next regular number is used for # speed, therefore the frequency resolution is always equal or finer # than the input value self.n_fft = next_regular(self.n_induced_voltage) self.slices.beam_spectrum_freq_generation(self.n_fft) #: *Frequency array of the impedance in Hz* self.freq = self.slices.beam_spectrum_freq # Length of the front wake in frequency domain calculations if self.front_wake_length: self.front_wake_buffer = int( np.ceil(np.max(self.front_wake_length) / self.slices.bin_size)) # Processing the impedances self.sum_impedances(self.freq)
def generate(self): for i in range(0, int(self.n_turns/self.corr)): # Scale amplitude to keep area (phase noise amplitude) constant k = i*self.corr # current time step ampl = self.A_i*self.fs[0]/self.fs[k] # Calculate the frequency step f_max = self.f0[k]/2 n_points_pos_f_incl_zero = np.ceil(f_max/self.delta_f) + 1 nt = 2*(n_points_pos_f_incl_zero - 1) nt_regular = next_regular(int(nt)) if nt_regular%2!=0 or nt_regular < self.corr: raise RuntimeError('Error in noise generation!') n_points_pos_f_incl_zero = nt_regular/2 + 1 freq = np.linspace(0, f_max, n_points_pos_f_incl_zero) delta_f = f_max/(n_points_pos_f_incl_zero-1) # Construct spectrum nmin = np.floor(self.fmin_s0*self.fs[k]/delta_f) nmax = np.ceil(self.fmax_s0*self.fs[k]/delta_f) # To compensate the notch due to PL at central frequency if self.predistortion == 'exponential': spectrum = np.concatenate((np.zeros(nmin), ampl*np.exp( np.log(100.)*np.arange(0,nmax-nmin+1)/(nmax-nmin) ), np.zeros(n_points_pos_f_incl_zero-nmax-1) )) elif self.predistortion == 'linear': spectrum = np.concatenate((np.zeros(nmin), np.linspace(0, ampl, nmax-nmin+1), np.zeros(n_points_pos_f_incl_zero-nmax-1))) elif self.predistortion == 'hyperbolic': spectrum = np.concatenate((np.zeros(nmin), ampl*np.ones(nmax-nmin+1)* \ 1/(1 + 0.99*(nmin - np.arange(nmin,nmax+1)) /(nmax-nmin)), np.zeros(n_points_pos_f_incl_zero-nmax-1) )) elif self.predistortion == 'weightfunction': frel = freq[nmin:nmax+1]/self.fs[k] # frequency relative to fs0 frel[np.where(frel > 0.999)[0]] = 0.999 # truncate center freqs sigma = 0.754 # rms bunch length in rad corresponding to 1.2 ns gamma = 0.577216 weight = (4.*np.pi*frel/sigma**2)**2 * \ np.exp(-16.*(1. - frel)/sigma**2) + \ 0.25*( 1 + 8.*frel/sigma**2 * np.exp(-8.*(1. - frel)/sigma**2) * ( gamma + np.log(8.*(1. - frel)/sigma**2) + 8.*(1. - frel)/sigma**2 ) )**2 weight /= weight[0] # normalise to have 1 at fmin spectrum = np.concatenate((np.zeros(nmin), ampl*weight, np.zeros(n_points_pos_f_incl_zero-nmax-1))) else: spectrum = np.concatenate((np.zeros(nmin), ampl*np.ones(nmax-nmin+1), np.zeros(n_points_pos_f_incl_zero-nmax-1))) # Fill phase noise array if i < int(self.n_turns/self.corr) - 1: kmax = (i + 1)*self.corr else: kmax = self.n_turns + 1 self.spectrum_to_phase_noise(freq, spectrum) self.seed1 +=239 self.seed2 +=158 self.dphi[k:kmax] = self.dphi_output[0:(kmax-k)] if self.continuous_phase: if i==0: self.spectrum_to_phase_noise(freq, spectrum) self.seed1 +=239 self.seed2 +=158 self.dphi2[:self.corr/4] = self.dphi_output[:self.corr/4] self.spectrum_to_phase_noise(freq, spectrum) self.seed1 +=239 self.seed2 +=158 self.dphi2[(k+self.corr/4):(kmax+self.corr/4)] = self.dphi_output[0:(kmax-k)] if self.folder_plots != None: fig_folder(self.folder_plots) plot_noise_spectrum(freq, spectrum, sampling=1, figno=i, dirname = self.folder_plots) plot_phase_noise(self.t[0:(kmax-k)], self.dphi_output[0:(kmax-k)], sampling=1, figno=i, dirname = self.folder_plots) rms_noise = np.std(self.dphi_output) if self.print_option: print("RF noise for time step %.4e s (iter %d) has r.m.s. phase %.4e rad (%.3e deg)" \ %(self.t[1], i, rms_noise, rms_noise*180/np.pi)) if self.continuous_phase: psi = np.arange(0, self.n_turns+1)*2*np.pi/self.corr self.dphi = self.dphi*np.sin(psi[:self.n_turns+1]) + self.dphi2[:(self.n_turns+1)]*np.cos(psi[:self.n_turns+1]) if self.initial_final_turns[0]>0 or self.initial_final_turns[1]<self.total_n_turns+1: self.dphi = np.concatenate((np.zeros(self.initial_final_turns[0]), self.dphi, np.zeros(1+self.total_n_turns-self.initial_final_turns[1])))
def process(self): ''' *Reprocess the impedance contributions. To be run when slices changes* ''' if (self.wake_length_input != None and self.frequency_resolution_input == None): # Number of points of the induced voltage array self.n_induced_voltage = int( np.ceil(self.wake_length_input / self.slices.bin_size)) if self.n_induced_voltage < self.slices.n_slices: raise RuntimeError('Error: too short wake length. ' + 'Increase it above {0:1.2e} s.'.format( self.slices.n_slices * self.slices.bin_size)) #: *Wake length in s, rounded up to the next multiple of bin size* self.wake_length = self.n_induced_voltage * self.slices.bin_size self.frequency_resolution = 1 / self.wake_length elif (self.frequency_resolution_input != None and self.wake_length_input == None): self.n_induced_voltage = int( np.ceil( 1 / (self.slices.bin_size * self.frequency_resolution_input))) if self.n_induced_voltage < self.slices.n_slices: raise RuntimeError( 'Error: too large frequency_resolution. ' + 'Reduce it below {0:1.2e} Hz.'.format( 1 / (self.slices.cut_right - self.slices.cut_left))) self.wake_length = self.n_induced_voltage * self.slices.bin_size #: *Frequency resolution in Hz* self.frequency_resolution = 1 / self.wake_length elif (self.wake_length_input == None and self.frequency_resolution_input == None): # By default the wake_length is the slicing frame length self.wake_length = (self.slices.cut_right - self.slices.cut_left) self.frequency_resolution = 1 / self.wake_length self.n_induced_voltage = self.slices.n_slices else: raise RuntimeError('Error: only one of wake_length or ' + 'frequency_resolution can be specified.') if self.multi_turn_wake: # Number of points of the memory array for multi-turn wake self.n_mtw_memory = self.n_induced_voltage self.front_wake_buffer = 0 if self.mtw_mode == 'freq': # In frequency domain, an extra buffer for a revolution turn is # needed due to the circular time shift in frequency domain self.buffer_size = \ np.ceil(np.max(self.RFParams.t_rev) / self.slices.bin_size) # Extending the buffer to reduce the effect of the front wake self.buffer_size += \ np.ceil(np.max(self.buffer_extra) / self.slices.bin_size) self.n_mtw_memory += int(self.buffer_size) # Using next regular for FFTs speedup self.n_mtw_fft = next_regular(self.n_mtw_memory) # Frequency and omega arrays self.freq_mtw = \ rfftfreq(self.n_mtw_fft, d=self.slices.bin_size) self.omegaj_mtw = 2.0j * np.pi * self.freq_mtw # Selecting time-shift method self.shift_trev = self.shift_trev_freq else: # Selecting time-shift method self.shift_trev = self.shift_trev_time # Time array self.time_mtw = np.arange(0, self.wake_length, self.wake_length / self.n_mtw_memory) # Array to add and shift in time the multi-turn wake over the turns self.mtw_memory = np.zeros(self.n_mtw_memory) # Select induced voltage generation method to be used self.induced_voltage_generation = self.induced_voltage_mtw else: self.induced_voltage_generation = self.induced_voltage_1turn
def __init__(self, Slices, impedance_source_list, frequency_resolution_input=None, freq_res_option='round', n_turns_memory=0, recalculation_impedance=False, index_save_individual_voltage=-1, n_windows_before=0, main_harmonic_number=1, overwrite_n_fft_sampling=None): #: *Copy of the Slices object in order to access the profile info.* self.slices = Slices #: *Impedance sources inputed as a list (eg: list of BBResonators objects)* self.impedance_source_list = impedance_source_list #: *Input frequency resolution in [Hz], the beam profile sampling for the spectrum #: will be adapted according to the freq_res_option.* self.frequency_resolution_input = frequency_resolution_input #: *Number of turns to be considered as memory for induced voltage calculation.* self.n_turns_memory = n_turns_memory #: *Length of one slice.* time_resolution = self.slices.bin_centers[1] - self.slices.bin_centers[ 0] self.recalculation_impedance = recalculation_impedance self.index_save_individual_voltage = index_save_individual_voltage if n_turns_memory == 0: if self.frequency_resolution_input == None: self.n_fft_sampling = self.slices.n_slices else: self.freq_res_option = freq_res_option if self.freq_res_option is 'round': self.n_fft_sampling = next_regular( int( np.round(1 / (self.frequency_resolution_input * time_resolution)))) elif self.freq_res_option is 'ceil': self.n_fft_sampling = next_regular( int( np.ceil(1 / (self.frequency_resolution_input * time_resolution)))) elif self.freq_res_option is 'floor': self.n_fft_sampling = next_regular( int( np.floor(1 / (self.frequency_resolution_input * time_resolution)))) else: raise RuntimeError( 'The input freq_res_option is not recognized') if self.n_fft_sampling < self.slices.n_slices: print( 'The input frequency resolution step is too big, and the whole \ bunch is not sliced... The number of sampling points for the \ FFT is corrected in order to sample the whole bunch (and \ you might consider changing the input in order to have \ a finer resolution).') self.n_fft_sampling = next_regular(self.slices.n_slices) if overwrite_n_fft_sampling != None: self.n_fft_sampling = overwrite_n_fft_sampling #: *Real frequency resolution in [Hz], according to the obtained n_fft_sampling.* self.frequency_resolution = 1 / (self.n_fft_sampling * time_resolution) #: *Frequency array of the impedance in [Hz]* self.frequency_array = rfftfreq( self.n_fft_sampling, self.slices.bin_centers[1] - self.slices.bin_centers[0]) #: *Total impedance array of all sources in* [:math:`\Omega`] self.total_impedance = 0 self.sum_impedances(self.frequency_array) #: *Induced voltage from the sum of the wake sources in [V]* self.induced_voltage = 0 else: self.n_windows_before = n_windows_before self.n_turns_memory = n_turns_memory self.main_harmonic_number = main_harmonic_number self.len_array_memory = ( self.n_turns_memory + 1 + n_windows_before) * self.slices.n_slices * main_harmonic_number self.n_fft_sampling = next_regular(self.len_array_memory) self.frequency_array_memory = rfftfreq(self.n_fft_sampling, time_resolution) self.total_impedance_memory = np.zeros( self.frequency_array_memory.shape) + 0j for imped_object in self.impedance_source_list: imped_object.imped_calc(self.frequency_array_memory) self.total_impedance_memory += imped_object.impedance