321 lines
9.2 KiB
Igor
321 lines
9.2 KiB
Igor
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
|
|
#include "pearl-area-profiles"
|
|
|
|
// $Id$
|
|
|
|
/// @file
|
|
/// @brief tools for analysing the Fermi edge measured by the Scienta EW4000 analyser.
|
|
/// @ingroup ArpesPackage
|
|
///
|
|
/// proposed procedure
|
|
///
|
|
/// * angular normalization
|
|
/// * fit curved fermi function
|
|
/// * calculate corrected energy coordinates and map to single independent variable
|
|
/// * fit normal fermi function
|
|
///
|
|
/// @author matthias muntwiler, matthias.muntwiler@psi.ch
|
|
/// @author thomas dienel
|
|
///
|
|
/// @copyright 2013-15 Paul Scherrer Institut @n
|
|
/// Licensed under the Apache License, Version 2.0 (the "License"); @n
|
|
/// you may not use this file except in compliance with the License. @n
|
|
/// You may obtain a copy of the License at
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
function analyse_curved_edge(data)
|
|
wave data // 2D counts data, x-scale = kinetic energy, y-scale = analyser angle
|
|
variable guess_EF // initial guess of the Fermi level
|
|
|
|
dfref savedf = GetDataFolderDFR()
|
|
dfref datadf = GetWavesDataFolderDFR(data)
|
|
setdatafolder datadf
|
|
|
|
make /n=5 /d /o w_coef_int
|
|
make /n=7 /d /o w_coef_curved
|
|
|
|
// 1) integrate data
|
|
wave xint = ad_profile_x(data, -inf, inf, "", noavg=1)
|
|
duplicate /free xint, xint_sig
|
|
xint_sig = sqrt(xint * 4) / 4
|
|
variable xmin = wavemin(xint)
|
|
variable xmax = wavemax(xint)
|
|
variable xmean = mean(xint)
|
|
|
|
// 2) fit integrated data for reference
|
|
w_coef_int[0] = xmin
|
|
w_coef_int[1] = 0
|
|
w_coef_int[2] = xmax - xmin
|
|
w_coef_int[3] = dimoffset(xint, 0) + dimdelta(xint, 0) * dimsize(xint, 0) / 2
|
|
w_coef_int[4] = 100
|
|
FuncFit /NTHR=0 FermiFuncLinDOS w_coef_int xint /D /I=1 /W=xint_sig
|
|
wave w_sigma
|
|
duplicate /o w_sigma, w_sigma_int
|
|
|
|
// 3) normalize data
|
|
wave yavg = ad_profile_y(data, -inf, inf, "")
|
|
variable ymean = mean(yavg)
|
|
|
|
duplicate /o data, data_norm, data_sig
|
|
data_sig = sqrt(data * 4) / 4
|
|
data_norm = data_norm * ymean / yavg[q]
|
|
data_sig = data_sig * ymean / yavg[q]
|
|
|
|
// 4) fit normalized data
|
|
wave xavg = ad_profile_x(data, -inf, inf, "")
|
|
xmin = wavemin(xavg)
|
|
xmax = wavemax(xavg)
|
|
xmean = mean(xavg)
|
|
|
|
w_coef_curved[0] = {0, 0, 1, 95.5, 100, 0, -0.0001}
|
|
w_coef_curved[0] = xmin
|
|
w_coef_curved[2] = xmax - xmin
|
|
w_coef_curved[3] = w_coef_int[3]
|
|
w_coef_curved[4] = w_coef_int[4]
|
|
|
|
//variable box = min(11, numpnts(xavg) / 5)
|
|
//FindLevel /B=(box) /EDGE=2 /Q xavg, (xmin + xmax) / 2
|
|
//if (v_flag == 0)
|
|
// w_coef_curved[3] = v_levelx
|
|
//else
|
|
// w_coef_curved[3] = dimoffset(data, 0) + dimdelta(data, 0) * dimsize(data, 0) / 2
|
|
//endif
|
|
|
|
duplicate /o data_norm, fit_data_norm
|
|
FuncFitMD /X=1 /NTHR=0 FermiFuncLinDOS_2Dcorr w_coef_curved data_norm /D=fit_data_norm /I=1 /W=data_sig
|
|
wave w_sigma
|
|
duplicate /o w_sigma, w_sigma_curved
|
|
|
|
display /k=1; appendimage data_norm
|
|
ModifyImage data_norm ctab= {xmin,xmax,Grays,0}
|
|
AppendMatrixContour fit_data_norm
|
|
|
|
setdatafolder savedf
|
|
return 0
|
|
end
|
|
|
|
function record_results(index)
|
|
variable index
|
|
|
|
dfref savedf = GetDataFolderDFR()
|
|
wave /sdfr=root: Tint, Tint_sig, Tcurv, Tcurv_sig, EFcurv, EFcurv_sig
|
|
wave w_coef_int, w_coef_curved
|
|
wave w_sigma_int, w_sigma_curved
|
|
|
|
Tint[index] = w_coef_int[4]
|
|
Tint_sig[index] = w_sigma_int[4]
|
|
Tcurv[index] = w_coef_curved[4]
|
|
Tcurv_sig[index] = w_sigma_curved[4]
|
|
EFcurv[index] = w_coef_curved[3]
|
|
EFcurv_sig[index] = w_sigma_curved[3]
|
|
|
|
setdatafolder savedf
|
|
end
|
|
|
|
function integrate_curved_edge(data, data_sig)
|
|
wave data
|
|
wave /z data_sig
|
|
//wave coef
|
|
|
|
string name = nameofwave(data)
|
|
|
|
duplicate /o data, data_out
|
|
redimension /n=(dimsize(data,0)) data_out
|
|
data_out = 0
|
|
|
|
if (waveexists(data_sig))
|
|
duplicate /o data_sig, sig_out
|
|
redimension /n=(dimsize(data,0)) sig_out
|
|
sig_out = 0
|
|
endif
|
|
|
|
wave ywgt = ad_profile_y(data, -inf, inf, "", noavg=1)
|
|
ywgt = 1 / ywgt / 4
|
|
//ywgt = 1 / 4
|
|
variable sum_ywgt = sum(ywgt)
|
|
|
|
variable nx = dimsize(data, 0)
|
|
variable ny = dimsize(data, 1)
|
|
variable iy
|
|
variable yy
|
|
variable dx
|
|
variable dy
|
|
variable dp
|
|
variable dp_min = 0
|
|
|
|
wave PassEnergy = :attr:PassEnergy
|
|
wave NumSlices = :attr:NumSlices
|
|
|
|
for (iy = 0; iy < ny; iy += 1)
|
|
dy = dimoffset(data, 1) + dimdelta(data, 1) * iy
|
|
dy = dy / dimdelta(data, 1)
|
|
dx = slit_shift(dy * 902 / NumSlices[0], PassEnergy[0])
|
|
dp = round(dx / dimdelta(data, 0)) // <= 0
|
|
dp_min = min(dp_min, dp)
|
|
|
|
data_out[] = p+dp >= 0 ? data_out + data[p+dp][iy] * ywgt[iy] : data_out
|
|
|
|
if (waveexists(data_sig))
|
|
sig_out = p+dp >= 0 ? sig_out + data_sig[p+dp][iy] * ywgt[iy] : sig_out
|
|
endif
|
|
endfor
|
|
|
|
data_out /= sum_ywgt
|
|
data_out[0, -dp_min][] = nan
|
|
|
|
if (waveexists(sig_out))
|
|
sig_out /= sum_ywgt
|
|
//sig_out[0, -dp_min] = nan
|
|
endif
|
|
end
|
|
|
|
function slit_correction(data, data_out, epass)
|
|
wave data // must be image with original dimensions (no cropping!)
|
|
wave /z data_out // 2D or 1D wave to receive the result
|
|
// X dimension must be identical to the one of data
|
|
// if 2D, Y dimension must be either identical to the one of data
|
|
// if 1D, the result will be the sum of the corrected slices
|
|
variable epass // pass energy
|
|
|
|
if (!WaveExists(data_out))
|
|
string name = nameofwave(data) + "_corr"
|
|
duplicate /o data, $name
|
|
wave data_out = $name
|
|
//redimension /n=(dimsize(data,0)) data_out
|
|
endif
|
|
data_out = 0
|
|
|
|
variable nx = dimsize(data, 0)
|
|
variable ny = dimsize(data, 1)
|
|
variable iy
|
|
variable yy
|
|
variable dx
|
|
variable dy
|
|
variable dp
|
|
variable dp_min = 0
|
|
|
|
for (iy = 0; iy < ny; iy += 1)
|
|
dy = dimoffset(data, 1) + dimdelta(data, 1) * iy
|
|
dy = dy / dimdelta(data, 1)
|
|
dx = slit_shift(dy * 902 / ny, epass)
|
|
dp = round(dx / dimdelta(data, 0)) // <= 0
|
|
dp_min = min(dp_min, dp)
|
|
|
|
if (wavedims(data_out) >= 2)
|
|
data_out[][iy] = p+dp >= 0 ? data_out + data[p+dp][iy] : data_out
|
|
else
|
|
data_out = p+dp >= 0 ? data_out + data[p+dp][iy] : data_out
|
|
endif
|
|
endfor
|
|
|
|
data_out[0, -dp_min][] = nan
|
|
end
|
|
|
|
//------------------------------------------------------------------------------
|
|
threadsafe Function FermiFuncLinDOS2D_corr(w,x,y) : FitFunc
|
|
// linear density of states below Fermi level
|
|
// 2D data with corrections:
|
|
// - straight slit (slit shift)
|
|
// - transmission function (polynomial)
|
|
//------------------------------------------------------------------------------
|
|
Wave w; Variable x; variable y
|
|
// w[0] = background far above the fermi level
|
|
// w[1] = slope of the linear background
|
|
// w[2] = amplitude
|
|
// w[3] = fermi level in eV
|
|
// w[4] = temperature in K
|
|
// w[5] = transmission - linear term
|
|
// w[6] = transmission - quadratic term
|
|
// w[7] = transmission - cubic term
|
|
// w[8] = pass energy (hold this value)
|
|
// w[9] = y scale: pixels / unit of y
|
|
|
|
variable pos = w[3] + slit_shift(y * w[9], w[8])
|
|
variable transm = 1 + w[5] * y + w[6] * y^2 + w[7] * y^3
|
|
variable fermi = (w[1] * min(x - pos, 0) + w[2]) / ( exp( (x - pos) / (kBoltzmann * w[4]) ) + 1.0 )
|
|
return transm * (fermi + w[0])
|
|
end
|
|
|
|
//------------------------------------------------------------------------------
|
|
threadsafe Function FermiFuncLinDOS_2Dcorr_old(w,x,y) : FitFunc
|
|
// linear density of states below Fermi level
|
|
// 2D data with a polynomial shift of the Fermi level in the second dimension
|
|
//------------------------------------------------------------------------------
|
|
Wave w; Variable x; variable y
|
|
// w[0] = background far above the fermi level
|
|
// w[1] = slope of the linear background
|
|
// w[2] = amplitude
|
|
// w[3] = fermi level in eV
|
|
// w[4] = temperature in K
|
|
// w[5] = shift - linear term
|
|
// w[6] = shift - quadratic term
|
|
|
|
variable pos = w[3] + w[5] * y + w[6] * y^2
|
|
return w[0] + (w[1] * min(x - pos, 0) + w[2]) / ( exp( (x - pos) / (kBoltzmann * w[4]) ) + 1.0 )
|
|
end
|
|
|
|
/// MCP radius seen by the camera in pixels
|
|
static constant mcp_radius_pix = 555
|
|
/// physical size (radius) of the MCP in mm
|
|
static constant mcp_radius_mm = 20
|
|
/// physical size (radius) of the hemisphere in mm
|
|
static constant hemi_radius_mm = 200
|
|
/// energy range imaged on MCP relative to the pass energy
|
|
static constant mcp_radius_epass = 0.04
|
|
|
|
threadsafe function slit_shift(ypix, epass)
|
|
// calculates the energy shift due to straight slit
|
|
// the radius of the curve is 1/2 of the hemisphere radius
|
|
variable ypix // vertical (angle/position) pixels distance from center
|
|
// = slice coordinate * 902 / number of slices
|
|
variable epass // pass energy
|
|
|
|
variable rpix = mcp_radius_pix * hemi_radius_mm / mcp_radius_mm
|
|
//variable rpix = mcp_radius_pix * hemi_radius_mm / 2 / mcp_radius_mm
|
|
variable rene = epass * mcp_radius_epass * hemi_radius_mm / 2 / mcp_radius_mm
|
|
|
|
variable isin = asin(ypix / rpix)
|
|
variable dene = rene * (cos(isin) - 1)
|
|
|
|
return dene
|
|
end
|
|
|
|
function show_shift(data)
|
|
wave data
|
|
variable epass
|
|
|
|
variable ny = dimsize(data, 1)
|
|
make /o /n=(ny) shift_x
|
|
setscale /i x -ny/2, ny/2, "", shift_x
|
|
wave PassEnergy = :attr:PassEnergy
|
|
wave NumSlices = :attr:NumSlices
|
|
|
|
shift_x = slit_shift(x * 902 / NumSlices[0], PassEnergy[0])
|
|
|
|
variable ecenter = dimoffset(data, 0) + dimdelta(data, 0) * dimsize(data, 0) / 2
|
|
shift_x += ecenter
|
|
end
|
|
|
|
/// calculate the energy resolution of the analyser
|
|
///
|
|
/// @param epass pass energy in eV
|
|
/// @param slit analyser entrance slit in mm
|
|
///
|
|
/// @return energy resolution (FWHM)
|
|
function analyser_energy_resolution(epass, slit)
|
|
variable epass
|
|
variable slit
|
|
|
|
variable respow
|
|
if (epass < 4)
|
|
respow = 1110
|
|
elseif (epass < 8)
|
|
respow = 1400
|
|
else
|
|
respow = 1750
|
|
endif
|
|
|
|
return epass * max(0.2, slit) / 0.2 / respow
|
|
end
|