Skip to content

Features (peaks) and Energy Cal explanation and diagrams

Brian Plimley edited this page Mar 23, 2017 · 2 revisions

Legend for diagrams

energycal_slide0.png

First: A note on super()

I'll explain this as if to myself from 2 weeks ago.

For single inheritance, super() is pretty simple. It allows you to call methods of the parent class, with directly specifying the parent class. This is a handy shortcut but that's about it.

But for multiple inheritance, super() is very important. If your class has multiple parents, you want to be able to call methods (particularly __init__) from every parent class, not just one. You do this by putting a super() call in every __init__ method of the classes involved. The key thing to understand is that super() does not go up to the parent class of whatever class it is in; it goes to the next class in the method resolution order (MRO). By putting super() in every __init__, it ensures that all the __init__s get called, even if this is not required for the class in which you are writing super(). It might be necessary for a daughter of that class. Otherwise, when the daughter calls super().__init__(), it only calls the first parent class, and could stop there.

This article explains it much better.

Also note that I from builtins import super which uses the future package to provide Python 3 super functionality even in Python 2.

Features and peaks

Abstract base classes

A peak in a spectrum has some fundamental properties, like energy and net counts (area). However, more generally one can have a feature in a spectrum that is not a peak, but still has a characteristic energy and/or area. For example a Compton edge has an energy but possibly not an area. So the base class is called Feature, and is an abstract class because it isn't a concrete thing yet.

Characteristics of a feature such as energy and area each carry certain functionality. For example, if you have a feature with energy, you can assign a calibration energy -- the known energy value of that feature -- in addition to the energy value (in channels) calculated from the spectrum. This functionality, which is based on a certain property of the feature, is contained in EnergyFeature which inherits from Feature. Similarly for AreaFeature, which would be used for an efficiency calibration (not implemented yet). Another subclass is SpectralFeature which contains the functionality of including a Spectrum object. By including the Spectrum in the attributes of the feature, the methods of the feature (such as peak-fitting) have access to the spectral data. The spectrum could be an intrinsic part of the Feature base class, however, it is convenient for calibration to have simple features with only a channel/energy value pair, without requiring an associated spectrum.

EnergyFeature, AreaFeature, and SpectralFeature are all abstract classes that inherit from Feature. They cannot be concrete yet, because they do not provide the means by which the energy or area is calculated.

Concrete classes

The abstract classes are the building blocks for concrete feature classes. The concrete classes don't need to rewrite the functions of the abstract classes, because everything is handled using super() (see separate discussion).

A simple example is the ArbitraryEnergyPoint, which inherits from EnergyFeature and just takes a channel and energy value and assigns them to the appropriate properties of EnergyFeature.

A more complete example is GrossROIPeak, which isn't very useful, but is just a usage example. GrossROIPeak is a feature associated with a spectrum, that has an area (gross counts) as well as an energy (centroid energy). So it inherits from SpectralFeature, EnergyFeature, and AreaFeature. GrossROIPeak calls the __init__ methods of all its parent classes (through the magic of super()). It provides its own methods that simply define how you initialize it (with a region of interest), and how you compute energy and area. The rest of the interface (calibration energy or area, spectrum input, providing properties) is handled by the parents.

energycal_slide1.png

Energy calibration

There are two kinds of energy calibration. The simplest kind is just an equation that converts channels to energies. The only data associated with it are coefficients (e.g. for a polynomial). It might come from a SPE file.

The second kind of energy calibration is not only an equation, but also a set of calibration points which produced the calibration. In this code, the points are Feature objects (actually, they must be subclasses of EnergyFeature). Since the points are included with the calibration object, you can add new points and calculate a new best fit, or revise or remove calibration points. You also want to weight points based on the uncertainty of the energy values (not implemented yet).

I designed the class structure of energy calibration to allow both of these styles to be used, while minimizing duplicated code.

Abstract classes

EnergyCalBase is the abstract base class for all energy calibrations, as the name implies. It requires subclasses to have a ch2kev method, which converts channels to keV. (kev2ch might be required too.)

FitEnergyCalBase is the abstract base class for the "second kind" of energy calibration described above, with a set of calibration points that can be adjusted. It is initialized with a list of Feature objects (the calibration points), has methods for adding and removing calibration points, and requires a fit method which uses the features to produce the coefficients or curve for ch2kev. It cannot be concrete because it does not define a fit method.

Concrete classes

SimplePolyCal is the "first kind" of energy calibration described above. It represents simply a polynomial equation for converting channels to keV. It inherits from EnergyCalBase only and takes coefficients as inputs.

FitPolyCal is the "second kind" of energy calibration described above. It inherits from FitEnergyCalBase and SimplePolyCal. It starts with the list of features/peaks, and fits a polynomial curve of the order specified. The actual calibration curve is produced with the SimplePolyCal code using the coefficients from the fit, so ch2kev is defined in SimplePolyCal instead of being written again.

Does FitPolyCal need to inherit from SimplePolyCal?

FitPolyCal inheriting from SimplePolyCal complicates things, because you have to make sure you fit the points to get coefficients before you can run SimplePolyCal's __init__ method. There may be a cleaner way to arrange the super() calls and __init__s, or it could be done without inheriting from SimplePolyCal. That inheritance only saves a couple lines of code.

energycal_slide2.png