The fit types available in the SpecTcl fitting subsystem are quite restricted. SpecTcl' fitting is not intended to replace that of full fledged offline analysis programs such as Root e.g. The SpecTcl fitting subsystem is, however extensible.
This chapter:
Describes the classes and objects used by the fitting subsystem.
Sketches what needs to be done to add a new fit type.
Note that to show an example would get us far afield into how to use various fit libraries and is therefore beyond the scope of this document. If, however, you've added a fit type to SpecTcl, by all means send it to us and I'll extend the manual with your code as an example.
The base class for the set of classes that can actually create
fits is CFit
. Your fitting class must
derive from this class.
CFit
defines the operations your class
must implement as a set of pure virtual methods. It also provides
utilities and services that are useful to programmers implementing
fits. These services include state management, point and parameter
management.
CFit
is implemented in the
CFit.h header. The important parts of this
header are shown below:
Example 9-1. CFit
class definition
class CFit : public CNamedItem { // Public data types. public: typedef enum { Accepting, Performed } FitState; struct Point { double x; double y; int operator==(const Point& rhs) const { return (x == rhs.x) && (y == rhs.y); } int operator!=(const Point& rhs) const { return !(operator==(rhs)); } }; typedef std::vector<Point> PointArray; typedef PointArray::iterator PointIterator; typedef std::pair<std::string, double> FitParameter; typedef std::vector<FitParameter> FitParameterList; typedef FitParameterList::iterator FitParameterIterator; public: CFit (std::string name, int id = 0); protected: void SetFitState(FitState state); public: void AddPoint (Point p) ; void ClearPoints(); PointIterator begin () ; PointIterator end () ; size_t size () ; FitState GetState () const ; public: virtual CFit* clone() = 0; virtual void Perform () = 0 ; virtual double operator() (double x) =0 ; virtual FitParameterList GetParameters () =0 ; virtual std::string Type() const = 0; virtual std::string makeTclFitScript() = 0; };
Let's start by looking at the data types the CFit
base class exports;
Fit objects are state-full. They alternate between accumulating points for a fit (Accepting) and having performed the fit (Performed. Fit parameters can only be retrieved from the fit when it is in the Performed state.
ClearPoints
and construction
place the fit in Accepting, when
AddPoint
can be called repeatedly
to add a data point. Invoking Perform
is expected to perform the fit and to put the fit in to
the Performed state.
This allows the same fit object to be repeatedly used on different data. The intent in the SpecTcl fitting system is that once a fit is defined on an area of interest in a spectrum, it can be repeatedly performed on new spectrum data.
Points encapsulate the data to be fit. A point contains
an x
and
y
member. The fit function
is assumed to fit a function defined on x
.
For SpecTcl, x
is a spectrum
channel number and y
is the
number of counts in that channel.
A point array is just that, an ordered collection of points. This typedef disconnects the type from its underlying representation.
Provides an iterator in the STL sense of the term among members of a PointArray
Fits, when performed produce a set of parameters that depend on the functional form of the fit. Each parameter is assigned a name by the author of its fit class.
A FitParameter object is a pair that consists of the name of a fit parameter and its value.
In general a fit produces more than one parameter. These are organized in a FitParameterList and passed back to the caller as one.
Provides clients of the class with an iterator over
a FitParameterList object. Using iterators
allows the client code to not make any assumptions
about the implementation of
operator[]
for
FitParameterList.
Next let's look at the services the CFit
class provides its clients and subclasses:
void SetFitState( FitState state);
This is protected. It should be used by actual, concrete,
fit classes to indicate a new state for the fit. For
example, the Perform
method should
at some point all this to set the fit state to
Performed.
void AddPoint( Point p);
Normally this is called by external clients to add a point to the fit. This sets the fit state to Accepting. Note that the point is added to the existing points.
Generally, after performing a fit,
ClearPoints
should be called
first prior to adding new points.
void ClearPoints();
Clears the set of points that have been accumulated for
the fit so far. Immediately after calling this a
call to size
will return
0.
This method also sets the fit state to Accepting.
PointIterator begin();
Returns an iterator to the beginning of the PointList maintained by this object. This is normally used by derived classes but is also available to external clients.
PointIterator end ();
Provides an end of iteration iterator to the PointList maintained by this object.
size_t size();
Returns the number of elements (points) in the PointList object maintained by the class.
const FitState GetState();
Returns the current state of the fit.
All CFit
concrete subclasses must
implement a specific set of methods. Below we describe the
call signatures of these methods as well as the responsibilities and
expectations of each of these methods.
virtual CFit* clone();
Expected to return a dynamically allocated copy of the current fit. This is essentially a virtual copy constructor.
A simple way to implement this is in terms of a copy constructor if one has been implemented.
virtual void Perform ();
This method should perform the fit and, if the fit was successful, set the state to Performed. The points to fit can be gotten via the point list iterator methods the base class provides.
virtual double operator()( double x);
This method should only be legal if the state is Performed. If called when the fit is Accepting, a std::string exception should be thrown.
If the fit has been performed, the method should
evaluate the fit function at x
and
return its value.
virtual FitParameterList GetParameters();
This method can only be legitimately called when the fit is in the Performed state. It is expected to return the values of the parameters of the fit.
Each fit parameter must be given a name by the
concrete fit class. The return value is an iterable
container of std::pair
objects
where the first element of each pair is an
std::string
containing the
parameter name and the second element is a
double that is the fit parameter.
virtual const std::string Type();
This method must return a string that identifies the fit type. The string should be unique among all other fit types. For example, the built in gaussian fit on constant background returns gaussian.
virtual std::string makeTclFitScript();
This method can only be called if the state is
Performed. If the fit is in the
Accepting state a
std::string
exception should be
thrown.
If the fit has been Performed, the method should return a string that defines a Tcl proc for a proc named fitline. The proc shoulid accept a single parameter (an x coordinate) and should return the fit evaluated at that point.
Note that since all fits will return a string that defines the same proc a typical Tcl usage of this might be:
This sample code, once it defines the proc renames it immediately to be the same as the name of the fit that produced it.
When SpecTcl needs to instantiate a concrete fit, it asks the
CFitFactory
class
(defined in CFitFactory.h) to create a
dynamically allocated object given the fit type string.
This class is made up entirely of static methods. The important
parts of this clss definition look like this:
Example 9-2. CFitFactory
class definition
class CFitFactory { // Public data types: public: typedef std::map<std::string, CFitCreator*> FitCreatorMap; typedef FitCreatorMap::iterator FitCreatorIterator; // Private member data. private: static FitCreatorMap m_mapCreators; // Note that all data are static, therefore // all functions are static too. public: static void AddFitType (const std::string & rType, CFitCreator* pCreator) ; static CFit* Create (std::string sFitType, std::string sFitName, int id = 0) ; static FitCreatorIterator beginCreators(); static FitCreatorIterator endCreators(); static int numberOfCreators(); static FitCreatorIterator FindFitCreator (std::string sType) ; };
The CFitCreatorIterator provides an interator throgh
the FitCreatorMap
. This container is
an associative container of pairs. The first element of each pair
is the name of a registered fit type. The second is a pointer
to an object that knows how to create a fit of that type
(CFitCreator*
).
Let's turn our attention to the methods defined by this class. The methods are all defined as static which means this class never actually needs any instances. All methods can be called relative to the class name.
static void AddFitType ((const std::string & rType, CFitCreator* pCreator);
Registers a new fit type with the factory. The
rType
referes to a string that
is the type name for the fit. This is normally what the
fit object's Type
method
returns.
The pCreator
parameter points to
an object that knows how to create fits of this type.
We'll describe CFitCreator
below.
static CFit* Create( std::string sFitType, std::string sFitName, int id = 0 );
Producs a fit type of the requested
sFitType
. The fit creator is passed
two parameters; sFitName
is a
name to be associated with the fit and id
is a numeric id (which is, for the most part obsolete).
If an appropriate creator for sFitType
has been registered, it is asked to produce a fit object.
If no matching fit creator can be found, nullptr is
returned. If a matching creator is found but returns a
nullptr when asked to produce a fit,
a std::runtime_error
exception
is thrown.
The specific type of exception is defined in
DesignByContract.h and is an
AssertionException
.
static FitCreatorIterator beginCreators();
Returns an iterator to the beginning of
container that associates fit type strings with fit
creator objects. Dereferencing this iterator results
in an
std::pair<std::string, CFitCreator*>
object. The first element of this object is a fit type.
The second the fit creator associated with this fit type.
static FitCreatorIterator endCreators();
An end of iteration iterator to the container that associates fit type names with their creators.
static int numberOfCreators();
Returns the number of creators that have been registered. Note that built in creators are just creators that are registered by standard SpecTcl code and therefore are included in this count.
static FitCreatorIterator FindFitCreator( std::string sType);
Given a fit type, returns an iterator to the
typename/creator pair in the container that associates
fit names with fit creators. If there is no match, this
method returns the same values as
endCreators()
.
The last piece of the puzzle are fit creators. These are expected to know how to create fits of a specific fit type. They are registered with the fit factory and called upon when SpecTcl wants to create a fit.
The CFitCreator
is an abstract base class
for all fit creators. It is defined in CfitCreator.h.
The important parts of that class definition are shown below:
Example 9-3. CFitCreator
class definition
class CFitCreator { public: virtual CFit* operator() (std::string name, int id=0) = 0 ; virtual std::string DescribeFit() = 0; };
virtual CFit* operator()( std::string name, int id=0 );
This is the main method. It is expected to return
a dynamically created CFit
object
that is specific to the type of fit it knows how to
create. name
is a name to be given
to the fit which it can retain or not (the fit subsystem
in SpecTcl puts fits into dictionary keyed by fit name
anyway). The id
is a mostly obsolete
integer that is included for compatibility with older
versions and was intended to be a unique integer identifying
the fit object.
virtual std::string DescribeFit();
Provides a string that describes the type of fit this creator creates. This allows SpecTcl to iterate through the registered fit creators documenting the types of fits that are supported at any instant by any tailoring of SpecTcl.