예제 #1
0
def fs_gap(data: DataType, shape=None, energy_range=None):
    data = normalize_to_spectrum(data)

    if energy_range is None:
        energy_range = slice(-0.1, None)

    data.sel(eV=energy_range)

    reduction = None
    if shape is None:
        # Just rebin the data along 'phi'
        reduction = {'phi': 16}

    data = rebin(data, reduction=reduction, shape=shape)
    return broadcast_model(GStepBModel, data, ['phi', 'beta'])
예제 #2
0
    def set_data(self, data: DataType, **kwargs):
        original_data = normalize_to_spectrum(data)
        self.original_data = original_data

        if len(data.dims) > 2:
            assert 'eV' in original_data.dims
            data = data.sel(eV=slice(-0.05, 0.05)).sum('eV', keep_attrs=True)
            data.coords['eV'] = 0
        else:
            data = original_data

        if 'eV' in data.dims:
            data = data.S.transpose_to_back('eV')

        self.data = data.copy(deep=True)

        if not kwargs:
            rng_mul = 1
            if data.coords['hv'] < 12:
                rng_mul = 0.5
            if data.coords['hv'] < 7:
                rng_mul = 0.25

            if 'eV' in self.data.dims:
                kwargs = {
                    'kp': np.linspace(-2, 2, 400) * rng_mul,
                }
            else:
                kwargs = {
                    'kx': np.linspace(-3, 3, 300) * rng_mul,
                    'ky': np.linspace(-3, 3, 300) * rng_mul,
                }

        self.conversion_kwargs = kwargs
예제 #3
0
def calculate_shirley_background(xps: DataType,
                                 energy_range: slice = None,
                                 eps=1e-7,
                                 max_iters=50,
                                 n_samples=5):
    """
    Calculates a shirley background iteratively over the full energy range `energy_range`.
    :param xps:
    :param energy_range:
    :param eps:
    :param max_iters:
    :return:
    """
    if energy_range is None:
        energy_range = slice(None, None)

    xps = normalize_to_spectrum(xps)
    xps_for_calc = xps.sel(eV=energy_range)

    bkg = calculate_shirley_background_full_range(xps_for_calc, eps, max_iters)
    full_bkg = xps * 0

    left_idx = np.searchsorted(full_bkg.eV.values,
                               bkg.eV.values[0],
                               side='left')
    right_idx = left_idx + len(bkg)

    full_bkg.values[:left_idx] = bkg.values[0]
    full_bkg.values[left_idx:right_idx] = bkg.values
    full_bkg.values[right_idx:] = bkg.values[-1]

    return full_bkg
예제 #4
0
def symmetrize(data: DataType, subpixel=False, full_spectrum=False):
    """
    Symmetrizes data across the chemical potential. This provides a crude tool by which
    gap analysis can be performed. In this implementation, subpixel accuracy is achieved by
    interpolating data.

    :param data: Input array.
    :param subpixel: Enable subpixel correction
    :param full_spectrum: Returns data above and below the chemical potential. By default, only
           the bound part of the spectrum (below the chemical potential) is returned, because
           the other half is identical.
    :return:
    """
    data = normalize_to_spectrum(data).S.transpose_to_front('eV')

    if subpixel or full_spectrum:
        data = _shift_energy_interpolate(data)

    above = data.sel(eV=slice(0, None))
    below = data.sel(eV=slice(None, 0)).copy(deep=True)

    l = len(above.coords['eV'])

    zeros = below.values * 0
    zeros[-l:] = above.values[::-1]

    below.values = below.values + zeros

    if full_spectrum:
        if not subpixel:
            warnings.warn("full spectrum symmetrization uses subpixel correction")

        full_data = below.copy(deep=True)

        new_above = full_data.copy(deep=True)[::-1]
        new_above.coords['eV'] = (new_above.coords['eV'] * -1)

        full_data = xr.concat([full_data, new_above[1:]], dim='eV')

        result = full_data
    else:
        result = below

    return result
def remove_incoherent_background(data: DataType, set_zero=True):
    """
    Sometimes spectra are contaminated by data above the Fermi level for
    various reasons (such as broad core levels from 2nd harmonic light,
    or slow enough electrons in ToF experiments to be counted in subsequent
    pulses).
    :param data:
    :param set_zero:
    :return:
    """
    data = normalize_to_spectrum(data)

    approximate_fermi_energy_level = data.S.find_spectrum_energy_edges().max()

    background = data.sel(eV=slice(approximate_fermi_energy_level + 0.1, None))
    density = background.sum('eV') / (np.logical_not(np.isnan(background)) *
                                      1).sum('eV')
    new = data - density
    if set_zero:
        new.values[new.values < 0] = 0

    return new
예제 #6
0
def normalize_by_fermi_dirac(data: DataType, reference_data: DataType = None, plot=False,
                             broadening=None,
                             temperature_axis=None,
                             temp_offset=0, **kwargs):
    """
    Normalizes data according to a Fermi level reference on separate data or using the same source spectrum.

    To do this, a linear density of states is multiplied against a resolution broadened Fermi-Dirac
    distribution (`arpes.fits.fit_models.AffineBroadenedFD`). We then set the density of states to 1 and
    evaluate this model to obtain a reference that the desired spectrum is normalized by.

    :param data: Data to be normalized.
    :param reference_data: A reference spectrum, typically a metal reference. If not provided the
                           integrated data is used. Beware: this is inappropriate if your data is gapped.
    :param plot: A debug flag, allowing you to view the normalization spectrum and relevant curve-fits.
    :param broadening: Detector broadening.
    :param temperature_axis: Temperature coordinate, used to adjust the quality
                             of the reference for temperature dependent data.
    :param temp_offset: Temperature calibration in the case of low temperature data. Useful if the
                        temperature at the sample is known to be hotter than the value recorded off of a diode.
    :param kwargs:
    :return:
    """
    reference_data = data if reference_data is None else reference_data
    broadening_fit = determine_broadened_fermi_distribution(reference_data, **kwargs)
    broadening = broadening_fit.params['conv_width'].value if broadening is None else broadening

    if plot:
        print('Gaussian broadening is: {} meV (Gaussian sigma)'.format(
            broadening_fit.params['conv_width'].value * 1000))
        print('Fermi edge location is: {} meV (fit chemical potential)'.format(
            broadening_fit.params['fd_center'].value * 1000))
        print('Fermi width is: {} meV (fit fermi width)'.format(
            broadening_fit.params['fd_width'].value * 1000))

        broadening_fit.plot()

    offset = broadening_fit.params['offset'].value
    without_offset = broadening_fit.eval(offset=0)

    cut_index = -np.argmax(without_offset[::-1] > 0.1 * offset)
    cut_energy = reference_data.coords['eV'].values[cut_index]

    if temperature_axis is None and 'temp' in data.dims:
        temperature_axis = 'temp'

    transpose_order = list(data.dims)
    transpose_order.remove('eV')

    if temperature_axis:
        transpose_order.remove(temperature_axis)
        transpose_order = transpose_order + [temperature_axis]

    transpose_order = transpose_order + ['eV']

    without_background = (data - data.sel(eV=slice(cut_energy, None)).mean('eV')).transpose(*transpose_order)

    if temperature_axis:
        without_background = normalize_to_spectrum(without_background)
        divided = without_background.T.map_axes(
            temperature_axis, lambda x, coord: x / broadening_fit.eval(
                x=x.coords['eV'].values, lin_bkg=0, const_bkg=1, offset=0,
                conv_width=broadening,
                fd_width=(coord[temperature_axis] + temp_offset) * K_BOLTZMANN_EV_KELVIN))
    else:
        without_background = normalize_to_spectrum(without_background)
        divided = without_background / broadening_fit.eval(
            x=data.coords['eV'].values,
            conv_width=broadening,
            lin_bkg=0, const_bkg=1, offset=0)

    divided.coords['eV'].values = divided.coords['eV'].values - broadening_fit.params['fd_center'].value
    return divided