コード例 #1
0
def build_quadratic_fermi_edge_correction(arr: xr.DataArray,
                                          fit_limit=0.001,
                                          eV_slice=None,
                                          plot=False) -> lf.model.ModelResult:
    # TODO improve robustness here by allowing passing in the location of the fermi edge guess
    # We could also do this automatically by using the same method we use for step detection to find the edge of the
    # spectrometer image

    if eV_slice is None:
        approximate_fermi_level = arr.S.find_spectrum_energy_edges().max()
        eV_slice = slice(approximate_fermi_level - 0.4,
                         approximate_fermi_level + 0.4)
    else:
        approximate_fermi_level = 0
    sum_axes = exclude_hemisphere_axes(arr.dims)
    edge_fit = broadcast_model(
        GStepBModel,
        arr.sum(sum_axes).sel(eV=eV_slice),
        'phi',
        params={'center': {
            'value': approximate_fermi_level
        }})

    size_phi = len(arr.coords['phi'])
    not_nanny = (np.logical_not(np.isnan(arr)) * 1).sum('eV') > size_phi * 0.30
    condition = np.logical_and(edge_fit.F.s('center') < fit_limit, not_nanny)

    quadratic_corr = QuadraticModel().guess_fit(edge_fit.F.p('center'),
                                                weights=condition * 1)
    if plot:
        edge_fit.F.p('center').plot()
        plt.plot(arr.coords['phi'], quadratic_corr.best_fit)

    return quadratic_corr
コード例 #2
0
def fit_for_effective_mass(data: DataType, fit_kwargs=None):
    """
    Performs an effective mass fit by first fitting for Lorentzian lineshapes and then fitting a quadratic
    model to the result. This is an alternative to global effective mass fitting.

    In the case that data is provided in anglespace, the Lorentzian fits are performed in anglespace
    before being converted to momentum where the effective mass is extracted.

    We should probably include uncertainties here.

    :param data:
    :param fit_kwargs: Passthrough for arguments to `broadcast_model`,
    used internally to obtain the Lorentzian peak locations
    :return:
    """
    if fit_kwargs is None:
        fit_kwargs = {}
    data = normalize_to_spectrum(data)
    mom_dim = [d for d in ['kp', 'kx', 'ky', 'kz', 'phi', 'beta', 'theta'] if d in data.dims][0]

    results = broadcast_model([LorentzianModel, AffineBackgroundModel], data, mom_dim, **fit_kwargs)
    if mom_dim in {'phi', 'beta', 'theta'}:
        forward = convert_coordinates_to_kspace_forward(data)
        final_mom = [d for d in ['kx', 'ky', 'kp', 'kz'] if d in forward][0]
        eVs = results.F.p('a_center').values
        kps = [forward[final_mom].sel(eV=eV, **dict([[mom_dim, ang]]), method='nearest') for
               eV, ang in zip(eVs, data.coords[mom_dim].values)]
        quad_fit = QuadraticModel().fit(eVs, x=np.array(kps))

        return HBAR_SQ_EV_PER_ELECTRON_MASS_ANGSTROM_SQ / (2 * quad_fit.params['a'].value)

    quad_fit = QuadraticModel().guess_fit(results.F.p('a_center'))
    return HBAR_SQ_EV_PER_ELECTRON_MASS_ANGSTROM_SQ / (2 * quad_fit.params['a'].value)
コード例 #3
0
def build_photon_energy_fermi_edge_correction(arr: xr.DataArray,
                                              plot=False,
                                              energy_window=0.2):
    edge_fit = broadcast_model(
        GStepBModel,
        arr.sum(exclude_hv_axes(
            arr.dims)).sel(eV=slice(-energy_window, energy_window)), 'hv')

    return edge_fit
コード例 #4
0
def build_cycle_fermi_edge_correction(data: DataType, energy_range=None):
    arr = normalize_to_spectrum(data)

    if 'pixels' in arr.dims or 'phi' in arr.dims:
        arr = arr.S.region_sel('wide_angular')

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

    arr = arr.S.sum_other(['eV', 'cycle'])
    return broadcast_model(GStepBModel, arr.sel(eV=energy_range), 'cycle')
コード例 #5
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'])
コード例 #6
0
    def on_add_new_peak(selection):
        amplitude = data.sel(**selection).mean().item()

        selection = selection[data.dims[0]]
        center = (selection.start + selection.stop) / 2
        sigma = (selection.stop - selection.start)

        model_settings.append({
            'center': {
                'value': center,
                'min': center - sigma,
                'max': center + sigma
            },
            'sigma': {
                'value': sigma
            },
            'amplitude': {
                'min': 0,
                'value': amplitude
            }
        })
        model_defs.append(LorentzianModel)

        if model_defs:
            results = broadcast_model(model_defs,
                                      for_fit,
                                      'fit_dim',
                                      params=compute_parameters())
            result = results.results[0].item()

            if result is not None:
                # residual
                for_residual = data.copy(deep=True)
                for_residual.values = result.residual
                residual_view.data = for_residual

                # fit_result
                for_best_fit = data.copy(deep=True)
                for_best_fit.values = result.best_fit
                fitted_view.data = for_best_fit

                # initial_fit_result
                for_initial_fit = data.copy(deep=True)
                for_initial_fit.values = result.init_fit
                initial_fit_view.data = for_initial_fit

                ax_fitted.set_ylim(ax_initial.get_ylim())
コード例 #7
0
def fit_fermi_edge(data, energy_range=None):
    """
    Fits a Fermi edge. Not much easier than doing it manually, but this can be
    useful sometimes inside procedures where you don't want to reimplement this logic.
    :param data:
    :param energy_range:
    :return:
    """
    if energy_range is None:
        energy_range = slice(-0.1, 0.1)

    broadcast_directions = list(data.dims)
    broadcast_directions.remove('eV')
    assert len(broadcast_directions) == 1 # for now we don't support more

    edge_fit = broadcast_model(GStepBModel, data.sel(eV=energy_range), broadcast_directions[0])
    return edge_fit
コード例 #8
0
def build_direct_fermi_edge_correction(arr: xr.DataArray,
                                       fit_limit=0.001,
                                       energy_range=None,
                                       plot=False,
                                       along='phi'):
    """
    Builds a direct fermi edge correction stencil.

    This means that fits are performed at each value of the 'phi' coordinate
    to get a list of fits. Bad fits are thrown out to form a stencil.

    This can be used to shift coordinates by the nearest value in the stencil.

    :param copper_ref:
    :param args:
    :param kwargs:
    :return:
    """

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

    exclude_axes = ['eV', along]
    others = [d for d in arr.dims if d not in exclude_axes]
    edge_fit = broadcast_model(GStepBModel,
                               arr.sum(others).sel(eV=energy_range),
                               along).results

    def sieve(c, v):
        return v.item().params['center'].stderr < 0.001

    corrections = edge_fit.T.filter_coord(
        along, sieve).T.map(lambda x: x.params['center'].value)

    if plot:
        corrections.plot()

    return corrections
コード例 #9
0
def fermi_edge_reference(data, title=None, ax=None, out=None, norm=None, **kwargs):
    warnings.warn('Not automatically correcting for slit shape distortions to the Fermi edge')

    sum_dimensions = {'cycle', 'phi', 'kp', 'kx'}
    sum_dimensions.intersection_update(set(data.dims))
    summed_data = data.sum(*list(sum_dimensions))

    broadcast_dimensions = summed_data.dims
    broadcast_dimensions.remove('eV')
    if len(broadcast_dimensions) == 1:
        edge_fit = broadcast_model(GStepBModel, summed_data.sel(eV=slice(-0.1, 0.1)), broadcast_dimensions[0])
    else:
        warnings.warn('Could not product fermi edge reference. Too many dimensions: {}'.format(broadcast_dimensions))
        return

    centers = apply_dataarray(edge_fit, np.vectorize(lambda x: x.params['center'].value, otypes=[np.float]))
    widths = apply_dataarray(edge_fit, np.vectorize(lambda x: x.params['width'].value, otypes=[np.float]))

    if ax is None:
        _, ax = plt.subplots(figsize=(8, 5))

    if title is None:
        title = data.S.label.replace('_', ' ')

    plot_centers = centers.plot(norm=norm, ax=ax)
    plot_widths = widths.plot(norm=norm, ax=ax)

    ax.set_xlabel(label_for_dim(data, ax.get_xlabel()))
    ax.set_ylabel(label_for_dim(data, ax.get_ylabel()))

    ax.set_title(title, font_size=14)

    if out is not None:
        plt.savefig(path_for_plot(out), dpi=400)
        return path_for_plot(out)

    plt.show()