Automatic Energy Calibration¶
The calibrate
interface¶
When you need to calibrate a TES detector (mass.core.channel.MicroDataSet
)
with any of its fields such as p_pulse_rms
, p_filt_value
, or
p_filt_value_dc
, you can use the mass.core.channel.MicrocalDataSet.calibrate(...)
method. An important caution is that this method computes energies and fills the dataset vector p_energy
,
clobbering any pre-existing values there.
line_names = ['ScKAlpha', 4460.2, 'TiKAlpha', 'TiKBeta']
# Calibrate the MicrocalDataSet ds using its ds.p_filt_value_dc[ds.good()].
fieldname = 'p_filt_value_dc'
ds.calibrate(fieldname, line_names)
If you need to calibrate using only a certain category, such as state="A"
, you can replace the calibrate line above with a call like
fieldname = 'p_filt_value_dc'
ds.calibrate(fieldname, line_names, category={"state": "A"})
This will calibrate using only data that would test True in the call ds.good(state="A")
.
If you want to customize histogram parameters before any of line fitters
use these histograms, you need to supply a closure that modifies any of member
variables of mass.calibration.algorithm.EnergyCalibrationAutocal
, which will be
called before EnergyCalibrationAutocal.fit_lines
is called.
def param_adjust_closure(ds, auto_cal):
gakb_idx = auto_cal.line_names.index("GaKBeta")
auto_cal.fit_lo_hi[gakb_idx] = (auto_cal.ph_opt[gakb_idx] * 0.9975, auto_cal.ph_opt[gakb_idx] * 1.0025)
aska_idx = auto_cal.line_names.index("AsKAlpha")
auto_cal.fit_lo_hi[aska_idx] = (auto_cal.ph_opt[aska_idx] * 0.99, auto_cal.ph_opt[aska_idx] * 1.007)
auto_cal.binsize_ev[aska_idx] = 3.0
line_names = ['ScKAlpha', 4460.2, 'TiKAlpha', 'TiKBeta']
# Calibrate the MicrocalDataSet ds using its ds.p_filt_value_dc[ds.good()].
ds.calibrate('p_filt_value_dc', line_names, param_adjust_closure=param_adjust_closure)
You can calibrate all the good channels of mass.core.channel_group.TESGroup
using mass.core.channel_group.TESGroup.calibrate
. This method works just like
mass.core.channel.MicrocalDataSet.calibrate
. But there are a couple of
differences. One is that it catches any of exceptions and set those channels bad
and proceed with next channel.
def param_adjust_closure(ds, auto_cal):
gakb_idx = auto_cal.line_names.index("GaKBeta")
auto_cal.fit_lo_hi[gakb_idx] = (auto_cal.ph_opt[gakb_idx] * 0.9975, auto_cal.ph_opt[gakb_idx] * 1.0025)
aska_idx = auto_cal.line_names.index("AsKAlpha")
auto_cal.fit_lo_hi[aska_idx] = (auto_cal.ph_opt[aska_idx] * 0.99, auto_cal.ph_opt[aska_idx] * 1.007)
auto_cal.binsize_ev[aska_idx] = 3.0
line_names = ['ScKAlpha', 4460.2, 'TiKAlpha', 'TiKBeta']
# It calibrates the MicrocalDataSet ds using its ds.p_filt_value_dc[ds.good()].
data.calibrate('p_filt_value_dc', line_names, param_adjust_closure=param_adjust_closure)
data.why_chan_bad # Any of failed channels are added into this dictionary.
ds = data.channel[1]
ds.p_energy[ds.good()] # This field is populated unless ds.channum in data.why_chan_bad
The Autocal object (deprecated?)¶
WARNING! Direct use of the Autocal object is not the current, preferred method to attempt automatic calibration.
Proceed at your own risk, but prefer ds.calibrate(...)
if possible. Direct use of Autocal is deprecated, and its
good features are supposed to be incorporated into the calibrate interface (above). If you want to use it anyway, read on….
When you want to calibrate a TES detector using a series of X-ray emission lines
of chemical elements or any sharp peaks in a X-ray spectrum, you can use
mass.calibration.algorithm.EnergyCalibrationAutocal
.
The simplest way to calibrate (manipulating a
mass.calibration.energy_calibration.EnergyCalibration
object) uses the
EnergyCalibrationAutocal.autocal
method. If you supply the name of emission
line, it tries to fit data using a corresponding MultiLorentzianComplexFitter
If only peak position (number in the eV units) is given, it uses the
GaussianLineFitter
to fit the data.
from mass.calibration.energy_calibration import EnergyCalibration
from mass.calibration.algorithm import EnergyCalibrationAutocal
# Suppose we have a numpy array of pulse heights and know that the names of X-ray emission
# lines or energies of sharp peaks in X-ray spectrum that data consist of.
# pulse_heights (np.array(dtype=float)): a numpy array of pulse heights.
# line_names (list[str or float]): names of emission lines or energies of X-ray feature in eV unit.
# e.g. line_names = ['ScKAlpha', 4460.5, 'FeKAlpha', 'FeKBeta', 'AsKAlpha', 11726.2]
cal = EnergyCalibration()
# If you want the calibration spline to go exactly through data points.
cal.set_use_approximation(False)
auto_cal = EnergyCalibrationAutocal(cal, pulse_heights, line_names)
auto_cal.autocal() # This method modifies the cal object underneath it.
Before data are fitted with corresponding line fitters,
EnergyCalibrationAutocal.autocal
needs to determine how to build histograms
which will be subsequently fed into line fitters. Its default parameters usually
work for chemical elements from Ti to Cu on tupac. Sometimes you need to adjust
these histogram parameters before histograms are handed into line fitters. In
this case you can split auto_cal.autocal
into auto_cal.guess_fit_params
and
auto_cal.fit_lines
and adjust default histogram parameters between these
method calls by changing member variables such as auto_cal.fit_lo_hi
,
auto_cal.binsize_ev
, or auto_cal.ph_opt
.
cal = EnergyCalibration()
cal.set_use_approximation(False) # If you want the calibration spline to go exactly through data points.
auto_cal = EnergyCalibrationAutocal(cal, pulse_heights, line_names)
auto_cal.guess_fit_params(fit_range_ev=100, maxacc=0.24) # Initial guess parameters are determined.
# your customizations goes below.
gakb_idx = auto_cal.line_names.index("GaKBeta")
auto_cal.fit_lo_hi[gakb_idx] = (auto_cal.ph_opt[gakb_idx] * 0.9975, auto_cal.ph_opt[gakb_idx] * 1.0025)
aska_idx = auto_cal.line_names.index("AsKAlpha")
auto_cal.fit_lo_hi[aska_idx] = (auto_cal.ph_opt[aska_idx] * 0.99, auto_cal.ph_opt[aska_idx] * 1.007)
auto_cal.binsize_ev[aska_idx] = 3.0
# your customizations are finished.
auto_cal.fit_lines() # Histograms are constructed and fitted with corresponding line fitters.
auto_cal.diagnose()
plt.show()