def fermi_edge(E, E_f, dE, c, d): """ The typical shape of a fermi edge for constant DOS. Suitable as a fit function, therefore it takes only floats, no quantities. :param E: The x-Axis of the data consists of energies in eV :param E_f: The Fermi energy in eV. :param dE: Energy Resolution. The broadening of the Fermi edge on top of the thermal broadening, which is introduced by all experimental errors, in eV. :param c: The height of the Fermi edge, in whichever units the data is given, e.g. "counts". :param d: Offset of the lower level of the fermi edge, e.g. "dark counts". :return: The value of the Fermi distribution at the energy E. """ E = u.to_ureg(E, 'eV').magnitude E_f = u.to_ureg(E_f, 'eV').magnitude dE = u.to_ureg(dE, 'eV').magnitude c = u.to_ureg(c) d = u.to_ureg(d) return 0.5 * (1 + scipy.special.erf((E_f - E) / (np.sqrt(( (1.7 * kBT_in_eV.magnitude)**2) + dE**2) * np.sqrt(2)))) * c + d
def __init__(self, name, epsilon=1., n=1.): """ The constructor. Can be initialized with an epsilon or an n value. If you give neither, you'll end up with Vacuum. All values can be complex. :param name: The name of the material. E.g. ITO :param epsilon: The relative dielectric constant of the medium. :param n: The refraction index, :return: nothing """ self.name = name epsilon = u.to_ureg(epsilon, 'dimensionless') n = u.to_ureg(n, 'dimensionless') if epsilon != 1.: self.dielconstant = epsilon self.refracindex = numpy.sqrt(epsilon) elif n != 1.: self.dielconstant = n**2 self.refracindex = n else: self.dielconstant = 1.0 * u.ureg('dimensionless') self.refracindex = 1.0 * u.ureg('dimensionless')
def fit_fermi_edge(energies, intensities, guess=None): """ This function fits a fermi edge to data. Uses numpy.optimize.curve_fit under the hood. :param energies: A quantity or array of energies. If no quantity, electronvolts are assumed. :param intensities: Quantity or array: The corresponding intensity values to powers. :param guess: optional: A tuple of start parameters (E_f, dE, c, d) as defined in fermi_edge method. :return: The coefficients and uncertainties of the fitted fermi edge E_f, dE, c, d, as defined in fermi_edge method. """ if u.is_quantity(energies): assert u.same_dimension(energies, "eV") energies = u.to_ureg(energies) else: energies = u.to_ureg(energies, 'eV') intensities = u.to_ureg(intensities) if guess is None: guess = (29.6, 0.1, 1.0, 0.01) # Just typical values else: # to assure the guess is represented in the correct units: energyunit = energies.units countsunit = intensities.units unitslist = [energyunit, energyunit, countsunit, countsunit] guesslist = [] for guesselement, guessunit in zip(guess, unitslist): guesslist.append(u.to_ureg(guesselement, guessunit).magnitude) guess = tuple(guess) return curve_fit(fermi_edge, energies.magnitude, intensities.magnitude, guess)
def __init__(self, sampling_delta, lowcut=None, highcut=None, order=5): """ The initializer of the Butterfilter. All "time and frequency" parameters can and should be given as quantities. If raw numerical data is given, it still works, but all values are assumed as dimensionless. :param sampling_delta: The spacing of the axis along which to filter the data. :type sampling_delta: `pint.Quantity` :param lowcut: The low cut, below which the frequency response is faded out. :type lowcut: `pint.Quantity` :param highcut: The high cut, below which the frequency response is faded out. :type highcut: `pint.Quantity` :param order: The order of the filter, see scipy docs. If the frequency response looks bad, try increasing this. :type order: int """ # Parse Arguments: sampling_delta = u.to_ureg(sampling_delta) if (lowcut is not None) and (highcut is not None): highcut = u.to_ureg(highcut) lowcut = u.to_ureg(lowcut) assert lowcut.units == highcut.units, "Low and Highcut must be given in same units." self.freq_unit = lowcut.units elif highcut is not None: highcut = u.to_ureg(highcut) self.freq_unit = highcut.units elif lowcut is not None: lowcut = u.to_ureg(lowcut) self.freq_unit = lowcut.units else: raise ValueError("Cannot define filter without high or lowcut.") nyq = (1 / (2 * sampling_delta)).to(self.freq_unit) self.highcut, self.lowcut = highcut, lowcut # Define filters: if (lowcut is not None) and (highcut is not None): # bandpass low = lowcut / nyq high = highcut / nyq # noinspection PyTupleAssignmentBalance,PyTupleAssignmentBalance b, a = signal.butter(order, [low, high], btype='band') elif lowcut is not None: # highpass low = lowcut / nyq # noinspection PyTupleAssignmentBalance b, a = signal.butter(order, low, btype='high') elif highcut is not None: # lowpass high = highcut / nyq # noinspection PyTupleAssignmentBalance b, a = signal.butter(order, high, btype='low') else: raise ValueError("Cannot define filter without high or lowcut.") self.b, self.a = b, a self.sampling_delta = sampling_delta self.order = order
def Kspp(omega, metal=default_metal, diel=default_diel, unit='1/um'): omega = u.to_ureg(omega, 'rad/s', convert_quantities=False) assert isinstance(metal, snomtools.calcs.materials.metals.Metal ), "ERROR: No metal given to Kspp" assert isinstance(diel, snomtools.calcs.materials.dielectrics.Dielectric ), "ERROR: No dielectric given to Kspp" wurzel = numpy.sqrt((metal.epsilon(omega) * diel.epsilon(omega)) / (metal.epsilon(omega) + diel.epsilon(omega))) k = omega / const.c * numpy.real(wurzel) k = u.to_ureg(k, unit) return k
def __init__(self, plasma_freq, damping_freq, central_freq): """ The constructor. All frequencies should be given as angular frequencies (rad/s) :param plasma_freq: the (quasi-)plasma frequency of the bound electrons :param damping_freq: the damping frequency of the bound electrons :param central_freq: the central frequency of the interband transition """ self.omega_p = units.to_ureg(plasma_freq, 'rad/s') self.gamma = units.to_ureg(damping_freq, 'rad/s') self.omega_0 = units.to_ureg(central_freq, 'rad/s')
def filteredslice(self, s, component, df=0): """ Filtered slice of the instance input data. :param s: A slice addressing a region of the data to return. Can be conveniently made with `numpy.s_[]`. :type s: slice :param component: The frequency component to return. :type component: int :param df: The DataArray in the given DataSet to filter, can be specified if multiple are present. Given as a valid identifier (index or label). :type df: int or str :return: The filtered data. :rtype: numpy.ndarray """ s = full_slice(s, self.indata.dimensions) if s[self.filter_axis_id] != np.s_[:]: warnings.warn( "Frequency filtering a slice that is not full along the filter axis might return bad results" ) df_in = self.indata.get_datafield(df) timedata = df_in.data[s] filtered_data = self.butters[component].filtered( timedata, axis=self.filter_axis_id) return u.to_ureg(filtered_data, df_in.get_unit())
def guess_parameters(energies, intensities): # ToDo: This just takes the 10%-edges of the value range, this could be improved with some cool statistics. i_min = min(intensities) i_max = max(intensities) i_range = i_max - i_min # Take the 10%-values from the edge as low and high: low_level = i_min + 0.1 * i_range high_level = i_max - 0.1 * i_range # As a guess for the edge position, # take the mean index of the 5% closest values to the middle between high and low: n_mean_index = min(int(round(len(energies) * 0.05)), 1) middle_index = int( np.round( np.mean( np.argsort( np.abs(intensities - (high_level - low_level) / 2))[:n_mean_index]))) guess = (energies[middle_index], u.to_ureg(0.1, 'eV'), high_level - low_level, low_level) if verbose: print("Guessing start parameters for Fermi Fit: {0}", guess) return guess
def fftslice(self, s, df=0): """ Get the fourier-transformed data of a specific slice of a DataArray of the instance input. :param s: The slice, addressing a selection of the instance input data. Can be conveniently made with `numpy.s_[]`. :type s: slice :param df: An identifier (index or label) of the DataArray to transform. :type df: int or str :return: The fourier-transformed data for the selection. :rtype: pint.Quantity """ s = full_slice(s, self.indata.dimensions) if s[self.axis_to_transform_id] != np.s_[:]: warnings.warn( "FFT of a slice that is not full along the FFT axis might return bad results" ) df_in = self.indata.get_datafield(df) timedata = df_in.data[s].magnitude freqdata = fftpack.fftshift(fftpack.fft( timedata, axis=self.axis_to_transform_id), axes=self.axis_to_transform_id) return u.to_ureg(freqdata, df_in.get_unit())
def show_state_parabola(dispersion_data, guess_origin=None, guess_mass=None, guess_energyoffset=None, k_axisid='k_y'): """ Calculates a free electron like parabola for a intermediate state with given parameters. Returns a tuple of x (k||) and y (energy) values, that can be used for plotting. Useful for finding out the specific band mass. :param dispersion_data: 2D-DataSet with an energy and a k-space dimension. :type dispersion_data: ds.DataSet :param guess_origin: The origin k value of the parable, given in ``1/angstrom``. :type guess_origin: pint.Quantity **or** float, optional :param guess_mass: The bandmass of the intermediate state you are interested. Typically given in units of m_e (electronmass). :type guess_mass: pint.Quantity **or** float, optional :param guess_energyoffset: The origin of the parable on the energy axis. Typically, something like the drift voltage in PEEM. :type guess_energyoffset: pint.Quantity **or** float, optional :param k_axisid: The name (label) of the k-axis of the data. Default: ``y`` :type k_axisid: str :return: The adjusted free electron like parabola as a tuple of x and y values (k, parab_data) for plotting and the corresponding bandmass, used in the plot. Bandmass is typically given in units of ``m_e`` (electronmass). """ # Define parabola and parameters for fit if guess_energyoffset is None: energy_offset = u.to_ureg(30, "eV") else: energy_offset = u.to_ureg(guess_energyoffset, "eV") k = dispersion_data.get_axis(k_axisid).data if guess_origin is None: origin = k.mean() else: origin = u.to_ureg(guess_origin, "1/angstrom") if guess_mass is None: bandmass = u.to_ureg(1, "m_e") else: bandmass = u.to_ureg(guess_mass, "m_e") # Calculate a parabola with set electronmass parab_data = bandDispersionRelation(k, bandmass, origin, energy_offset) return (k, parab_data), bandmass
def kscale_axes(data, yscalefactor, xscalefactor, yzero=None, xzero=None, y_axisid='y', x_axisid='x'): """ Scales the x- and y-axis of a given set of dldpixels from an nD-Dataset to k-space, depending on a before determined scalefactor. :param data: nD-DataSet with y-pixel, x-pixel, and possibly other dimensions. :type data: ds.DataSet :param yscalefactor: The scalefactor translating unscaled ky-axis units to k-space. Typically given in ``angstrom**-1 per pixel``. :type yscalefactor: pint.Quantity :param xscalefactor: The scalefactor translating unscaled kx-axis units to k-space. Typically given in ``angstrom**-1 per pixel``. :type xscalefactor: pint.Quantity :param yzero: The offset of the Gamma-point in k_y direction, given in unscaled units (typically pixels). :type yzero: pint.Quantity, optional :param xzero: The offset of the Gamma-point in k_x direction, given in unscaled units (typically pixels). :type xzero: pint.Quantity, optional :param y_axisid: The name (label) of the x-axis of the data. Default: ``y`` :type y_axisid: str :param x_axisid: The name (label) of the y-axis of the data. Default: ``x`` :type x_axisid: str :return: The k-scaled 4D-Dataset. :rtype: ds.DataSet """ if yzero is None: yzero = data.get_axis(y_axisid).mean() else: yzero = u.to_ureg(yzero, data.get_axis(y_axisid).units) data.get_axis(y_axisid).scale_linear(yscalefactor, yscalefactor * (-yzero), 'angstrom**-1', label='k_y', plotlabel="k_y / Angstroem^-1") if xzero is None: xzero = data.get_axis(x_axisid).mean() else: xzero = u.to_ureg(xzero, data.get_axis(x_axisid).units) data.get_axis(x_axisid).scale_linear(xscalefactor, xscalefactor * (-xzero), 'angstrom**-1', label='k_x', plotlabel="k_x / Angstroem^-1")
def from_xy(cls, energies, intensities, guess): if u.is_quantity(energies): assert u.same_dimension(energies, "eV") energies = u.to_ureg(energies) else: energies = u.to_ureg(energies, 'eV') intensities = u.to_ureg(intensities) energyunit = str(energies.units) countsunit = str(intensities.units) f = cls() f.coeffs, f.accuracy = cls.fit_fermi_edge(energies, intensities, guess) f.E_f_unit = energyunit f.dE_unit = energyunit f.c_unit = countsunit f.d_unit = countsunit return f
def powerlaw_folder_peem_dld(folderpath, pattern="mW", powerunit=None, powerunitlabel=None): """ :param folderpath: The (relative or absolute) path of the folders containing the powerlaw measurement series. :param pattern: string: A pattern the powers in the filenames are named with. For example in the default case "mW", the filename containing '50,2mW' or '50.2mW' or '50.2 mW' would accord to a power of 50.2 milliwatts. The power units for the axis quantities are also cast from this pattern if not explicitly given with powerunit. :param powerunit: A valid unit string that will be cast as the unit for the power axis values. If not given, the pattern parameter will be cast as unit. :param powerunitlabel: string: Will be used as the unit for the power axis plotlabel. Can be for example a LaTeX siunitx command. If not given, the powerunit parameter will be used. :return: The dataset containing the images stacked along a power axis. """ if powerunit is None: powerunit = pattern if powerunitlabel is None: powerunitlabel = powerunit pat = re.compile('(\d*[,|.]?\d+)\s?' + pattern) # Translate input path to absolute path: folderpath = os.path.abspath(folderpath) # Inspect the given folder for the tif files of the powerlaw: powerfiles = {} for filename in filter(is_tif, os.listdir(folderpath)): found = re.search(pat, filename) if found: power = float(found.group(1).replace(',', '.')) powerfiles[power] = filename axlist = [] datastack = [] for power in iter(sorted(powerfiles.keys())): datastack.append( peem_dld_read_terra(os.path.join(folderpath, powerfiles[power]))) axlist.append(power) powers = u.to_ureg(axlist, powerunit) pl = 'Power / ' + powerunitlabel # Plot label for power axis. poweraxis = snomtools.data.datasets.Axis(powers, label='power', plotlabel=pl) return snomtools.data.datasets.stack_DataSets(datastack, poweraxis, axis=-1, label="Powerlaw " + folderpath)
def energy_scale_linear(channel_axis, a, b): """ Energy scaling calculated for the time channel axis of a DataSet containing DLD PEEM data, Linear version. :param channel_axis: The Axis instance containing the channel numbers as data. :param a: The fit parameter a as in the energy calibration files from Terra (.kalfit.txt). :param b: The fit parameter b as in the energy calibration files from Terra (.kalfit.txt). :return: A new Axis instance with energy scaling, that can replace the channel axis in the DataSet. """ a = u.to_ureg(a, 'eV') b = u.to_ureg(b, 'eV') channels = channel_axis.get_data() energy_points = a * channels + b return snomtools.data.datasets.Axis( energy_points, label="energy", plotlabel="Electron Energy / \\si{\electronvolt}")
def energy_scale_quadratic(channel_axis, C, t_0): """ Energy scaling calculated for the time channel axis of a DataSet containing DLD PEEM data, Quadratic version. :param channel_axis: The Axis instance containing the channel numbers as data. :param C: The fit parameter C as in the energy calibration files from Terra (.kalfit.txt). :param t_0: The fit parameter C as in the energy calibration files from Terra (.kalfit.txt). :return: A new Axis instance with energy scaling, that can replace the channel axis in the DataSet. """ t_0 = u.to_ureg(t_0, '') c_square = u.to_ureg(C**2, 'eV') channels = channel_axis.get_data() energy_points = c_square / (channels - t_0)**2 return snomtools.data.datasets.Axis( energy_points, label="energy", plotlabel="Electron Energy / \\si{\electronvolt}")
def Vspp(omega, metal=default_metal, diel=default_diel, unit='m/s'): omega = u.to_ureg(omega, 'rad/s', convert_quantities=False) assert isinstance(metal, (snomtools.calcs.materials.metals.Metal, snomtools.calcs.materials.literature.Literature_Material)), \ "ERROR: No metal given to Vspp" assert isinstance(diel, snomtools.calcs.materials.dielectrics.Dielectric ), "ERROR: No dielectric given to Vspp" wurzel = numpy.sqrt((metal.epsilon(omega) * diel.epsilon(omega)) / (metal.epsilon(omega) + diel.epsilon(omega))) return const.c / wurzel
def from_coeffs(cls, coeffs, energyunit='eV', countsunit=None): # Parse input and handle units: E_f, d_E, c, d = coeffs E_f = u.to_ureg(E_f, energyunit) d_E = u.to_ureg(d_E, energyunit) c = u.to_ureg(c, countsunit) d = u.to_ureg(d, countsunit) # Prepare variables: energyunit = str(E_f.units) countsunit = str(c.units) coeffs = (E_f.magnitude, d_E.magnitude, c, d) # Make instance: f = cls() f.coeffs = coeffs f.E_f_unit = energyunit f.dE_unit = energyunit f.c_unit = countsunit f.d_unit = countsunit return f
def n(self, omega=0.): """ The complex refraction index of the medium. :param omega: just for compatibility with the metal function, and for determining the output type. :return: the refraction index. will be the same type as omega """ omega = u.to_ureg(omega, 'THz') # Just to check if input is valid. omega = omega.magnitude # For addition with dimensionless epsilon myn = omega * 0 + self.refracindex # to conserve format of input array return myn
def epsilon(self, omega=0): """ The dielectric constant of the medium. :param omega: just for compatibility with the metal function, and for determining the output type. :return: the dielectric function. will be the same type as omega """ omega = u.to_ureg(omega, 'THz') # Just to check if input is valid. omega = omega.magnitude # For addition with dimensionless epsilon eps = omega * 0 + self.dielconstant # to conserve format of input array return eps
def fit_xy_linear(xdata, ydata): """ A simple linear fit to data given as x and y values. Fits y = m*x + c and returns c tuple of (m, c), where m and c are quantities according to the physical dimensions of data. Numerical data is assumed as dimensionless. :param xdata: DataArray or Quantity or numpy array: The x values. :param ydata: DataArray or Quantity or numpy array: The y values. :return:tuple: (m, c) """ if isinstance(xdata, snomtools.data.datasets.DataArray): xdata = xdata.get_data() else: xdata = u.to_ureg(xdata) if isinstance(ydata, snomtools.data.datasets.DataArray): ydata = ydata.get_data() else: ydata = u.to_ureg(ydata) xdata = snomtools.data.tools.assure_1D(xdata) ydata = snomtools.data.tools.assure_1D(ydata) m, c = numpy.polyfit(xdata.magnitude, ydata.magnitude, deg=1, full=False) one_xunit = u.to_ureg(str(xdata.units)) one_yunit = u.to_ureg(str(ydata.units)) m = u.to_ureg(m, one_yunit / one_xunit) c = u.to_ureg(c, one_yunit) return m, c
def fit_powerlaw(powers, intensities, guess=None): """ This function fits a powerlaw to data. :param powers: A quantity or array of powers. If no quantity, milliwatts are assumed. :param intensities: Quantity or array: The corresponding intensity values to powers. :return: The powerlaw coefficients of the fitted polynom. """ if u.is_quantity(powers): assert u.same_dimension(powers, "watts") powers = u.to_ureg(powers) else: powers = u.to_ureg(powers, 'mW') intensities = u.to_ureg(intensities) if guess is None: guess_amplitude = 1. guess_exponent = 2. guess_offset = min(intensities).magnitude else: assert len(guess) == 3 guess_amplitude = u.to_ureg(guess[0], str(powers.units)).magnitude guess_exponent = u.to_ureg(guess[1], 'dimensionless').magnitude guess_offset = u.to_ureg(guess[2], str(powers.units)).magnitude guess = (guess_amplitude, guess_exponent, guess_offset) return scipy.optimize.curve_fit(Powerlaw.fitfunction, powers.magnitude, intensities.magnitude, guess, maxfev=100000)
def fermi_edge(self, E): """ The shape of a fermi edge for the known fit parameters of the Fermi_Edge instance. :param E: Electron Energy (Quantity or numerical in eV). :return: The value of the Fermi distribution at the energy E. Returned as Quantity in whichever unit the fit data was given. """ E = u.to_ureg(E, "eV") return 0.5 * (1 + scipy.special.erf( (self.E_f - E) / (np.sqrt(((1.7 * kBT_in_eV)**2) + self.dE**2) * np.sqrt(2))) ) * self.c + self.d
def fit_powerlaw(powers, intensities): """ This function fits a powerlaw to data. :param powers: A quantity or array of powers. If no quantity, milliwatts are assumed. :param intensities: Quantity or array: The corresponding intensity values to powers. :return: The powerlaw coefficients of the fitted polynom. """ if u.is_quantity(powers): assert u.same_dimension(powers, "watts") powers = u.to_ureg(powers) else: powers = u.to_ureg(powers, 'mW') intensities = u.to_ureg(intensities) # Do the fit with numpy.ma functions to ignore invalid numbers like log(0) # (these occur often when using dark count subtraction) return numpy.ma.polyfit(numpy.ma.log(powers.magnitude), numpy.ma.log(intensities.magnitude), deg=1, full=False)
def show_kscale(dispersion_data, guess_zeropixel=None, guess_scalefactor=None, guess_energyoffset=None, guess_kfov=None, k_axisid='y'): """ Calculates a free electron parabola with given parameters to approximate a photoemission horizon. Returns a tuple of x (x-pixels) and y (energy) values, that can be used for plotting. Useful to test k scale. :param dispersion_data: 2D-DataSet with an energy and a k-space dimension. :type dispersion_data: ds.DataSet :param guess_zeropixel: The origin pixel value of the parable, given in pixels or unscaled k-axis units. :type guess_zeropixel: pint.Quantity **or** float, optional :param guess_scalefactor: The scalefactor translating unscaled k-axis units to k-space. Typically given in ``angstrom**-1 per pixel``. Alternatively, ``guess_kfov`` can be used to give full kspace width instead, see below. :type guess_scalefactor: pint.Quantity **or** float, optional :param guess_energyoffset: The origin of the parable on the energy axis. Typically, something like the drift voltage in PEEM. :type guess_energyoffset: pint.Quantity **or** float, optional :param guess_kfov: Only used if ``guess_scalefactor`` is not given. Then, this can be given (in ``angstrom**-1``) to guess the kspace-Field-of-View (full kspace image width) instead of a factor per pixel. If neither ``guess_scalefactor`` or this parameter are given, a generic value for ``guess_kfov`` of ``1.5 angstrom**-1`` is used. :type guess_kfov: pint.Quantity **or** float, optional :param k_axisid: The name (label) of the k-axis of the data. Default: ``y`` :type k_axisid: str :return: parab_data, scalefactor & zeropixel with parab_data being the calculated free electron parabola. As scalefactor & zeropoint are just the replicated input parameters, it can be ignored or used for info/debugging. The scalefactor is typically given in unit ``1/angstrom per pixel`` and the zeropoint in unit ``pixel`` """ # Define parabola and parameters for fit if guess_energyoffset is None: energy_offset = u.to_ureg(30, "eV") else: energy_offset = u.to_ureg(guess_energyoffset, "eV") dldpixels = dispersion_data.get_axis(k_axisid).data if guess_zeropixel is None: zeropoint = dldpixels.mean() else: zeropoint = u.to_ureg(guess_zeropixel, "pixel") if guess_kfov is None: guess_kfov = u.to_ureg(1.5, "1/angstrom") else: guess_kfov = u.to_ureg(guess_kfov, "1/angstrom") if guess_scalefactor is None: scalefactor = guess_kfov / (dldpixels.max() - dldpixels.min()) else: scalefactor = u.to_ureg(guess_scalefactor, "1/angstrom per pixel") # Calculate a free electron parabola with given parameters parab_data = freeElectronParabola(dldpixels, scalefactor, zeropoint, energy_offset) return (dldpixels, parab_data), scalefactor, zeropoint
def __init__(self, name, plasma_freq, damping_freq, interband_transitions=None): """ The constructor. :param name: A string for the name of the material. e.g. "Au" :param plasma_freq: the plasma frequency of the free electrons in (rad/s) :param damping_freq: the damping frequency of the free electrons in (rad/s) :param interband_transitions: a list of interband transitions, given either as instance of the InterbandTransition class or as list or tuple of 3 elements containing the frequencies to initialize one. :return: nothing """ self.name = name self.omega_p = units.to_ureg(plasma_freq, 'rad/s') self.gamma = units.to_ureg(damping_freq, 'rad/s') self.interbands = [] if interband_transitions: for trans in interband_transitions: if isinstance(trans, InterbandTransition): self.interbands.append(trans) elif isinstance( trans, list) or isinstance(trans, tuple) and len(trans) == 3: self.interbands.append( InterbandTransition(trans[0], trans[1], trans[2])) else: print( "ERROR: Metal: Interband transition could not be cast. Ignoring the transition." )
def __init__(self, data, angle, rot_plane_axes=(1, 0), axes_mode='keep'): # TODO: Implement other axes handling modes. assert isinstance(data, ds.DataSet) or isinstance(data, ds.ROI), "Invalid input data." self.data_original = data self.data_rotated = None self.angle = u.to_ureg(angle, 'degree') self.rot_plane = (self.data_original.get_axis_index(rot_plane_axes[0]), self.data_original.get_axis_index(rot_plane_axes[1])) self.axes_mode = axes_mode # Interpolation settings: self.reshape = False self.order = 1 self.mode = 'constant' self.cval = np.nan self.prefilter = False
def resultAC(self, s): """ Return the autocorrelation trace according to the fit result for a specific slice. Can be used to compare the fit with the data. ..warning:: This will throw an exception if the result doesn't exist yet. :param s: A slice addressing a single point on the result data. :return: A tuple of (Delays, Fitted Autocorrelation) :rtype: tuple(Quantity, Quantity) """ if self.result is None: raise ValueError( "A result AC cannot be calculated without a result.") assert (self.result.get_datafield(0)[s].shape == () ), "Trying to address multiple elements at once." # Set global variables for copypasted methods. global gpuOBE_stepsize gpuOBE_stepsize = self.cuda_IAC_stepsize.magnitude global gpuOBE_laserBlau gpuOBE_laserBlau = self.laser_lambda.magnitude global gpuOBE_LaserBlauFWHM gpuOBE_LaserBlauFWHM = self.laser_AC_FWHM.magnitude # gpuOBE_normparameter is used in TauACCopol to switch between normalizing the curve before scaling and offset. # It must be True to fit including Amplitude and Offset, as done below! global gpuOBE_normparameter gpuOBE_normparameter = True global gpuOBE_Phaseresolution gpuOBE_Phaseresolution = False ExpDelays = self.fitaxis.data.magnitude ac = TauACCoPol( ExpDelays, self.laser_lambda.magnitude, self.laser_AC_FWHM.magnitude, self.result.get_datafield('lifetimes').data[s].magnitude, self.result.get_datafield('amplitude').data[s].magnitude, self.result.get_datafield('offset').data[s].magnitude, self.result.get_datafield('center').data[s].magnitude, normparameter=True, Phase=False) return ExpDelays, u.to_ureg(ac, self.countunit)
def fov_scale_absolute(pixel_axis, fov, unit='m'): """ Transforms an Axis from pixels to a real space length from a known field of view. :param pixel_axis: The Axis to transform. :param fov: Quantity: The absolute size of the FoV. If a number instead of a quantity is given, m is assumed as unit. :param unit: String: Specifies the output unit for the Axis, Must evaluate to a length unit. :return: The converted Axis. """ assert isinstance(pixel_axis, snomtools.data.datasets.Axis ), "No Axis instance given to scale function." length_per_pixel = fov / len(pixel_axis) length_per_pixel = u.to_ureg(length_per_pixel, 'meters/pixel') return pixel_axis.scale_linear(length_per_pixel, unit=unit)
def fermi_edge(self, E): """ The shape of a fermi edge for the known fit parameters of the Fermi_Edge instance. This is the equivalent of the standalone `fermi_fit` function outside the class, only it is aware of the units of the class parameters. :param E: Electron Energy (Quantity or numerical in eV). :return: The value of the Fermi distribution at the energy E. Returned as Quantity in whichever unit the fit data was given. """ E = u.to_ureg(E, self.E_f_unit) E_f = self.E_f dE = self.dE c = self.c d = self.d return 0.5 * (1 + scipy.special.erf(((E_f - E) / (np.sqrt(( (1.7 * kBT_in_eV)**2) + dE**2) * np.sqrt(2))).magnitude)) * c + d
def fov_scale_relative(pixel_axis, length_per_pixel, unit='m'): """ Transforms an Axis from pixels to a real space length from a known length per pixel scaling. :param pixel_axis: The Axis to transform. :param length_per_pixel: Quantity: The relative scale in length per pixel. If a number instead of a quantity is given, m/pixel is assumed as unit. :param unit: String: Specifies the output unit for the Axis, Must evaluate to a length unit. :return: The converted Axis. """ assert isinstance(pixel_axis, snomtools.data.datasets.Axis ), "No Axis instance given to scale function." length_per_pixel = u.to_ureg(length_per_pixel, 'meters/pixel', convert_quantities=False) return pixel_axis.scale_linear(length_per_pixel, unit=unit)