Analysis Objects
The classes listed on this page are solutions for reoccurring tasks in an analysis considered more or less ‘standalone’ from the rest of cait. E.g. a standard event (SEV) can exist as an array in isolation (it doesn’t need a cait.DataHandler to be created and it doesn’t have to be saved in one). Likewise, to perform an energy calibration, only arrays of testpulse information are needed (which, of course, can be obtained from a cait.DataHandler). All the classes below have ‘easy-plotting’ built in, i.e. it is very easy to assess the quality of the results, or how the methods work in the first place. Eventually, you will probably save the results into a file or back to a cait.DataHandler, but the point here is that these objects provide you with a playground for trying out things without commitment until you’re happy with the result.
SEV, NPS, NCM, OF, GOF
All these objects are ‘glorified’ numpy-arrays. As such, they can be reshaped, combined to form new arrays, and they carry all typical attributes like .shape, .ndim, …
- class cait.versatile.SEV(data: ndarray | IteratorBaseClass = None, dt_us: int = None)[source]
Bases:
ArrayWithBenefitsObject representing a Standard Event (SEV). It can either be created by averaging events from an EventIterator, from an np.ndarray or read from a DataHandler or xy-file.
If created from an EventIterator, the (constant) baseline is removed automatically.
- Parameters:
data (Union[np.array, Type[IteratorBaseClass]]) – The data to use for the SEV. If None, an empty SEV is created. If np.ndarray (or a list that can be converted to an array), each row in the array is interpreted as a SEV for separate channels. If iterator (possibly from multiple channels) a SEV is calculated by averaging the events returned by the iterator. Defaults to None.
dt_us (int) – The microsecond timebase used in the recording. Only necessary if the input is an array (because it can be automatically inferred if constructed from an EventIterator, a file, or a DataHandler). Defaults to None (i.e. automatic detection if possible).
- classmethod from_dh(dh, group: str = 'stdevent', dataset: str = 'event')[source]
Construct SEV from DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to read from.
group (str) – The HDF5 group where the SEV is stored.
dataset (str) – The HDF5 dataset where the SEV is stored.
- Returns:
Instance of SEV.
- Return type:
- classmethod from_file(fname: str, src_dir: str = '')[source]
Construct SEV from xy-file.
- Parameters:
fname (str) – Filename to look for (without file-extension)
src_dir (str) – Directory to look in. Defaults to ‘’ which means searching current directory. Optional
- Returns:
Instance of SEV.
- Return type:
- show(**kwargs)[source]
Plot SEV for all channels. To inspect just one channel, you can index SEV first and call .show on the slice.
- Parameters:
kwargs (Any) – Keyword arguments passed on to
cait.versatile.Line.
- to_dh(dh, group: str = 'stdevent', dataset: str = 'event', **kwargs)[source]
Save SEV to DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to write to.
group (str) – The HDF5 group where the SEV should be stored.
dataset (str) – The HDF5 dataset where the SEV should be stored.
kwargs (Any) – Keyword arguments for
cait.DataHandler.set().
- to_file(fname: str, out_dir: str = '', info_str: str = '')[source]
Write SEV to xy-file.
- Parameters:
fname (str) – Filename to use (without file-extension)
out_dir (str) – Directory to write to. Defaults to ‘’ which means writing to current directory. Optional
info_str (str) – An info string to be saved as a description of the SEV in the header of the file. Optional
- classmethod unscaled(it: IteratorBaseClass)[source]
Construct SEV from event iterator WITHOUT scaling amplitudes to unity.
- Parameters:
it (IteratorBaseClass) – Events from which to construct the SEV.
- Returns:
Instance of SEV and relative amplitudes of all channels.
- Return type:
Tuple[SEV, np.ndarray]
Example:
import cait.versatile as vai # Construct mock data to get event iterator it = vai.MockData().get_event_iterator() # Get the SEV WITHOUT normalizing (unscaled_sev) and # the pulse heights (maxima) for all channels. unscaled_sev, maxima = vai.SEV.unscaled(it) # With this information, you can construct any desired # normalization. E.g. the following gives the regular # SEV, where all channels are normalized to pulse height 1. regular_sev = unscaled_sev/maxima # However, if you want to normalize all channels to the first # channel, e.g., you can do sev_scaled_to_ch0 = unscaled_sev/maxima[0]
- class cait.versatile.NPS(data: ndarray | IteratorBaseClass = None, dt_us: int = None)[source]
Bases:
ArrayWithBenefitsObject representing a Noise Power Spectrum (NPS). It can either be created by averaging the Fourier transformed events from an EventIterator, from an np.ndarray or read from a DataHandler or xy-file. If created from an EventIterator, a proper normalisation to physical units (V²/Hz) is performed automatically.
If created from an EventIterator, the (constant) baseline is removed automatically. To improve the quality of the NPS, a window function is often applied to the noise traces before performing the Fourier transform and averaging (see Numerical Recipes by Press, Teukolsky, Vetterling, Flannery chapter 13.4.1). This can only be achieved when we still have the original noise traces, i.e. when we construct the NPS from an iterator. Instead of just a bare iterator
ityou can pass the iteratorit.with_processing([vai.RemoveBaseline(), vai.TukeyWindow()])toNPS.- Parameters:
data (Union[np.array, Type[IteratorBaseClass]]) – The data to use for the NPS. If None, an empty NPS is created. If np.ndarray, each row in the array is interpreted as an NPS for separate channels. If iterator (possibly from multiple channels) an NPS is calculated by averaging the Fourier transformed events returned by the iterator. Defaults to None.
dt_us (int) – The microsecond timebase used in the recording. Only necessary if the input is an array (because it can be automatically inferred if constructed from an EventIterator, a file, or a DataHandler). Defaults to None (i.e. automatic detection if possible).
- classmethod from_dh(dh, group: str = 'noise', dataset: str = 'nps')[source]
Construct NPS from DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to read from.
group (str) – The HDF5 group where the NPS is stored.
dataset (str) – The HDF5 dataset where the NPS is stored.
- Returns:
Instance of NPS.
- Return type:
- classmethod from_file(fname: str, src_dir: str = '')[source]
Construct NPS from xy-file.
- Parameters:
fname (str) – Filename to look for (without file-extension)
src_dir (str) – Directory to look in. Defaults to ‘’ which means searching current directory. Optional
- Returns:
Instance of NPS.
- Return type:
- show(**kwargs)[source]
Plot NPS for all channels. To inspect just one channel, you can index NPS first and call .show on the slice.
- Parameters:
kwargs (Any) – Keyword arguments passed on to
cait.versatile.Line.
- to_dh(dh, group: str = 'noise', dataset: str = 'nps', **kwargs)[source]
Save NPS to DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to write to.
group (str) – The HDF5 group where the NPS should be stored.
dataset (str) – The HDF5 dataset where the NPS should be stored.
kwargs (Any) – Keyword arguments for
cait.DataHandler.set().
- to_file(fname: str, out_dir: str = '', info_str: str = '')[source]
Write NPS to xy-file.
- Parameters:
fname (str) – Filename to use (without file-extension)
out_dir (str) – Directory to write to. Defaults to ‘’ which means writing to current directory. Optional
info_str (str) – An info string to be saved as a description of the NPS in the header of the file. Optional
- class cait.versatile.NCM(data: ndarray | IteratorBaseClass = None, dt_us: int = None)[source]
Bases:
ArrayWithBenefitsObject representing a Noise Covariance Matrix (NCM). The diagonal elements are Noise Power Spectra (NPS) of all channels, while the off-diagonal elements represent the Cross Power Spectra (CPS). The NCM is either constructed from an EventIterator, from an np.ndarray or read from a DataHandler or xy-file. If created from an EventIterator, a proper normalisation to physical units (V²/Hz) is performed automatically.
If created from an EventIterator, the (constant) baseline is removed automatically. To improve the quality of the NCM, a window function is often applied to the noise traces before performing the Fourier transform and averaging (see Numerical Recipes by Press, Teukolsky, Vetterling, Flannery chapter 13.4.1). This can only be achieved when we still have the original noise traces, i.e. when we construct the NCM from an iterator. Instead of just a bare iterator
ityou can pass the iteratorit.with_processing([vai.RemoveBaseline(), vai.TukeyWindow()])toNCM.- Parameters:
data (Union[np.array, Type[IteratorBaseClass]]) – The data to use for the NCM. If None, an empty NCM is created. If np.ndarray of shape (n_channels, n_channels, n_freq), each diagonal entry (i,i,:) is interpreted as the NPS of channel i, and the off-diagonal elements (i,j,:) correspond to the CPS between channels i and j. If iterator an NCM is calculated by averaging the Fourier transformed events returned by the iterator. Defaults to None.
dt_us (int) – The microsecond timebase used in the recording. Only necessary if the input is an array (because it can be automatically inferred if constructed from an EventIterator, a file, or a DataHandler). Defaults to None (i.e. automatic detection if possible).
- classmethod from_dh(dh, group: str = 'noise', dataset: str = 'ncm')[source]
Load NCM from DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to read from.
group (str) – The HDF5 group where the NCM is stored.
dataset (str) – The HDF5 dataset where the NCM is stored.
- Returns:
Instance of NCM.
- Return type:
- classmethod from_file(fname: str, src_dir: str = '')[source]
Load NCM from xy-file.
- Parameters:
fname (str) – Filename to look for (without file-extension)
src_dir (str) – Directory to look in. Defaults to ‘’ which means searching current directory. Optional
- Returns:
Instance of NCM.
- Return type:
- show(**kwargs)[source]
Plot NCM for all channels.
- Parameters:
kwargs (Any) – Keyword arguments passed on to
cait.versatile.Line.
- to_dh(dh, group: str = 'noise', dataset: str = 'ncm', **kwargs)[source]
Save NCM to DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to write to.
group (str) – The HDF5 group where the NCM should be stored.
dataset (str) – The HDF5 dataset where the NCM should be stored.
kwargs (Any) – Keyword arguments for
cait.DataHandler.set().
- to_file(fname: str, out_dir: str = '', info_str: str = '')[source]
Write NCM to xy-file.
- Parameters:
fname (str) – Filename to use (without file-extension)
out_dir (str) – Directory to write to. Defaults to ‘’ which means writing to current directory. Optional
info_str (str) – An info string to be saved as a description of the NCM in the header of the file. Optional
- class cait.versatile.OF(*args: Any, dt_us: int = None)[source]
Bases:
ArrayWithBenefitsObject representing an Optimum Filter (OF). It can either be created from a Standard Event (SEV) and a Noise Power Spectrum (NPS), a SEV and a Noise Covariance Matrix (NCM), from an np.ndarray or read from a DataHandler or xy-file.
- Parameters:
args – The data to use for the OF. If None, an empty OF is created. If np.ndarray, each row in the array is interpreted as an OF for separate channels. If instances of
SEVandNPS, the OF is calculated from them. If instances ofSEVandNCM, the 2d optimum filter (a correlated version of an optimum filter with multiple channels) is calculated. Defaults to None.dt_us (int) – The microsecond timebase used in the recording. Only necessary if the input is an array (because it can be automatically inferred if constructed from NPS and SEV, a file, or a DataHandler). Defaults to None (i.e. automatic detection if possible).
sev = vai.SEV.from_dh(dh) nps = vai.NPS.from_dh(dh) of = vai.OF(sev, nps)
- classmethod from_dh(dh, group: str = 'optimumfilter', dataset: str = 'optimumfilter*')[source]
Construct OF from DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to read from.
group (str) – The HDF5 group where the OF is stored.
dataset (str) – The HDF5 dataset where the OF is stored. The star * denotes the position of the suffixes ‘_real’ and ‘_imag’.
- Returns:
Instance of OF.
- Return type:
- classmethod from_file(fname: str, src_dir: str = '')[source]
Construct OF from xy-file.
- Parameters:
fname (str) – Filename to look for (without file-extension)
out_dir (str) – Directory to look in. Defaults to ‘’ which means searching current directory. Optional
- Returns:
Instance of OF.
- Return type:
- show(**kwargs)[source]
Plot OF for all channels. To inspect just one channel, you can index OF first and call .show on the slice.
- Parameters:
kwargs (Any) – Keyword arguments passed on to
cait.versatile.Line.
- to_dh(dh, group: str = 'optimumfilter', dataset: str = 'optimumfilter*', **kwargs)[source]
Save OF to DataHandler.
- Parameters:
dh (DataHandler) – The DataHandler instance to write to.
group (str) – The HDF5 group where the OF should be stored.
dataset (str) – The HDF5 dataset where the OF should be stored. The star * denotes the position of the suffixes ‘_real’ and ‘_imag’.
kwargs (Any) – Keyword arguments for
cait.DataHandler.set().
- to_file(fname: str, out_dir: str = '', info_str: str = '')[source]
Write OF to xy-file.
- Parameters:
fname (str) – Filename to use (without file-extension)
out_dir (str) – Directory to write to. Defaults to ‘’ which means writing to current directory. Optional
info_str (str) – An info string to be saved as a description of the OF in the header of the file. Optional
- class cait.versatile.GOF(sev: SEV, nps: NPS, *, basis: str = 'poly', bspec: int | Tuple[List[float], bool] | List[List[float]] = 3, ltc: bool = False, bcc: ndarray = None, window_sev: bool = False, phase: str = '1/4')[source]
Bases:
OFObject representing a generalized optimum filter (see P. Schreiner et al. 2026, to be published).
The generalized filter has to be constructed form a template (SEV) and a noise power spectrum (NPS), as well as a basis which describes the baseline. If you intend to build a traditional optimum filter, use
cait.versatile.OFinstead.Note
vai.OF(sev, nps)is equivalent tovai.GOF(sev, nps, basis='poly', bspec=0, phase='argmax').Warning
If the maximum of your SEV is not aligned at 1/4th of the record window, choosing the correct
phaseargument (depending on the situation) is important. See below.- Parameters:
sev (SEV) – The template to be used for the filter. Note that only single channel SEVs are supported, i.e. has to be of shape
(N,).nps (NPS) – The noise power spectrum to be used for the filter. Note that only single channel NPSs are supported. Has to have shape
(N//2+1,).basis (str, optional) – The basis for the baseline description to be used. Choose either of poly (polynomial baseline), exp (exponential baseline, plus a constant), or custom (arbitrary basis). The specifics of the corresponding basis are handled by the
bspecargument. Defaults to ‘poly’.bspec (Union[int, Tuple[List[float], bool], List[List[float]]], optional) – Detailed specifications of
basis. See table below. Defaults to 3. Together with the default ofbasis, this corresponds to cubic polynomials.bcc (np.ndarray, optional) – Basis Coefficient Covariance matrix. If given, the … Has to have shape (K, K), where K is the number of baseline basis vectors. Defaults to None, i.e. implicitly assuming no knowledge about coefficients = infinite variance.
window_sev (bool, optional) – If True, the standard
cait.versatile.TukeyWindowis applied to the SEV before filter creation. This might especially be relevant if the SEV does not fully decay inside the record window as it reduces high frequency pollution from the resulting step. Note that applying a window will always increase the minimum possible resolution, but the tradeoff of having less numerical artifacts might be worth it. Defaults to False.phase (str, optional) – The (complex) phase convention to use in the maximum alignment of the filter. Either of [‘1/4’, ‘argmax’]. See explanation below. Defaults to ‘1/4’.
Baseline model basis functions:
basisbspecDescription
‘poly’
0, 1, 2, …
Polynomial degree, e.g. 3 -> cubic baseline.
‘exp’
[tau1, tau2, …]
List of decay constants to consider in the model in seconds.
‘exp_dtau’
[tau1, tau2, …]
Same as ‘exp’ but allows for first order deviations of the time constants from the given values.
‘custom’
[b1, b2, b3, …]
List of arrays which constitute the basis.
Phase argument:
The complex phase (
np.exp(-1j*omega*phi), whereomega=2*np.pi*np.fft.rfftfreq(sev.shape[-1])) of a filter determines its lag in the time domain. Traditionally, it is chosen such that (in the time domain) the filter output peaks at the maximum position of a pulse. This is achieved withphi=np.argmax(sev).For finite record windows, the chosen lag acts circularly. You can picture it like moving the SEV in the time domain sample by sample. Samples that fall outside the record window enter again on the other side. For a flat baseline model (which is trivially periodic) and SEVs that decay completely within the record window such shifts usually don’t cause problems. However, if you describe the baseline using non-constant functions (e.g. polynomials) and/or your SEV is non-zero at the end of the record window, this wrap-around is an issue.
There are two cases to consider:
If you just Fourier transform a voltage trace, then multiply it by the filter in frequency domain, then evaluate the result at a fixed sample, the phase is irrelevant as long as the sample where you evaluate it matches the chosen phase. E.g. if you choose
phase='argmax'and you evaluate the result atnp.argmax(sev), you’re good. Likewise, choosingphase='1/4and evaluating atsev.shape[-1]//4is fine.If you want to slide the filter (like e.g. in
cait.versatile.trigger_of()orcait.DataHandler.trigger_of()), the phase has to be zero. Otherwise the wrap-around would spoil the result. For backwards compatibility with traditional OFs (phase aligned with argmax of SEV, usually very close to 1/4th of the record window), those sliding trigger functions automatically account for a phase of 1/4th of the record window! This means that if you intend to use the filter for a sliding trigger,phase='1/4'has to be set when constructing the filter such that it is compensated in the trigger function and the resulting phase is zero again (I agree that this appears cumbersome and confusing, but while implementing this seemed like the best tradeoff between usability, clarity, and backwards compatibility).
Trivially, if
np.argmax(sev) == sev.shape[-1]//4, phase arguments ‘1/4’ and ‘argmax’ are equivalent.phaseUse case
Description
‘1/4’
vai.trigger_ofdh.trigger_ofLag such that it is automatically compensated by sliding trigger functions. Important when triggering with non-constant baselines and SEVs that are non- zero at the end of the window.
‘argmax’
vai.OptimumFilteringdh.apply_ofLag such that filter output peaks at SEV maximum. Traditional phase choice. Use for filter output evaluation at fixed sample.
- property filter_var
Theoretical variance of the filter’s amplitude estimate.
- property filter_var_opt
Theoretical variance of a filter’s amplitude estimate on flat baselines (i.e. if no performance is lost to baseline compensation).
- classmethod from_file(fname: str, src_dir: str = '')[source]
Construct GOF from xy-file.
- Parameters:
fname (str) – Filename to look for (without file-extension).
out_dir (str, optional) – Directory to look in. Defaults to ‘’ which means searching current directory.
- Returns:
Instance of GOF.
- Return type:
- to_file(fname: str, out_dir: str = '', info_str: str = '')[source]
Write GOF to xy-file.
- Parameters:
fname (str) – Filename to use (without file-extension).
out_dir (str, optional) – Directory to write to. Defaults to ‘’ which means writing to current directory.
info_str (str, optional) – An info string to be saved as a description of the GOF in the header of the file. Defaults to ‘’, i.e. no info string.
Energy Calibration
- class cait.versatile.EnergyCalibration(tp_x: ndarray, tp_phs: ndarray, tpas: ndarray, testpulse_response: TestpulseResponse, transfer_function: TransferFunction, max_x_gap: float = inf, param_scale: float = 2.7777777777777777e-10)[source]
Bases:
SerializingMixinEnergy calibration for fixed testpulse amplitudes (
tpas) and testpulse pulse heights (tp_phs) which vary with an independent variable (tp_x). This independent variable is usually the (microsecond) timestamp of the testpulses but can in principle be any quantity.NOTICE: The array
tp_phsmust be cleaned from outliers BEFORE starting the calibration!- Parameters:
tp_x (np.array, shape (N,)) – The independent variable which the testpulse pulse heights
tp_phsdepend on. Usually the (microsecond) timestamps of the testpulses.tp_phs (np.array, shape (N,)) – The testpulse pulse heights which vary with
tp_x.tpas (np.array, shape (N,)) – The testpulse amplitudes of each testpulse. This array is used to divide different testpulse amplitudes into separate calibrations.
testpulse_response (TestpulseResponse) – The description for how to fit the set of
(tp_x, tp_phs)points (for each TPA individually).transfer_function (TransferFunction) – The description for how to fit
(tpas, tp_phs)values for a fixed x-value.max_x_gap (float, optional) – If
<np.inf, a separate calibration is started if there are gaps in the independent variabletp_xwhich are larger than this value. Units ofmax_x_gapare determined byparam_scale. Usually, this would be used whentp_xare (microsecond) timestamps and there is no data for > half an hour. In such a case, setmax_x_gap=0.5andparam_scale=1e-6/3600. Defaults tonp.inf, i.e. this functionality is disabled.param_scale (float, optional) – Conversion factor between the argument
max_x_gapand the units of the input x-values. E.g. If the x-values are given in microseconds, but you want to specify themax_x_gapin hours, you can setparam_scale=1e-6/3600. Defaults to1e-6/3600.
Example:
import numpy as np import scipy as sp import cait.versatile as vai # Define a random state (so that this example is reproducible) rng_seed = 137 # Create mock data. In your analysis, you would of course already # have timestamps, testpulseamplitudes and testpulse heights. N = 5000 start_us = 1426321613000000 ts = np.hstack([sp.stats.randint.rvs(low=start_us, high=start_us+3*1e6*3600, size=N, random_state=rng_seed), sp.stats.randint.rvs(low=start_us+4*1e6*3600, high=start_us+7*1e6*3600, size=N, random_state=rng_seed)]) tpas = sp.stats.randint.rvs(low=1, high=6, size=2*N, random_state=rng_seed) tp_phs = sp.stats.norm.rvs(loc=np.sqrt(0.3*tpas), scale=0.05, size=2*N, random_state=rng_seed) # Create the EnergyCalibration object. # Choose a testpulse respose object from ['TPRCubicSpline', 'TPRPoly']. # Choose a transfer function object from ['TFPoly', 'TFPchip']. # Here, we choose piecewise cubic splines with Gaussian smoothing and # piecewise cubic Hermite polynomials. my_ecal = vai.EnergyCalibration( tp_x=ts, tp_phs=tp_phs, tpas=tpas, testpulse_response=vai.TPRCubicSpline(kernel_length=0.1), transfer_function=vai.TFPchip(), max_x_gap=0.5, # start new calibrations for segments which are separated by > 0.5 hours ) # Open plot that lets you interactively check what your calibration is doing. # You can change the parameters of the TestpulseResponse and TransferFunction. # You can click at any time in the left plot to see what the transfer function # looks like at that time. my_ecal.preview() # To convert TPE values to PH values, call the object: timestamps_of_interest = ts[100:200] tpes_of_interest = np.linspace(2, 3, len(timestamps_of_interest)) output_phs = my_ecal(timestamps_of_interest, tpes_of_interest) # To convert PH values to TPE values, call its inverse: timestamps_of_interest = ts[100:200] phs_of_interest = output_phs output_tpes = my_ecal.inverse(timestamps_of_interest, phs_of_interest) # ... in 'output_tpes' you should have recovered the initial 'tpes_of_interest'. # Of course, in a real-world scenario, you would only be interested in either # of the two directions.
- __call__(x: ndarray, tpes: ndarray)[source]
Convert testpulse equivalent heights to pulse heights at given x-values (usually timestamps).
- Parameters:
x (np.ndarray, shape (N,)) – Independent variable (usually (microsecond) timestamps) for which to evaluate the calibration.
tpes (np.ndarray, shape (N,) or (N, M)) – Testpulse equivalent heights at x-values for which to calculate the pulse height. If 2D, the rows are evaluated at identical x-values.
- Returns:
The pulse height for pulses at
xwith testpulse equivalent heightstpes.- Return type:
np.ndarray, shape (N,) or (N, M)
- classmethod from_file(fname: str)[source]
Construct EnergyCalibration from
.json-file.- Parameters:
fname (str) – Filename to look for (without file-extension)
- Returns:
Instance of EnergyCalibration.
- Return type:
- get_tp_phs(x: ndarray)[source]
Return the testpulse heights evaluated at x (usually (microsecond) timestamps) for each unique TPA (columns).
- Parameters:
x (np.ndarray, shape (N,)) – The independent variable (usually microsecond timestamp) for which to return the testpulse pulse heights.
- Returns:
Pulse heights at
xfor each unique testpulse amplitude (TPA). Invalid values (which fall outside the calibration range) are marked asnp.nan. Furthermore, a flag of validxvalues is returned.- Return type:
Tuple[np.ndarray, np.ndarray], shape (N, n_unique_tpas) and (N,)
- inverse(x: ndarray, phs: ndarray)[source]
Convert pulse heights to testpulse equivalent heights at given x-values (usually timestamps).
- Parameters:
x (np.ndarray, shape (N,)) – Independent variable (usually (microsecond) timestamps) for which to evaluate the calibration.
phs (np.ndarray, shape (N,) or (N, M)) – Heights of pulses at x-values for which to calculate the testpulse equivalent height. If 2D, the rows are evaluated at identical times.
- Returns:
The testpulse equivalent energies for pulses at
xwith pulse heightsphs.- Return type:
np.ndarray, shape (N,) or (N, M)
- plot_testpulse_response(n_fit_grid_points: int = 1000, downsample_factor: int = 1, **viewer_kwargs)[source]
Plot the testpulse response at a given x-value (usually microsecond timestamp).
- Parameters:
n_fit_grid_points (int, optional) – Number of points used for plotting the fits. Defaults to 1000.
downsample_factor (int, optional) – Downsample the
(tp_x, tp_phs)points by this factor. Defaults to 1, i.e. no downsampling.viewer_kwargs (Any) – Additional keyword arguments for
cait.versatile.plot.viewer.Viewer.
- plot_transfer_function(x: float, n_fit_grid_points: int = 1000, **viewer_kwargs)[source]
Plot the testpulse response at a given x-value (usually microsecond timestamp).
- Parameters:
x (float) – Number of points used for plotting the fits. Defaults to 1000.
n_fit_grid_points (int, optional) – Number of points used for plotting the fits. Defaults to 1000.
viewer_kwargs (Any) – Additional keyword arguments for
cait.versatile.plot.viewer.Viewer.
- preview(n_fit_grid_points: int = 1000, downsample_factor: int = 1, **viewer_kwargs)[source]
Plot the testpulse response over time and the transfer function for selected times.
- Parameters:
n_fit_grid_points (int, optional) – Number of points used for plotting the fits. Defaults to 1000.
downsample_factor (int, optional) – Downsample the
(tp_x, tp_phs)points by this factor. Defaults to 1, i.e. no downsampling.viewer_kwargs (Any) – Additional keyword arguments for
cait.versatile.plot.viewer.Viewer.
Testpulse Responses
- class cait.versatile.analysisobjects.testpulseresponse.TestpulseResponse(remove_outliers: bool = False, **kwargs)[source]
Bases:
SerializingMixin,ABCAbstract object describing the height of testpulses over x (usually time) for a single testpulse amplitude (TPA).
- Parameters:
remove_outliers (bool, optional) – If True, pulse heights that deviate more than 5 sigma from the pulse heights trend will be removed. Note that we always recommend to REMOVE OUTLIERS MANUALLY before even starting the calibration! Defaults to False.
To add a specific model (e.g. piecewise cubic splines or polynomials), the following things need to be implemented:
TestpulseResponse.__init__(): Initialize the object with parameters that it needs (e.g. polynomial order) and call the constructor of the super class with those arguments.TestpulseResponse.prepare(): Takes a 1-d array of x-values (usually microsecond timestamps) of testpulses of A SINGLE testpulse amplitude and a 1-d array of the corresponding testpulse heights at these x-values (usually timestamps) and performs the fit over x. This method must return the object itself again. Must perform input shape validation, raise a ValueError in case of shape mismatch, and generate a UserWarning if there are outliers in the pulse-heights or less than 10 data points (you can use the function ‘_sanitize_inputs_prepare’ to perform those checks).TestpulseResponse.__call__(): Takes a 1-d array of x-values (usually microsecond timestamps) and evaluates the polynomial (which was pre-calculated inTestpulseResponse.prepare()). Must perform input shape validation and raise a ValueError in case of shape mismatch (you can use the function ‘_sanitize_inputs_call’ to perform those checks).If the class attribute
_PREVIEW_INPUTSis defined, it will be used for interactively changing the arguments passed to__init__in the preview methods. See below for how they must be structured. You can only allow a subset of the input arguments to be varied, but all field names in_PREVIEW_INPUTSmust be input arguments to__init__.
The usage of this class is demonstrated below for the specific implementation of
TPRPoly. See the docstrings of the child classes for more information on the specific behavior.Example:
import numpy as np import scipy as sp import cait.versatile as vai start_us = 1426321613000000 ts = np.sort(sp.stats.randint.rvs(low=start_us, high=start_us+3*1e6*3600, size=1000)) tp_phs = sp.stats.norm.rvs(loc=1.3, scale=0.05, size=len(ts)) selected_times = np.linspace(start_us, start_us+10000, 100) # Instantiate the object (this does not yet do any fitting). tpr = vai.TPRPoly() # Calculate the fit (this is done only once. Afterwards, the # object can be used for as many evaluations as you like). tpr.prepare(ts, tp_phs) # Get testpulse pulse heights at selected values tph_at_selected_times = tpr(selected_times) # Every TestpulseResponse object can be plotted to interactively # try out different configuration parameters (even before instantiating): vai.TPRPoly.preview(ts, tp_phs)
- abstractmethod __call__(x: ndarray)[source]
Return predicted testpulse heights for given x (usually timestamps).
To use this method, ‘prepare’ has to be called beforehand.
- Parameters:
x (np.ndarray, shape (N,)) – Independent variable for where to evaluate the testpulse heights (usually (microsecond) timestamps).
- Returns:
Predicted testpulse heights at given x-values.
- Return type:
np.ndarray, same shape as x
- abstractmethod prepare(x: ndarray, tp_phs: ndarray)[source]
Precalculate the fit over x (usually time) given x-values (usually testpulse timestamps) and testpulse heights.
It is the caller’s responsibility to only pass values corresponding to a single testpulse amplitude (TPA), AND that this data has been cleaned from outliers.
- Parameters:
x (np.ndarray, shape (N,)) – Independent variable of testpulses for the calibration (usually (microsecond) timestamps).
tp_phs (np.ndarray, shape (N,)) – Pulse heights of testpulses used for the calibration.
- Returns:
This instance.
- Return type:
- classmethod preview(x: ndarray, tp_phs: ndarray, n_grid_points: int = 1000, **viewer_kwargs)[source]
Plot the testpulses defined by
(x, tp_phs)and preview the fit result.- Parameters:
ts (np.ndarray, shape (K,)) – (Microsecond) timestamps of testpulses used for the calibration.
tp_phs (np.ndarray, shape (K,)) – Pulse heights of testpulses used for the calibration.
n_grid_points (int) – Number of points used for plotting the fit.
viewer_kwargs (Any) – Additional keyword arguments for
cait.versatile.plot.viewer.Viewer.
- class cait.versatile.TPRCubicSpline(kernel_length: int = 0.5, param_scale: float = 2.7777777777777777e-10, **kwargs)[source]
Bases:
TestpulseResponseFit testpulse heights with piecewise cubic polynomials. Optionally, Gaussian data smoothing is available before the spline interpolation is performed.
The smoothing can be specified in units different from the x-units using the
param_scaleargument. I.e. if the x values are microsecond timestamps but you want to specify the smoothing width (kernel length) in terms of hours, you can setparam_scale=1e-6/3600which converts microseconds to hours.- Parameters:
kernel_length (float, optional) – Size of the Gaussian smoothing (1 sigma of the distribution). Units of
kernel_lengthare determined byparam_scale. Defaults to 0.5, i.e. withparam_scale=1e-6/3600, this corresponds to half an hour.param_scale (float, optional) – Conversion factor between the argument
kernel_lengthand the units of the input x-values. E.g. If the x-values are given in microseconds, but you want to specify thekernel_lengthin hours, you can setparam_scale=1e-6/3600. Defaults to1e-6/3600.kwargs – Additional keyword arguments for see
cait.versatile.analysisobjects.testpulseresponse.TestpulseResponse.
This example just demonstrates how the interpolation function looks. For a general description on how to use it, see
cait.versatile.analysisobjects.testpulseresponse.TestpulseResponse.Example:
import numpy as np import scipy as sp import cait.versatile as vai start_us = 1426321613000000 ts = np.sort(sp.stats.randint.rvs(low=start_us, high=start_us+3*1e6*3600, size=1000)) tp_phs = sp.stats.norm.rvs(loc=1.3, scale=0.05, size=len(ts)) vai.TPRCubicSpline.preview(ts, tp_phs)
- class cait.versatile.TPRPoly(poly_order: int = 1, **kwargs)[source]
Bases:
TestpulseResponseFit testpulse heights (globally) with a polynomial.
- Parameters:
poly_order (Any) – Order of the polynomial. Defaults to 1 (i.e. a linear polynomial).
kwargs – Additional keyword arguments for see
cait.versatile.analysisobjects.testpulseresponse.TestpulseResponse.
This example just demonstrates how the interpolation function looks. For a general description on how to use it, see
cait.versatile.analysisobjects.testpulseresponse.TestpulseResponse.Example:
import numpy as np import scipy as sp import cait.versatile as vai start_us = 1426321613000000 ts = np.sort(sp.stats.randint.rvs(low=start_us, high=start_us+3*1e6*3600, size=1000)) tp_phs = sp.stats.norm.rvs(loc=1.3, scale=0.05, size=len(ts)) vai.TPRPoly.preview(ts, tp_phs)
Transfer Functions
- class cait.versatile.analysisobjects.transferfunction.TransferFunction(**kwargs)[source]
Bases:
SerializingMixin,ABCAbstract object describing the relation between pulse heights and testpulse equivalent pulse heights. This class is unaware of time, i.e. the interpolation/fit happens in the (TPE, PH)-plane only.
To implement a specific model (e.g. piecewise cubic splines or polynomials), the following things need to be implemented:
TransferFunction.__init__(): Initialize the object with parameters that it needs (e.g. polynomial order) and call the constructor of the super class with those arguments.TestpulseResponse.__call__(): see its docstring. Must perform input shape validation and raise a ValueError in case of shape mismatch (you can use the function ‘_sanitize_inputs’ to perform those checks).If the class attribute
_PREVIEW_INPUTSis defined, it will be used for interactively changing the arguments passed to__init__in the preview methods. See below for how they must be structured. You can only allow a subset of the input arguments to be varied, but all field names in_PREVIEW_INPUTSmust be input arguments to__init__.
This base class provides a default implementation for
TestpulseResponse.inverse(), which calculates the inverse of the fit numerically using the Secant and Bisection Method, given the functionTestpulseResponse.__call__(). The class attribute_DEFAULT_ROOT_FIND_ARGSstores the configuration details of this method. If you wish to implement your own inverse (e.g. because there is a more efficient or analytical way to do it) you can overrideTestpulseResponse.inverse()in the child class. Must perform input shape validation and raise a ValueError in case of shape mismatch (you can use the function ‘_sanitize_inputs’ to perform those checks).The usage of this class is demonstrated below for the specific implementation of
TFPchip. See the docstrings of the child classes for more information on the specific behavior.Example:
import numpy as np import scipy as sp import cait.versatile as vai tpas = [1.0, 2.5, 4.0, 7.0] tp_phs_single = [0.8, 1.5, 1.9, 2.3] tp_phs_multi = [[0.8, 1.3, 1.9, 2.3], [0.7, 1.2, 1.8, 2.4]] tpes_single = np.linspace(0, 5, 100) tpes_multi = [np.linspace(0, 5, 100), np.linspace(1, 4, 100)] tf = vai.TFPchip() # Evaluate a single calibration (i.e. fit tpas and tp_phs) # and evaluate this calibration at tpes. # Shapes: tpas: (n_unique_tpas,) # tp_phs: (n_unique_tpas,) # tpes: (M,) # output: (M,) ph_values_single = tf(tpas, tp_phs_single, tpes_single) # Evaluate a multiple calibrations at the same time (i.e. fit # tpas and rows of tp_phs SEPARATLY, then evaluate each row's # calibration with each row in tpes). # Shapes: tpas: (n_unique_tpas,) # tp_phs: (N, n_unique_tpas) # tpes: (N, M) # output: (N, M) ph_values_multi = tf(tpas, tp_phs_multi, tpes_multi) # The object also has an inverse method: # Call tf.inverse() analogously to above to convert from # pulse height to TPE # Preview what the transfer function does for different arguments. tf.preview(tpas, tp_phs_single)
- abstractmethod __call__(tpas: ndarray, tp_phs: ndarray, tpes: ndarray)[source]
Calculate pulse height values corresponding to testpulse-equivalent amplitudes
tpes, given the testpulse amplitudestpasand corresponding testpulse pulse heightstp_phsthat should be used for the fit.- Parameters:
tpas (np.ndarray, shape (n_unique_tpas,)) – Testpulse amplitudes used for fit. Has to have as many elements as there are unique testpulse amplitudes.
tp_phs (np.ndarray, shape (n_unique_tpas,) or (N, n_unique_tpas)) – Testpulse pulse heights corresponding to
tpas. You can pass either as many as there are TPAs or a 2-d array with as many columns as there are TPAs. In the latter case, the fit is performed for each row. See also shape explanation oftp_phs.tpes (np.ndarray, shape (M,) or (N, M)) – Testpulse-equivalent amplitudes for which to evaluate the calibration. If 2-d, the rows evaluate identical fits.
- Returns:
Pulse heights for
tpes.- Return type:
np.ndarray, same shape as tpes
- inverse(tpas: ndarray, tp_phs: ndarray, phs: ndarray)[source]
Calculate testpulse-equivalent amplitudes corresponding to pulse heights
phs, given the testpulse amplitudestpasand corresponding testpulse pulse heightstp_phsthat should be used for the fit.- Parameters:
tpas (np.ndarray, shape (n_unique_tpas,)) – Testpulse amplitudes used for fit. Has to have as many elements as there are unique testpulse amplitudes.
tp_phs (np.ndarray, shape (n_unique_tpas,) or (N, n_unique_tpas)) – Testpulse pulse heights corresponding to
tpas. You can pass either as many as there are TPAs or a 2-d array with as many columns as there are TPAs. In the latter case, the fit is performed for each row. See also shape explanation oftp_phs.phs (np.ndarray, shape (M,) or (N, M)) – Pulse heights for which to evaluate the calibration. If 2-d, the rows evaluate identical fits.
- Returns:
Testpulse equivalent amplitudes for
phs.- Return type:
np.ndarray, same shape as phs
- classmethod preview(tpas: ndarray, tp_phs: ndarray, n_grid_points: int = 100, **viewer_kwargs)[source]
Plot the testpulses defined by
(tpas, tp_phs)and preview the fit result.- Parameters:
tpas (np.ndarray, shape (N,)) – Testpulse amplitudes used for the fit.
tp_phs (np.ndarray, shape (N,)) – Pulse heights of testpulses used for the fit.
n_grid_points (int) – Number of points used for plotting the fit.
viewer_kwargs (Any) – Additional keyword arguments for
cait.versatile.plot.viewer.Viewer.
- class cait.versatile.TFPchip(fix_at_yaxis: bool = True, y_intercept: float = 0, extrapolate_tangent: bool = True)[source]
Bases:
TransferFunctionTransfer function using a Piecewise Cubic Hermite Interpolating Polynomial (monotonic cubic splines).
- Parameters:
fix_at_yaxis (bool, optional) – If True, the value when intercepting the y-axis is fixed to the value specified by
y_intercept. I.e. when True, the fit considers the additional point(0, y_intercept). Defaults to True.y_intercept (float, optional) – The y-intercept corresponding to the previous argument. Defaults to 0.
extrapolate_tangent (bool, optional) – If True, values outside the interpolation range (extrapolation) are calculated using the tangent of the polynomial at the last node. This can prevent nonsensical result due to extrapolating cubic polynomials too far. Defaults to True.
This example just demonstrates how the interpolation function looks. For a general description on how to use it, see
cait.versatile.analysisobjects.transferfunction.TransferFunction.Example:
import numpy as np import scipy as sp import cait.versatile as vai tpas = [1.0, 2.5, 4.0, 7.0] tp_phs = [0.8, 1.5, 1.9, 2.3] vai.TFPchip().preview(tpas, tp_phs)
- class cait.versatile.TFPlinear(fix_at_yaxis: bool = True, y_intercept: float = 0)[source]
Bases:
TransferFunctionTransfer function using a Piecewise linear polynomial.
- Parameters:
fix_at_yaxis (bool) – If True, the value when intercepting the y-axis is fixed to the value specified by
y_intercept. I.e. when True, the fit considers the additional point(0, y_intercept). Defaults to True.y_intercept (float) – The y-intercept corresponding to the previous argument. Defaults to 0.
This example just demonstrates how the interpolation function looks. For a general description on how to use it, see
cait.versatile.analysisobjects.transferfunction.TransferFunction.Example:
import numpy as np import scipy as sp import cait.versatile as vai tpas = [1.0, 2.5, 4.0, 7.0] tp_phs = [0.8, 1.5, 1.9, 2.3] vai.TFPlinear().preview(tpas, tp_phs)
- class cait.versatile.TFPoly(poly_deg: int = 3, fix_at_yaxis: bool = True, y_intercept: float = 0)[source]
Bases:
TransferFunctionTransfer function using a polynomial.
- Parameters:
poly_deg (int) – The degree of the polynomial to use. Defaults to 3.
fix_at_yaxis (bool) – If True, the value when intercepting the y-axis is fixed to the value specified by
y_intercept. I.e. when True, the fit considers the point(0, y_intercept)as an additional point. Defaults to True.y_intercept (float) – The y-intercept corresponding to the previous argument. Defaults to 0.
This example just demonstrates how the interpolation function looks. For a general description on how to use it, see
cait.versatile.analysisobjects.transferfunction.TransferFunction.Example:
import numpy as np import scipy as sp import cait.versatile as vai tpas = [1.0, 2.5, 4.0, 7.0] tp_phs = [0.8, 1.5, 1.9, 2.3] vai.TFPoly().preview(tpas, tp_phs)