A Templated Consumer Module for Handling Calibrations
Which Use
Multi Bank Input and Single Table Output
 

Compiled by Jack Cranshaw
Last updated 11/19/2002

Online Calibrations :
Online calibration data will be collected, analyzed, and written to the database with consumers. For any consumers requiring online histogramming, these should use the  consumer monitoring framework  developed by Hans Wenzel and Kaori Maeshima. The file I/O (tape, disk, data stream, EDM2, ...) is provided as modules inside AC++ where it is up to the user to include and specify the appropriate parameters. The online display tools and  Root  I/O are provided by the consumer framework. The interface to the database is provided by the Calibration database API and the CalibrationManager AC++ module. Calibration data can be taken in one of two modes:  X-mode  (data collected in the crate) or D-mode (accumulate D-banks like normal data). The method for making X-mode consumers is described at the indicated link.

General Aspects of D-mode Calibration Data:
When calibration data is collected in D-mode, it involves:

The three points listed above will be done for all calibrations but will differ between calibrations. All the mechanics of connecting to the database, displaying histograms, etc. is the same and shared. Since this sort of situation lends itself to templating, a templated class has been created which follows this flow, while doing all the common procedures, thus requiring the user to only maintain the code which handles the three bullets.

Understanding the Template:
The template class comes in several flavors/versions which are kept in the CalibConsumer package.

Essentially there are only two, but GenericDMode3 is kept around for backward compatibility and testing.

The template contains the talk-to parameters. User specific code then gets called based on those parameters.

Defining User Classes:
The class GenericDMode3.hh is not modified by the user, but the user must know what is expected in order to include them in the user class. Methods which are required by the template are marked as Required.

    1. (Required) The user class must provide at least three typedef's.

    Multiple EventClass's can be defined, so that for GenericDMode2 user classes there is an
    EventClass1, EventClass2, etc. which designate different input bank classes.

    2. The user can define a transient class for collecting data from the event which will be used in the
    calculation, for example:

          struct Avg {
            int noent;
            double sum;
            double sum2;
            Avg():noent(0),sum(0.0),sum2(0.0) {}
          };

    This struct contains the information needed to calculate a mean and sigma. There will be one struct
    per channel, so a mapping of channel to struct must be provided. One of the simplest ways to do this
    is with an STL map, for example:

          std::map<MapClass::CalibKey,Avg>  Avgmap;

    3. (Required) The user class must provide a method bookHistos() where the Root objects to be
    presented for display are declared and put in a TList. The method should return the pointer to the
    list. For example:

      TList* bookHistos()
      {
        // Declare histograms
        _testhist1 = TH1F("testhist1","totevents RAW",2,0,2);
        _testhist2 = TH1F("testhist2","totevents FILTERED",2,0,2);

        // add histograms to list
        TList* lista = new TList();
        lista->Add(&_testhist1);
        lista->Add(&_testhist2);

        return lista;
      }

    All the Root manipulations are handled by the base class of the template.

    4. (Required) The user class must provide an initialize method where histograms are reset and any
    containers or maps are cleared.

    5. (Required) The user class must provide an accumulate method. This method is called by the
    template once for every channel for every event and fills the map of structs. For example:

    void LEDCalib2::accumulate(const EventRecord::ConstIterator p_bank)
    {
      ConstHandle<StorableBank> temp(p_bank);
      string name = temp->bank_name();
      cout << "Processing bank " << name << endl;

      if(name == "CEMD") {
      ConstHandle<EventClass>   e(p_bank);
      for (EventClass::ConstGrandBankIter iter(e) ;
           iter.is_valid() ;
           ++iter)
        {
          double sumenergy = e->get_energy(iter);
          double sum2energy = sumenergy*sumenergy;
          // Decode channel, and re-encode using EventClass parent
          //   because bank does not give access to channel
          MapClass::EventKey ek = _m.makeEventKey(e->get_we(iter),e->get_phi(iter),
               e->get_eta(iter),e->get_pmt(iter));
          MapClass::CalibKey key = _m.EventKeyToCalibKey(ek);
          _calibdata[key].sum += sumenergy;
          _calibdata[key].sum2 += sum2energy;
          _calibdata[key].noent++;

        }

      }
 

      if(name == "LEDD") {
        ConstHandle<EventClass2>   g(p_bank);
        for (EventClass2::ConstGrandBankIter iter(g) ;
             iter.is_valid() ;
             ++iter)
          {
                  < Analysis code for LEDD>
          }
        }

      }

    All the EventClass's must obviously be StorableBank's. The accumulate method for
    GenericDMode3 uses a pointer to an EventClass as an argument rather than a pointer
    to a record in the EventClass. This allows the user class to handle any differences in EventClass
    iterators while the template need only loop over the designated banks in the event and call the
    accumulate method for the banks indicated by the user class.

    6. At the end of the run the template begins the writing to the database. First it callse the calculate
    method of the user class which should contain any universal manipulations of the raw data to turn
    it into calibration data. It then calls the convert, valid, badchan methods based on the talk-to parameter
    DBStatusList and the values RAW, GOOD, BAD for the respective methods.

    7. (Required)  "RAW" data is written by the convert method in the user class.
 
    For example:

    std::auto_ptr<LEDCalib::CalibClass> LEDCalib::convert()
    {
      int thresh=0;
      std::auto_ptr<CalibClass> c(new CalibClass);
      Avgmap::const_iterator it = _calibdata.begin();
      while (it != _calibdata.end() )
        {
          double n  = it->second.noent;
          double s  = it->second.sum;
          double s2 = it->second.sum2;
          double response = s/n;
          double sigma = sqrt(s2/n-response*response);
          if (n>=thresh) {
            c->push_back(CalibClass::value_type(it->first,n,response,sigma,0.0,0.0));
            _testhist1.Fill(1.);
          }
          ++it;
        }
      return c;
    }

    This example shows how this method writes the results of the calculation into an instance of the
    CalibClass.

    8. (Required) Another write to the database is done using the validate method in the user class to
    filter out badly calibrated channels. The results are written to the database and labelled "GOOD".
    A simple example would look like the example in 6. but with a minimum number of entries required
    per channel, i.e. thresh=10.

    9. (Required) Another write to the database is done using the badchan method in the user class to
    filter out badly calibrated channels. The results are written to the database and labelled "BAD". This
    method is separate from the validate method because "BAD" does not always mean not "GOOD".
    A simple example would look like the example in 6. but with a maximum sigma allowed,
    i.e. sigma < 10 counts.

    10. The user class is used with the template in an AC++ program like the following.

    add(new GenericDMode<LEDCalib>("DCalibModule", "DCalib for a consumer program" ));

    Multiple instances for multiple user classes can be declared in one executable.