def get_spectrogram(self, t=None, iteration=None, pol=None, theta=0, slicing_dir='y', plot=False, **kw): """ Calculates the spectrogram of a laserpulse, by the FROG method. Mathematically: $$ s(\omega, \tau) = | \int_{-\infty}^{\infty} E(t) |E(t-\tau)|^2 \exp( -i\omega t) dt |^2 $$ See Trebino, R: Frequency Resolved Optical Gating: The measurements of Ultrashort Laser Pulses: year 2000: formula 5.2 The time is centered around the laser pulse. Parameters ---------- t : float (in seconds), optional Time at which to obtain the data (if this does not correspond to an available file, the last file before `t` will be used) Either `t` or `iteration` should be given by the user. iteration : int The iteration at which to obtain the data Either `t` or `iteration` should be given by the user. pol : string Polarization of the laser field. Options are 'x', 'y' plot: bool, optional Whether to plot the spectrogram **kw : dict, otional Additional options to be passed to matplotlib's `imshow` method Returns ------- - A 2d array with spectrogram - info : a FieldMetaInformation object (see the corresponding docstring) """ # Get the field envelope env, _ = self.get_laser_envelope(t=t, iteration=iteration, pol=pol) # Get the field E, info = self.get_field(t=t, iteration=iteration, field='E', coord=pol, theta=theta, slicing_dir=slicing_dir) # Get central slice E = E[int(E.shape[0] / 2), :] Nz = len(E) # Get time domain of the data tmin = info.zmin / const.c tmax = info.zmax / const.c T = tmax - tmin dt = T / Nz # Normalize the Envelope env /= np.sqrt(np.trapz(env**2, dx=dt)) # Allocate array for the gating function and the spectrogran E_shift = np.zeros_like(E) spectrogram = np.zeros((2 * Nz, Nz)) # Loop over the time variable of the spectrogram for i in range(Nz * 2): itau = i % Nz # Shift the E field and fill the rest with zeros if i < Nz: E_shift[:itau] = env[Nz - itau:Nz] E_shift[itau:] = 0 else: E_shift[itau:] = env[:Nz - itau] E_shift[:itau] = 0 EE = E * E_shift**2 fft_EE = np.fft.fft(EE) spectrogram[i, :] = np.abs(fft_EE)**2 # Rotate and flip array to have input form of imshow spectrogram = np.flipud(np.rot90(spectrogram[:, int(Nz / 2):])) # Find the time at which the wigner transform is the highest maxi, maxj = np.unravel_index(spectrogram.argmax(), spectrogram.shape) tmin = -(T - T / spectrogram.shape[1] * maxj) info = FieldMetaInformation({ 0: 'omega', 1: 't' }, spectrogram.shape, grid_spacing=(2 * np.pi / T, dt / 2.), grid_unitSI=1, global_offset=(0, tmin), position=(0, 0)) # Plot the result if needed if plot: check_matplotlib() iteration = self.iterations[self._current_i] time_fs = 1.e15 * self.t[self._current_i] plt.imshow(spectrogram, extent=info.imshow_extent, aspect='auto', **kw) plt.title("Spectrogram at %.1f fs (iteration %d)" % (time_fs, iteration), fontsize=self.plotter.fontsize) plt.xlabel('$t \;(s)$', fontsize=self.plotter.fontsize) plt.ylabel('$\omega \;(rad.s^{-1})$', fontsize=self.plotter.fontsize) return (spectrogram, info)
def get_spectrum(self, t=None, iteration=None, pol=None, m='all', plot=False, **kw): """ Return the spectrum of the laser (Absolute value of the Fourier transform of the fields.) Parameters ---------- t : float (in seconds), optional Time at which to obtain the data (if this does not correspond to an available file, the last file before `t` will be used) Either `t` or `iteration` should be given by the user. iteration : int The iteration at which to obtain the data Either `t` or `iteration` should be given by the user. pol : string Polarization of the field. Options are 'x', 'y' m : int or str, optional Only used for thetaMode geometry Either 'all' (for the sum of all the modes) or an integer (for the selection of a particular mode) plot: bool, optional Whether to plot the data **kw : dict, otional Additional options to be passed to matplotlib's `plot` method Returns ------- A tuple with: - The 1D spectrum on axis - A FieldMetaInformation object """ # Check if polarization has been entered if pol not in ['x', 'y']: raise ValueError('The `pol` argument is missing or erroneous.') if pol == 'x': slicing_dir = 'y' theta = 0 else: slicing_dir = 'x' theta = np.pi / 2. # Get field data field, info = self.get_field(t=t, iteration=iteration, field='E', coord=pol, theta=theta, m=m, slicing_dir=slicing_dir) # Get central field lineout field1d = field[int(field.shape[0] / 2), :] # FFT of 1d data dt = (info.z[1] - info.z[0]) / const.c # Integration step for the FFT fft_field = np.fft.fft(field1d) * dt # Take half of the data (positive frequencies only) spectrum = abs(fft_field[:int(len(fft_field) / 2)]) # Create a FieldMetaInformation object T = (info.zmax - info.zmin) / const.c spect_info = FieldMetaInformation({0: 'omega'}, spectrum.shape, grid_spacing=(2 * np.pi / T, ), grid_unitSI=1, global_offset=(0, ), position=(0, )) # Plot the field if required if plot: check_matplotlib() iteration = self.iterations[self._current_i] time_fs = 1.e15 * self.t[self._current_i] plt.plot(spect_info.omega, spectrum, **kw) plt.xlabel('$\omega \; (rad.s^{-1})$', fontsize=self.plotter.fontsize) plt.ylabel('Spectrum', fontsize=self.plotter.fontsize) plt.title("Spectrum at %.1f fs (iteration %d)" % (time_fs, iteration), fontsize=self.plotter.fontsize) return (spectrum, spect_info)
def get_current(self, t=None, iteration=None, species=None, select=None, bins=100, plot=False, **kw): """ Calculate the electric current along the z-axis for selected particles. Parameters ---------- t : float (in seconds), optional Time at which to obtain the data (if this does not correspond to an available file, the last file before `t` will be used) Either `t` or `iteration` should be given by the user iteration : int The iteration at which to obtain the data Either `t` or `iteration` should be given by the user species : string Particle species to use for calculations select : dict, optional Either None or a dictionary of rules to select the particles, of the form 'x' : [-4., 10.] (Particles having x between -4 and 10 microns) 'z' : [0, 100] (Particles having x between 0 and 100 microns) bins : int, optional Number of bins along the z-axis in which to calculate the current plot : bool, optional Whether to plot the requested quantity **kw : dict, otional Additional options to be passed to matplotlib's `plot` method Returns ------- A tuple of arrays containig - The current in each bin in Ampere - A FieldMetaInformation object (see object's docstring for more details) """ # Get particle data z, uz, uy, ux, w, q = self.get_particle( var_list=['z', 'uz', 'uy', 'ux', 'w', 'charge'], t=t, iteration=iteration, species=species, select=select) # Calculate Lorentz factor for all particles gamma = np.sqrt(1 + ux**2 + uy**2 + uz**2) # Calculate particle velocities vz = uz / gamma * const.c # Length to be seperated in bins len_z = np.max(z) - np.min(z) vzq_sum, _ = np.histogram(z, bins=bins, weights=(vz * w * q)) # Calculete the current in each bin current = np.abs(vzq_sum * bins / (len_z * 1.e-6)) # Info object with central position of the bins info = FieldMetaInformation({0: 'z'}, current.shape, grid_spacing=(len_z / bins, ), grid_unitSI=1, global_offset=(np.min(z) + len_z / bins / 2, ), position=(0, )) # Plot the result if needed if plot: check_matplotlib() iteration = self.iterations[self._current_i] time_fs = 1.e15 * self.t[self._current_i] plt.plot(info.z, current, **kw) plt.title("Current at %.1f fs (iteration %d)" % (time_fs, iteration), fontsize=self.plotter.fontsize) plt.xlabel('$z \;(\mu m)$', fontsize=self.plotter.fontsize) plt.ylabel('$I \;(A)$', fontsize=self.plotter.fontsize) # Return the current and bin centers return (current, info)
def get_laser_envelope(self, t=None, iteration=None, pol=None, m='all', index='center', theta=0, slicing_dir='y', plot=False, **kw): """ Calculate a laser field by filtering out high frequencies. Can either return the envelope slice-wise or a full 2D envelope. Parameters ---------- t : float (in seconds), optional Time at which to obtain the data (if this does not correspond to an available file, the last file before `t` will be used) Either `t` or `iteration` should be given by the user. iteration : int The iteration at which to obtain the data Either `t` or `iteration` should be given by the user. pol : string Polarization of the field. Options are 'x', 'y' m : int or str, optional Only used for thetaMode geometry Either 'all' (for the sum of all the modes) or an integer (for the selection of a particular mode) index : int or str, optional Transversal index of the slice from which to calculate the envelope Default is 'center', using the center slice. Use 'all' to calculate a full 2D envelope theta : float, optional Only used for thetaMode geometry The angle of the plane of observation, with respect to the x axis slicing_dir : str, optional Only used for 3dcartesian geometry The direction along which to slice the data Either 'x', 'y' plot : bool, optional Whether to plot the requested quantity **kw : dict, otional Additional options to be passed to matplotlib's `plot`(1D) or `imshow` (2D) method Returns ------- A tuple with: - Envelope data (1D or 2D array) - A FieldMetaInformation object """ # Check if polarization has been entered if pol is None: raise ValueError('The `pol` argument is missing or erroneous.') # Get field data field = self.get_field(t=t, iteration=iteration, field='E', coord=pol, theta=theta, m=m, slicing_dir=slicing_dir) info = field[1] if index == 'all': # Filter the full 2D array e_complx = hilbert(field[0], axis=1) elif index == 'center': # Filter the central slice (1D array) field_slice = field[0][int(field[0].shape[0] / 2), :] e_complx = hilbert(field_slice) else: # Filter the requested slice (2D array) field_slice = field[0][index, :] e_complx = hilbert(field_slice) envelope = np.abs(e_complx) # Restrict the metainformation to 1d if needed if index != 'all': info.restrict_to_1Daxis(info.axes[1]) # Plot the result if needed if plot: check_matplotlib() iteration = self.iterations[self._current_i] time_fs = 1.e15 * self.t[self._current_i] if index != 'all': plt.plot(1.e6 * info.z, envelope, **kw) plt.ylabel('$E_%s \;(V/m)$' % pol, fontsize=self.plotter.fontsize) else: plt.imshow(envelope, extent=1.e6 * info.imshow_extent, aspect='auto', **kw) plt.colorbar() plt.ylabel('$%s \;(\mu m)$' % pol, fontsize=self.plotter.fontsize) plt.title("Laser envelope at %.1f fs (iteration %d)" % (time_fs, iteration), fontsize=self.plotter.fontsize) plt.xlabel('$z \;(\mu m)$', fontsize=self.plotter.fontsize) # Return the result return (envelope, info)
def get_sigma_gamma_slice(self, dz, t=None, iteration=None, species=None, select=None, plot=False, **kw): """ Calculate the standard deviation of gamma for particles in z-slices of width dz Parameters ---------- dz : float (in micrometers) Width of slices in which to calculate sigma gamma t : float (in seconds), optional Time at which to obtain the data (if this does not correspond to an available file, the last file before `t` will be used) Either `t` or `iteration` should be given by the user. iteration : int The iteration at which to obtain the data Either `t` or `iteration` should be given by the user. species : string Particle species to use for calculations select : dict, optional Either None or a dictionary of rules to select the particles, of the form 'x' : [-4., 10.] (Particles having x between -4 and 10 microns) 'z' : [0, 100] (Particles having x between 0 and 100 microns) plot : bool, optional Whether to plot the requested quantity **kw : dict, otional Additional options to be passed to matplotlib's `plot` method Returns ------- A tuple of arrays: - Sigma gamma in each slice - Central z position of each slice """ z, uz, ux, uy, w = self.get_particle( t=t, species=species, select=select, var_list=['z', 'uz', 'ux', 'uy', 'w'], iteration=iteration) # Calculate gamma of each particle gamma = np.sqrt(1 + (uz**2 + ux**2 + uy**2)) z0 = min(z) zend = max(z) N = int((zend - z0) / dz) spreads = np.zeros(N + 1) z_pos = np.linspace(z0, zend, N + 1) zi = z0 + dz / 2. i = 0 # Iterate over slices and calculate sigma gamma while zi < zend: z_filter = (z > zi - dz / 2.) & (z < zi + dz / 2.) spreads[i] = w_std(gamma[z_filter], w[z_filter]) zi += dz i += 1 # Plot the result if needed if plot: check_matplotlib() iteration = self.iterations[self._current_i] time_fs = 1.e15 * self.t[self._current_i] plt.plot(z_pos, spreads, **kw) plt.title("Slice energy spread at %.1f fs (iteration %d)" % (time_fs, iteration), fontsize=self.plotter.fontsize) plt.xlabel('$z \;(\mu m)$', fontsize=self.plotter.fontsize) plt.ylabel('$\sigma_\gamma (\Delta_z=%s\mu m)$' % dz, fontsize=self.plotter.fontsize) return (spreads, z_pos)