myfitter is hosted by Hepforge, IPPP Durham

2.2 Writing your own Model Class

The only thing the Model class does not do for you is calculating the observables for given values of the parameters. Derived classes must implement this by overloading the virtual method

     virtual int calc()

This method should use the current values of the parameters, as returned by Model::parameter(int i), compute the observables and assign them to the elements of the protected member

     ObservableVector observables_

The type ObservableVector is a synonym for boost::numeric::ublas::vector<double> and documented in the Boost uBLAS library. For most purposes, you only need to know that the elements of observables_ can be accessed like those of a normal C array or std::vector<double>, i.e. with observables_[i]. The calc() method should return zero when the computation was successful and a non-zero value otherwise. To avoid computing the observables repeatedly with the same parameter values, the Model class has a protected boolean member

     bool needs_update_

which is set to true whenever a parameter value is changed. You can check the value of needs_update_ at the start of your calc() implementation and you should set it to false after a successful computation of the observables. The default implementation of Model::calc() only sets needs_update_ to false and returns zero.

When you write your own Model classes, you should observe few guidelines which will make your life easier in the long run. A typical declaration of a model class will look like this:

     #include <myfitter/model.hpp>
     using namespace myfitter;
     
     class MyModel : public Model {
     private:
         ...
     
     protected:
         MyModel(int npar, int nobs) : Model(npar, nobs) { ... }
         ...
     
     public:
         // parameters
         enum {P_FIRSTPAR, P_SECONDPAR, ... , P_LASTPAR};
         static const int NPAR = P_LASTPAR+1;
     
         // observables
         enum {O_FIRSTOBS, O_SECONDOBS, ... , O_LASTOBS};
         static const int NOBS = P_LASTOBS+1;
     
         // constructors
         MyModel() : Model(NPAR, NOBS) { ... }
         MyModel(const MyModel& m) : Model(m) { ... }
     
         // assignment operator
         virtual const MyModel& operator=(const MyModel& m) {
             Model::operator=(m);
             ...
             return *this;
         }
     
         ...
     
         virtual int calc();
     };

First of all, note that parameters and observables are identified in myFitter by integer numbers starting from zero. Since you probably don't want to remember the integer assignments for all parameters and observables in all your models, you should define enum types that give intuitive names to the integers associated with your parameters and observables. As the assignments are specific to each model class, you should make the enum types members of the corresponding model class. Using prefixes ‘P_’ and ‘O_’ for the names of parameters and observables, respectively, will avoid name clashes in cases where a parameter is at the same time an observable. In addition, each model class should define static integer constants NPAR and NOBS which keep information about the number of parameters and observables defined in that model.

It is also a good idea to define a default constructor, a copy constructor and a virtual assignment operator for your model. This will allow you to save and restore the state of a model object in a single line. Furthermore, you should re-define the constructor with the two integer arguments from the Model class as a protected constructor. This will allow you to write derived classes of MyModel with additional parameters or observables. This is how the declaration of a derived class could look like:

     class MyOtherModel : public MyModel {
     private:
         ...
     
     protected:
         MyOtherModel(int npar, int nobs) : MyModel(npar, nobs) { ... }
         ...
     
     public:
         // parameters
         enum {P_FIRSTNEWPAR=MyModel::NPAR, ... , P_LASTNEWPAR};
         static const int NPAR = P_LASTNEWPAR+1;
     
         // observables
         enum {O_FIRSTNEWOBS=MyModel::NOBS, ... , O_LASTNEWOBS};
         static const int NOBS = P_LASTNEWOBS+1;
     
         // constructors
         MyOtherModel() : MyModel(NPAR, NOBS) { ... }
         MyOtherModel(const MyOtherModel& m) : MyModel(m) { ... }
     
         // assignment operator
         virtual const MyOtherModel& operator=(const MyOtherModel& m) {
             MyModel::operator=(m);
             ...
             return *this;
         }
     
         ...
     
         virtual int calc() {
             int status;
             if((status = MyModel::calc())) return status;
     
             // calculate new observables
             ...
         }
     };

Note that the definitions for NPAR and NOBS in MyOtherModel shadow the ones in MyModel.