The parameter container factories in HYDRA

by Ilse Koenig, GSI Jan 30, 2002

Introduction

Different versions (different contexts) for some parameter containers valid for the same run (example RichAnaMinimumBias and RichAnaNormalBias for the RICH analysis parameters) are actually not supported in the HYDRA code. After a discussion with people from all detector groups, the following decisions were taken in an analysis meeting:
  1. Parameter containers with different contexts have different names. Names are (eventually) concatinated as
    "Original container name" + "_" + "Context name"
    Having different names, they can be read and written from/to the same ROOT file without the need to change the version management. Each parameter container has a default context. As long as a parameter container does not support deffferent context, the default context is an empty string. When implementing differend contexts, one is choosen as default (e.g. RichAnaNormalBias for the RICH analysis parameters). For backward compatibility the name of the parameter container is the original container name and not the concatinated name.
  2. The user sets eventually the context in the macro (by default the parameter container initialized for the default context) and the analysis has to guarantee, that different tasks using the same container cannot create different versions.
  3. Different, but somehow related containers may share the same context and the code must guarantee, that the context is identical in all these containers.
Manuel Sanchez made the proposal to create all parameter containers via container factories. These factories provide a unique place to store the actual contexts and to streamline existing code.
Each library gets its own factory, which is created automatically, when the library is loaded into ROOT. The runtime database owns a list with all factories.

 

New class HContFact: base class for all container factories

(See .../base/runtimedb/hcontfact.h)
class HContainer : public TNamed {
private:
  HContainer();
protected:
  TList* contexts;       // available contexts for this parameter container
  TString actualContext; // actual context set by the user
public:
  HContainer( const char*, const char*, const char*);
  ~HContainer();
  void addContext(const char*);
  Bool_t setActualContext(const char* c);
  const char* getDefaultContext();
  const char* getActualContext() { return actualContext.Data(); }
  void print();
  TString getConcatName();
  const char* getContext();
  ClassDef(HContainer,0) // class for list elements in class HContFact
};

class HContFact : public TNamed {
protected:
  TList* containers;   // all parameter containers managed by this factory
public:
  HContFact();
  virtual ~HContFact();
  Bool_t addContext(const char* name);
  void print();
  HParSet* getContainer(const char*);
  virtual HParSet* createContainer(HContainer*) {return 0;}
protected:
  const char* getActualContext(const char* name) {
    return ((HContainer*)containers->FindObject(name))->getActualContext();
  }
  ClassDef(HContFact,0) // base class of all factories for parameter containers
};
 
(See .../base/runtimedb/hcontfact.cc)
/////////////////////////////////////////////////////////////
//
//  HContFact
//
//  Base class of all factories for the parameter containers
//
/////////////////////////////////////////////////////////////

#include "hcontfact.h"
#include "hades.h"
#include "hruntimedb.h"
#include "TObjString.h"
#include <iostream.h>

ClassImp(HContainer)
ClassImp(HContFact)

HContainer::HContainer() { contexts=0; }
  // Default constructor

HContainer::HContainer(const char* name, const char* title,
                       const char* defContext)
           : TNamed(name, title) {
  // Constructor
  // Arguments:  name       = name of the corresponding parameter container
  //             title      = title of this parameter container
  //             defContext = default context of this parameter container
  contexts=new TList;
  addContext(defContext);
  actualContext="";
}

HContainer::~HContainer() {
  // Destructor deletes the list of accepted contexts
  if (contexts) {
    contexts->Delete();
    delete contexts;
  }
}

void HContainer::addContext(const char* name) {
  // Adds a context to the list of accepted contexts
  TObjString* c=new TObjString(name);
  contexts->Add(c);
}

Bool_t HContainer::setActualContext(const char* c) {
  // The function sets the actual context for the container, if it is in the list of
  // accepted contexts. When the actual context was already set before, it prints a warning
  // and ignores the second setting.
  // The function returns kFALSE, when the context is not in the list.
  if (contexts->FindObject(c)) {
    if (actualContext.IsNull()) actualContext=c;
    else Warning("addContext",
                 "Actual context of parameter container %s already defined as %s",
                 GetName(),actualContext.Data());
    return kTRUE;
  }
  return kFALSE;
}

const char* HContainer::getDefaultContext() {
  // Returns the default context
  return ((TObjString*)contexts->At(0))->String().Data();
}

void HContainer::print() {
  // prints the name, title of the container together with the actual context set
  // or all possible contexts, when the actual context was not set
  cout<String().IsNull()) cout<<"     \"\"";
      else cout<<"     "<String();
      if (i==0) cout<<"\t default";
      cout<<"\n";
      i++;
    }
  }
}

TString HContainer::getConcatName() {
  // Returns the name of the parameter container used in the constructor and the
  // runtime database.
  // When the parameter container supportes different contexts (not only an empty string)
  // and the actual context set is not the default context, the new name of the parameter
  // container is concatinated as
  //      original container name  +  _  +  actualcontext
  TString cn=fName;
  if (!actualContext.IsNull() && actualContext!=((TObjString*)contexts->At(0))->String()) {
    cn+="_";
    cn+=actualContext;
  }
  return cn;
}

const char* HContainer::getContext() {
  // return the actual context, if set, or the default context
  if (!actualContext.IsNull()) return actualContext.Data();
  else return getDefaultContext();
}

//------------------------------------------------------------------

HContFact::HContFact() : TNamed() {
  // Constructor creates a list to store objects of type HContainer
  containers=new TList;
}

HContFact::~HContFact() {
  // Destructor deletes the container list and its elements
  containers->Delete();
  delete containers;
}

Bool_t HContFact::addContext(const char* name) {
  // Set the actual context in all containers, which accept this context
  HContainer* c=0;
  Bool_t found=kFALSE;
  TIter next(containers);
  while ((c=(HContainer*)next())) {
    if (c->setActualContext(name)) found=kTRUE;
  }
  return found;
}

HParSet* HContFact::getContainer(const char* name) {
  // Returns the pointer to the parameter container in the runtime database
  // If this parameter container does not yet exit, it calls the function
  // createContainer(HContainer*), which is implemented in the derived classes
  // and calls the corresponding constructor. Then the pointer it added in the
  // runtime database.
  HContainer* c=(HContainer*)(containers->FindObject(name));
  HParSet* cont=0;
  if (c) {
    TString cn=c->getConcatName();
    HRuntimeDb* rtdb=gHades->getRuntimeDb();
    if (!(cont=rtdb->findContainer(c->getConcatName().Data()))) {
      if (strlen(c->getActualContext())==0) c->setActualContext(c->getDefaultContext());
      cont=createContainer(c);
      if (cont) rtdb->addContainer(cont);
      else Error("getContainer(const char* name)","Container %s not created",name);
    }
  }
  return cont;
}

void HContFact::print() {
  // Loops over all containers in the list and calls their print() function
  cout<<"---------------------------------------------------------------------------"<<"\n";
  cout<print();
}

 

Example HRichContFact: container factory for RICH

(See .../rich/hrichcontfact.h)
class HRichContFact : public HContFact {
private:
  void setAllContainers();
public:
  HRichContFact();
  ~HRichContFact() {}
  HParSet* createContainer(HContainer*);
  ClassDef(HRichContFact,0) // Factory for parameter containers in libRich
};
 
(See .../rich/hrichcontfact.cc)
/////////////////////////////////////////////////////////////
//
//  HRichContFact
//
//  Factory for the parameter containers in libRich
//
/////////////////////////////////////////////////////////////

#include "hrichcontfact.h"
#include "hruntimedb.h"
#include "hrichanalysis.h"
#include "hrichcalpar.h"
#include "hrichcorrelatorpar.h"
#include "hrichdigitisationpar.h"
#include "hrichgeometrypar.h"
#include "hrichmappingpar.h"
#include <iostream.h>

ClassImp(HRichContFact)

static HRichContFact gHRichContFact;  // instantiated when libRich is loaded

HRichContFact::HRichContFact() {
  // Constructor (called when the library is loaded)
  fName="RichContFact";
  fTitle="Factory for parameter containers in libRich";
  setAllContainers();
  HRuntimeDb::instance()->addContFactory(this);
}
//============================================================================

//---------------------------------------------------------------------------- 
void HRichContFact::setAllContainers() {
  // Creates the Container objects with all accepted contexts and adds them to
  // the list of containers for the Rich library.
  HContainer* ca=new HContainer("RichAnalysisParameters",
                                "Rich Analysis Parameters",
                                "RichAnaNormalBias");
  ca->addContext("RichAnaMinimumBias");
  ca->addContext("RichAnaHighBias");
  containers->Add(ca);

  containers->Add(
     new HContainer("RichCalPar",
                    "Rich Calibration Parameters",
                    ""));

  HContainer* cc=new HContainer("RichCorrelatorParameters",
                                "Rich Correlator Parameters",
                                "RichCorrSharpCut");
  cc->addContext("RichCorrWideCut");
  cc->addContext("RichCorrSharpPhiWideThe");
  containers->Add(cc);

  HContainer* cd=new HContainer("RichDigitisationParameters",
                                "Rich Digitisation Parameters",
                                "RichDigiNoiseOn");
  cd->addContext("RichDigiNoiseOff");
  containers->Add(cd);

  containers->Add(
     new HContainer("RichGeometryParameters",
                    "Rich Geometry Parameters",
                    ""));

  containers->Add(
     new HContainer("RichMappingParameters",
                    "Rich Mapping Parameters",
                    ""));
}
//============================================================================

//---------------------------------------------------------------------------- 
HParSet* HRichContFact::createContainer(HContainer* c) {
  // Calls the constructor of the corresponding parameter container.
  // For an actual context, which is not an empty string and not the default context
  // of this container, the name is concatinated with the context.
  const char* name=c->GetName();
  if (strcmp(name,"RichAnalysisParameters")==0)
    return new HRichAnalysisPar(c->getConcatName().Data(),c->GetTitle(),c->getContext());
  if (strcmp(name,"RichCalPar")==0)
     return new HRichCalPar(c->getConcatName().Data(),c->GetTitle(),c->getContext());
  if (strcmp(name,"RichCorrelatorParameters")==0)
    return new HRichCorrelatorPar(c->getConcatName().Data(),c->GetTitle(),c->getContext());
  if (strcmp(name,"RichDigitisationParameters")==0)
    return new HRichDigitisationPar(c->getConcatName().Data(),c->GetTitle(),c->getContext());
  if (strcmp(name,"RichGeometryParameters")==0)
    return new HRichGeometryPar(c->getConcatName().Data(),c->GetTitle(),c->getContext());
  if (strcmp(name,"RichMappingParameters")==0)
    return new HRichMappingPar(c->getConcatName().Data(),c->GetTitle(),c->getContext());    
  return 0;
}

 

Change of class HRuntimedb

All changes are marked green.

class HRuntimeDb : public TObject {
private:
  static HRuntimeDb* gRtdb;  //!
protected:
  HRuntimeDb(void);
  TList* contFactories;    //! list of container factories
  ...
public:
  static HRuntimeDb* instance(void);
  ...
  Bool_t addParamContext(const char*);
  void printParamContexts();
  void addContFactory(HContFact*);
  HParSet* getContainer(Text_t*);
  HParSet* findContainer(const char*); 
  ...
(See .../base/runtimedb/hruntimedb.h for complete class definition)
 
...
HRuntimeDb* HRuntimeDb::gRtdb=0;

HRuntimeDb* HRuntimeDb::instance(void) {
   if (gRtdb==0) gRtdb=new HRuntimeDb;
   return gRtdb;
}


HRuntimeDb::HRuntimeDb(void) {
  // constructor creates an empty list for parameter containers
  // and an empty list of runs for the version management
  gRtdb=this;
  contFactories=new TList();
  containerList=new TList();
  runs=new TList();
  firstInput=0;
  secondInput=0;
  output=0;
  versionsChanged=kFALSE;
  currentRun=0;
  isRootFileOutput=kFALSE;
}

HRuntimeDb::~HRuntimeDb() {
  // destructor
  // deletes the list of runs and all containers
  closeFirstInput();
  closeSecondInput();
  closeOutput();
  if (containerList) {
    containerList->Delete();
    delete containerList;
  }
  if (runs) {
    runs->Delete();
    delete runs;
  }
  if (contFactories) {
    contFactories->Delete();
    delete contFactories;
  }
  gRtdb=0;
}



void HRuntimeDb::addContFactory(HContFact* fact) {
  // Adds a container factory to the list of factories
  if (!(contFactories->FindObject(fact->GetName()))) contFactories->Add(fact);
}

Bool_t HRuntimeDb::addParamContext(const char* context) {
  // Sets via the container factories the context of all parameter containers,
  // which accept this context 
  Bool_t found=kFALSE;
  TIter next(contFactories);
  HContFact* fact;
  while((fact=(HContFact*)next())) {
    if (fact->addContext(context)) found=kTRUE;
  }
  if (!found) Error("addParamContext","Unknown context");
  return found;
}

void HRuntimeDb::printParamContexts() {
  // Prints the context of all parameter containers, which can be created by
  // the container factories
  TIter next(contFactories);
  HContFact* fact;
  while((fact=(HContFact*)next())) fact->print();
}

HParSet* HRuntimeDb::getContainer(Text_t* name) {
  // The function loops over the container factories to find the corresponding container
  // with the give name and its context.
  // The name is the original name of the parameter container without the concatination
  // with the context.
  // The factory checks, if the container exists already in the runtime database. Otherwise
  // it will be created and added by the factory.    
  // The function returns a pointer to the container or NULL, if not created.
  TIter next(contFactories);
  HContFact* fact;
  HParSet* c=0;
  while(!c && (fact=(HContFact*)next())) {
    c=fact->getContainer(name);
  }
  if (!c) Error("getContainer","Container %s not created!",name); 
  return c;
}

HParSet* HRuntimeDb::findContainer(const char* name) {
  // Returns a pointer to the container called by name
  // The name is the original name of the parameter container eventually concatinated with
  // a non-default context.
  return (HParSet*)(containerList->FindObject(name));
}

...
(See .../base/runtimedb/hruntimedb.cc for complete implementation)

Change of class HParSet (base class for all parameter containers)

.../base/runtimedb/hparset.h
All changes are marked green.

class HParSet : public TNamed {
protected:
  ...
  TString paramContext;    //! Context/purpose for conditions
  TString author;          //! Author of parameters
  TString description ;    //! Description of parameters
public:
  HParSet(const char* name="",const char* title="",const char* context="");
  ...
  void setParamContext(const char*);
  const char* getParamContext() const { return paramContext.Data(); }

  void setAuthor(const char* s) {author=s;}
  const char* getAuthor() const { return author.Data(); }

  void setDescription(const char* s) {description=s;}
  const char* getDescription() const { return description.Data(); }

  ClassDef(HParSet,2) // Base class for all parameter containers
};
(See .../base/runtimedb/hparset.cc for implementation)
The old default constructor without any arguments has been changed to a constructor with three arguments:
name, title, default context
The constructors of all parameter containers must be changed, but existing code still compiles as long as the corresonding container factory is not added.

The class has gotten three additional data-elements:

  1. paramContext
  2. author
  3. description
The class version was incremented and an customized streamer garantees backward compatibility for old parameter ROOT files.
The print() functions prints them out to the screen additionally to the original printed informations (versions, flags).

 

Example for new constructor: HRichCalPar

(See .../rich/hrichcalpar.h)
class HRichCalPar : public HRichParSet {
public:
  HRichCalPar(const char* name="RichCalPar",
              const char* title="Rich Calibration Parameters",
              const char* context="");
  ... 
 
(See .../rich/hrichcalpar.cc)

HRichCalPar::HRichCalPar(const char* name,const char* title,
                         const char* context)
                : HRichParSet(name,title,context) {
  m_pReadParam = NULL;

  setSetup(6, 96, 96); //default setup
  // setSetup(6, 64, 64); //default setup old
  setCellClassName("HRichCalParCell");
}

 

Change in the init function of all tasks

The init function can be simplified:
Because the function
HParSet* HRuntimeDb::getContainer(Text_t* name)
takes care, that a non-existing parameter container is created automatically via the factory, the blue marked lines, typically found in the old code, are obsolete. They should be removed, because they create always a container with the default context, which might be wrong.
  HRuntimeDb* rtdb=gHades->getRuntimeDb();
  fCalPar = rtdb->getContainer("RichCalPar");
  if (!fCalPar) {
    fCalPar= new HRichCalPar;
    rtdb->addContainer(fCalPar);
  }

 

Example for the creation of a ROOT file containing a parameter container with two different contexts

This macro creates the RICH analysis parameter container twice with two different contexts. The first version is created via the factory after setting the non-default context. The second version with the default context must be created expliciatly, because the container factory does not allow to create a second version.
The containers are initialized from Oracle (actually only test versions) and written out to a ROOT file.
{
  Hades* myHades=new Hades;
  HRuntimeDb* rtdb=gHades->getRuntimeDb();
  HSpectrometer* spec=gHades->getSetup();

  HRichDetector* rich=new HRichDetector;
  spec->addDetector(rich);

  HParOraIo* inp1=new HParOraIo;
  inp1->open();
  rtdb->setFirstInput(inp1);

  HParRootFileIo* output=new HParRootFileIo;
  output->open("tmp.root","RECREATE");
  rtdb->setOutput(output);

////////////////////////////////  RICH  ////////////////////////////////////////

  rtdb->addParamContext("RichAnaHighBias");
  HRichAnalysisPar* p1=(HRichAnalysisPar*)(rtdb->getContainer("RichAnalysisParameters"));

// Second version with defaults: context = RichAnaNormalBias
  HRichAnalysisPar* p2=new HRichAnalysisPar;
  rtdb->addContainer(p2);

  if (!rtdb->initContainers(1007785790)) return;
  p1->print();
  p2->print();

////////////////////////////////////////////////////////////////////////////////

  rtdb->saveOutput();
  rtdb->print();

  delete myHades;
}