Chapter 9. Extending the set of SpecTcl fit types.

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:

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.

9.1. Classes and objects of the fitting subsystem.

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;

FitState

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.

Point

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.

PointArray

A point array is just that, an ordered collection of points. This typedef disconnects the type from its underlying representation.

PointIterator

Provides an iterator in the STL sense of the term among members of a PointArray

FitParameter

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.

FitParameterList

In general a fit produces more than one parameter. These are organized in a FitParameterList and passed back to the caller as one.

FitParameterIterator

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:


set procdef [fit proc somefit]
eval $procdef
rename fitline somefit
                        

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.