| GSI | HADES | HADES@Catania | HADES@Cracow | HADES@Giessen | HADES@GSI | HADES@Santiago | HADES@TUM | CERN |

HADES Logo

Initialization in the HADES analysis program

status report by Ilse Koenig
GSI June 15, 2000

(documentation for HYDRA version v401 and below: initialization_1999)

| Computing | HYDRA | class docu | CVS | Oracle | ROOT |

Content:





Introduction


To run an analysis, initialization parameters are needed. Each task needs special sets of parameters which are stored in container classes in memory. Some tasks might share the same container (e.g. geometry parameters are needed by the digitization, the tracking, the event display, etc.). The parameters are valid for very different time scales. Once a detector is built, some parameters are fixed for the whole lifetime of this detector (e.g. number of wires in a given layer of a MDC). Containers holding such data must be initialized only once in an analysis. Some parameters might change seldom, others quite often (e.g. calibration parameters). In these cases, a reinitialization might be needed during the analysis of several event files (To save memory and time, each container is created only once). A task might change parameters during the analysis of an event file and it is then necessary to save these data before a reinitialization. All initialization data are managed by the so-called runtime database which can be saved in a ROOT file for further use.

Also, during daily work one often launches an analysis only for a subset of a detector, that means only for some modules of a detector. The parameter containers hold then only the data for the actual setup, not for the complete one.

Ultimately, all information should be permanently stored in the HADES Oracle database. Actually database tables for the MDC, RICH and TOF up to calibration level, geometry information for simulation and analysis, the runs, the logbook and slow control of the magnet exist. The design of these tables is closely related to the technical structure of the detectors, as they hold not only parameters needed by the analysis, but also information about the electronic setup and the cabeling. Some tables have a version management to keep track of changes. One version is valid for one or more event files characterized by the run id. For the same run, even several versions of parameters might exist, e.g. first version of calibration parameters, a second version after a more detailed analysis, etc. The tables were designed under the aspect to minimize the storage of redundant information without loosing too much performance. As a result the parameters in one container are often stored in several tables and sometimes a quite complicated query is needed.

For a more detailed description of the table design in the database see the document Table Layout for HADES MDC Calibration Tables by Michael Dahlinger.

Approved parameter sets are stored permanently in the Oracle DB by authorized persons (password needed!). For daily work one needs an additional storage medium for the runtime database: ROOT files have been foreseen to store parameter sets as objects. For several reasons a local version management has been implemented:
  1. To avoid the need of many different parameter files
  2. To avoid to keep the connection to Oracle open all the time during a long analysis of many files
  3. To be sure to have all initialization parameters available e.g. for running a batch job overnight
  4. To distribute the parameters within the collaboration when the direct access to Oracle is not possible or the newest data are not yet written to the database
To have an easy way to edit or create parameters, an interface to ascii files has been implemented as well. The aim was not to store all temporary data in ascii files, but to have the possibility to read parameters for the first time or to write a special parameter set out to an ascii file, to change it and then to use this file for initialization.

The initialization concept was developed for the MDC as a stand-alone program and then integrated into the framework. Some basic parts are still missing, e.g. the storage of information in a log file. Not all features are (yet) needed and implemented for the different detectors.


Detector setup


The HADES spectrometer is represented through the class HSpectrometer which contains a list of detectors. A pointer to this object is stored in the class Hades. Each detector has its own class ( HMdcDetector, HTofDetector, ...). They are all derived from a common base class HDetector. Each detector consists of one or more modules placed in the different sectors, except the START detector consisting of two modules placed directly in the Cave. The actually used detector configuration is created via a ROOT macro.

In the following example an MDC detector is instantiated with modules 1 and 2 in the 3rd and 6th sector:
   HSpectrometer* spec=gHades->getSetup();
   HMdcDetector mdc;
   Int_t mod[4]={1,1,0,0};
   mdc.setModules(2,mod);
   mdc.setModules(5,mod);
   spec->addDetector(&mdc);
A 0 in the Int_t array mod means that the module at this position is absent, the first parameter in the function setModules(Int_t,Int_t*) specifies the sector (starts counting with 0) or -1 for a detector not devided into sectors as e.g the START detector.


Runtime database


The runtime database is not a database in the classical sense. Instead, it is a parameter manager. It knows the I/Os defined by the user in the macro and all parameter containers needed for the actual analysis. It manages the automatic initialization and saving to an output and contains, after all initialization and saving is done, a complete list of runs and related parameter input and output versions.
It is represented by the class HRuntimeDb and instantiated in the constructor of the class Hades.
It holds two lists: the list of parameter containers, and the list of runs and related parameter versions. The containers can be initialized automatically from one or two inputs, and written out to one output. Possible inputs/output are If a container can not or only partly be initialized from the first input, the missing information is taken automatically from the second input. Any combination of inputs is possible. In case of a ROOT file, the first input and the output might be identical.

Parameter containers

The parameters are stored in parameter containers which are accessible in the runtime database via their names. To avoid unnecessary initialization and storage, the design of these containers should consider the following aspects: All container classes are derived from a common base class HParSet.They are instantiated in the program in the init() functions of the detectors and the tasks (not in the constructors!) and added to the list of containers in the runtime database. However, it is also possible to create a container explicitely in a macro, to initialize it and to add it to the list. This feature can be used, when a container should be initialized from a special input and not automatically from one of the inputs defined in the runtime database.
Parameters used privately in a task and calculated completely from other parameters should not be added to the list of parameter containers in the runtime database. Instead, their init() function should be called explicitly in the reinit() function of the task (called automatically after the initialization of all containers for each run).
Each container has overloaded init() and write() functions for the parameter I/O, functions to support the version management, to clear the data and to print information. (see Description of functions provided for parameter I/O).
Each container has additional data members to support input/output:
Bool_t status
If this flag is true, the container is never reinitialized again automatically.
Bool_t changed
This flag is true after the (re)initialization or is set true by tasks creating new parameters which should be saved before a reinitialization. Then the container will be written to the output (if defined) and the flag is reset.
Int_t version[3]
version[1] is the version of the parameters taken from the first input, version[2] the one taken from the second input used for the actual initialization of the container (version[0] is not used).

There is no common structure for the parameter containers. The containers for the MDC and START are implemented as pointer arrays forming a tree like structure. Shower and RICH use ROOT trees to store the parameters.

Example for a pointer array structure:
The MdcCalPar container for the calibration parameters is an array of 6 pointers (for the 6 sectors) pointing to arrays of 4 pointers ( for the 4 modules) pointing to arrays of 6 pointers again (for the 6 layers) pointing to arrays of pointers of variable size, the size depending on the number of wires in the corresponding MDC layer. These last arrays hold the pointers to the objects which contain the real parameters - slope and offset - for one wire.
This structure is the most compact way to store the parameters: The layer array of a missing module is not created and the corresponding pointer is 0.
Containers implemented this way can be written out to a ROOT file and read in again using the default ROOT streamer function. Although it is not possible to plot the data directly, the class provides a function to fill the parameters on request into a ROOT NTuple, whose Draw() function can then be used.

Input/Output

The class design for I/O was developed according to the following user requests:
For each type of I/O a set of classes is implemented, with the main class deriving from the base class HParIo and containing functions to open and close the I/O. The I/O class is instantiated in a macro and a pointer to it is stored as first/second input or output in the runtime database. Internally this class has as a data member a pointer to a connection class. It holds also a list of detector I/O classes, which are instantiated automatically according to the actual setup when a connection is opened, and deleted when the connection is closed. All detector classes are derived from the base class HDetParIo, which itself is derived from TNamed. The detector classes have public member functions to initialize and write their respective containers.

I/O Type I/O base class Connection Class Detector Io Base Class Detector Io Classes
Oracle HParOraIo HOraConn HDetParOraIo HMdcParOraIo, HRichParOraIo ...
ROOT file HParRootFileIo HParRootFile HDetParRootFileIo HMdcParRootFileIo, HRichParRootFileIo, ...
Ascii file HParAsciiFileIo fstream HDetParAsciiFileIo HMdcParAsciiFileIo, HStartParAsciiFileIo

RICH and Shower use a different ascii I/O. See hadascii documentation by W.Przygoda.

A container itself does not know the concrete type of I/O used. It receives from the runtime database a pointer of the base type HParIo. With the name of the detector I/O class (e.g. for MDC: "HMdcParIo") it then gets via the list of I/O objects access to the init() and write() functions.
For more detailed information about the member functions see Description of functions provided for parameter I/O.

Version management in Oracle

Oracle does not know parameter containers needed by the analysis. The data are stored here not as 'objects' but in sometimes many different tables related to each other via so-called referential constraints. The tables were designed under the aspect to minimize the storage of redundant information to guarantee data consistency and to save storage space. (Redundancy should be avoided because it could spoil the data integrity, but in very rare cases is needed to gain performance.) For time dependent information a version management is needed which fulfills the following requirements: As a result the following concept has been developed and tested:
Time dependend entries have a time stamp (date + time with the precision of one second) in form of three columns (Format: DATE):
valid_since First date when the entry is valid.
valid_until Last date when the entry is still valid.
invalid_since Date when the entry is replaced by a correct entry or a better version in case of e.g. calibration parameters and therefore gets invalid.
For run related parameters valid_since can be the run_start of one run and valid_until the run_stop of the same or another run. Parameters as cabeling are changed between runs or even beamtimes. Valid_since is then the time when the cabeling was changed and the actual cabeling has the value 'year 4000' for valid_until. These two columns define the validity time range and refer always to dates in the experiment.
All valid entries have invalid_since = year 4000. If for the same validity time range an entry (or several new entries by splitting this time range into several subsets) is made, invalid_since of the old entry is set to the actual time the insert is made (time on the Oracle server) and the new entry has invalid_since = year 4000.
Time dependent information from different tables not related to each other are joined by their timestamps.
To make the data retrievel more easy, a set of views exist in Oracle (A view is a stored SQL statement). These views provide the valid data often from many tables for a certain point in time set before via a stored package procedure (compiled program stored in Oracle).

For the analysis interface to Oracle, this has the following consequences:

There is no version number in Oracle for a parameter container. The set of data provided by a view intended to fill a parameter container is valid for a certain time range, namely the maximum common time range of the different time-dependent parameters of this set. All runs with a start date in this time range use the same parameters. Once a parameter container is initialized it has to be reinitialized only for a run with a start date outside this validity time range.
The 'version' of the parameter container in the analysis is the id of the run the container is (re)initialized for. This has some consequences described later in the context of the ROOT file output.

Remark: The formerly used concept with version numbers instead of time stamps, had be be abandoned because of its lack of flexibility and bad performance (at least a factor 100 slower).

Version management in the ROOT file

In the ROOT file parameter containers are stored as objects. Every time an object is written it gets automatically a new version incrementing the former version by 1, starting with 1 in each newly created file. By default the Read() or Find() functions provided by ROOT read the object with the highest version. A retrieval of another version is possible by adding ";version number" to the name of the parameter container. The information 'which run corresponds to which version' of each parameter container must be stored in the ROOT file together with the data.

Version management in the runtime database

An important request for the implementation was to avoid reading the same information several times and storing them several times in an output. Because a container is initialized and written always as a whole object, this has the following consequences:
For the ascii I/O a version management is not foreseen, however.

The runtime database (class HRuntimeDb) has as a data element a list of objects of type HRun, one object for each run to be analyzed. HRun is derived from TNamed and the object name is the run id as string.
In the event loop, the list of runs is filled sequentially. The event file can be a listmode data file (.hld) or a Root file (.root). The datasource retrieves the run_id from the event header and signalizes, when a new run starts. The event loop then calls the function
    HRunTimedb::initContainers(Int_t runId,Int_t refRunId=-1,const Text_t* fileName="")
which adds a new object of type HRun to the list.
Remark: Since HYDRA version v501 dating from 30/3/2000 the data sources (classes HldFileSource or HRootSource) hold the list of event files, not the runtime database as in earlier releases.
Each HRun object has two data elements:
A list of objects of type HParVersion (derived from TNamed), one for each container.
Each object has the same name as the container it belongs to and has two data elements: an array for the two possible input versions and the output version (for a ROOT file as output).
The run id of a reference run (optional)
If this number is not -1, then all parameter containers are initialized with the same parameters as for the reference run.
The initialization is done inside the event loop, except for some containers which hold information needed to create the categories or other parameter containers. These containers are created and initialized only once in the init() functions of the detectors.
When the runtime database is asked to initialize the containers providing the run id and maybe additionally a reference run id, the following actions take place: If no error occured the event loop starts analyzing the events of this run, otherwise the loop exits.

After all event files have been analyzed, the runtime database contains a list in which for all runs the input and output versions of all containers are stored. This list is also written to the ROOT file (the input versions are not written, only the output versions) together with the setup of the spectrometer. The output ROOT file is a snapshot of the actual runtime database and can be used again for initialization. (See Example1)

Additionally, the runtime database has a member function readAll() which can be used to fill it directly without passing via the Hades event loop. After defining the parameter I/O, the setup, all parameter containers and the list of runs (with the addRun() function), a call to readAll() fills and saves the runtime database. This serves to create in one go a ROOT file containing all parameters needed for a full analysis, connecting only once to the database. Such a ROOT file can also be sent to institutes that have no direct access to Oracle (see Example2).

The run id is a very unhandy number (creation date expressed in number of seconds since 01/01/1970 GMT), but the Oracle interface class HOraInfo provides a function which returns the run id corresponding to an hld filename.


Not all data might be available from the Oracle database or the database might not have been updated with the newest data. (However settled data should be stored as soon as possible!) Also a ROOT file written earlier might not contain the complete information one needs now. Therefore two inputs have been foreseen. This feature can be used, e.g. to combine the newest information contained in a ROOT file (e.g. new calibration parameters) with information retrieved from Oracle or from another ROOT file. The output Root files contains the merged information. One simply has to use the ROOT file with the newest information as first input and the other one as second input in the runtime database. The first input always wins!
Additional containers or additional versions needed for additional runs can be added to an already existing ROOT file by using it as first input, as well as output. The file must be opened in UPDATE mode.

However updating an existing ROOT file can cause problems and should be made very carefully:
If Oracle is used as first input, a new version of a parameter container and run already existing in the ROOT output file would not been written out again automatically.
One must force the output manually by calling the write-function of the parameter container. This output version must be set in the version table.
In this case it is safer to open a new ROOT file and to merge this file with the other one in a second step

By default, the newest version of a container valid for a given event file would be taken from Oracle or a ROOT file. Via the member function setInputVersion(...) another version can be set explicitly in a macro (see Description of functions provided for parameter I/O).

It is not possible to merge the information of more than two ROOT files in one step, nor to use many ascii files in the automatic initialization. However, in a macro the information can be filled stepwise into a ROOT file to end up with a maximum of two needed inputs. (see Example3).


Examples (Macros)


The following macros show examples of how to create and fill a runtime database using different parameter sets for different runs of event data.


Example1:
MDC analysis with initialization from Oracle, saved in a ROOT file

This macro calibrates MDC data from two event files. It reads the initialization parameters from Oracle and saves them in a ROOT file named "oraPar.root". The calibration parameters are different for the two event files. ( The code is colored. )

Create a Hades object. This instantiates the spectrometer class and the runtime database.
   Hades* myHades = new Hades;

Create the detector and its setup.
Here the Mdc consists only of the second module in the 3rd sector.
   HSpectrometer* spec=gHades->getSetup();
   HMdcDetector* mdc=new HMdcDetector;
   int mod[4]={0,1,0,0};
   mdc->setModules(2,mod);
   spec->addDetector(mdc);

Create the Oracle I/O and open the connection (here default connection as user Hades with read-only access).
   HParOraIo* ora=new HParOraIo;
   ora->open();
   if (!ora->check()) {
     printf("no connection to Oracle\n");
     return;
   }

Get a pointer to the runtime database and set the Oracle I/O as first input.
   HRuntimeDb* rtdb=gHades->getRuntimeDb();
   rtdb->setFirstInput(ora);

Create the ROOT file I/O and open a file with write access.
Define it as output in the runtime database.
   HParRootFileIo output;
   output.open("oraPar.root","RECREATE");
   rtdb->setOutput(&output);

Define the datasource and the unpacker.
   HldFileSource *source = new HldFileSource;
   source->addUnpacker(new HMdcUnpacker(513, FALSE));
   gHades->setDataSource(source);

Set the file directory and add the event files.
   source->setDirectory("/d/hades/nov99");
   source->addFile("n99_085.hld");
   source->addFile("n99_086.hld");

Set the reconstruction tree.
   HTaskSet *mdctasks = new HTaskSet("MDC","MDC");
   mdctasks->connect(new HMdcCalibrater1("mdc.cal1","mdc.cal1"));
   gHades->getTaskSet("real")->connect(mdctasks);

Call Hades::init(), which makes the following:
  1. adds the first run in the runtime database
  2. invokes the initialization of all detectors
    Here the containers "MdcGeomStruct" is created and initialized. It stores the number of the wires in each layer of the MDC module(s). This information is needed to create the categories (except Raw) and most other parameter containers. This container is initialized only once and is not allowed to change during the analysis.
  3. invokes the initialization of the datasource, which calls the init() functions of all unpackers
    Here the parameter container "MdcRawStruct" is instantiated and directly initialized, because it contains the information on how many motherboards and TDC channels each module has. This information is needed to create the Raw category as well as the lookup table "MdcEvReadout" for the unpacker. For technical reasons this container has no read function, but is initialized from the lookup table "MdcMboReadout" for the opposite direction by copiing. Therefore the container "MdcMboReadout" is added to the list before the other one.
  4. invokes the initialization of all tasks
    In the init() function of the MDC calibrater the parameter containers for the calibration parameters and the lookup table raw->cal are instantiated.
At this point only the containers "MdcGeomStruct" and "MdcRawStruct" are initialized. All other containers are created, but not yet initialized. This will be done in the event loop.
   printf("gHades->init()\n");
   if (!gHades->init()) {
     printf("Error\n");
     return;
   }

Define the output file and make the tree.
   Int_t compLevel = 1;
   gHades->setOutputFile("testcalibrater.root","RECREATE","Nov99",compLevel);
   gHades->makeTree();

Start the event loop.
   printf("Event loop\n");
   gHades->eventLoop();

Save the parameter output (second version of calibration parameters was not yet written out) and get an info of the runtime database.
   rtdb->saveOutput();
   rtdb->print();

Delete the Hades objects.
The Oracle connection and the parameter ROOT file are closed (Information in the runtime database not yet stored would be written before). The event tree is stored and then all resources are freed.
   delete myHades;

The function call rtdb->print() leads to the following output on the screen, showing e.g. 2 different input and output versions for the calibration parameters:
-----------------------------------------------------------
---------  actual containers in runtime database  ---------
MdcGeomStruct           Mdc parameters for geometry structure
MdcRawStruct            Mdc parameters for hardware structure
MdcMboReadout           Mdc Mbo readout addresses
MdcEvReadout            Mdc subevent readout addresses
MdcCalParRaw            raw calibration parameters for Mdc
MdcLookupGeom           Mdc lookup table from raw to cal1
-----------------  runs, versions  -----------------
run id
  container                 1st-inp    2nd-inp     output
run: 944445810
  MdcGeomStruct           944447160         -1          1
  MdcRawStruct            944447160         -1          1
  MdcMboReadout           944445810         -1          1
  MdcCalParRaw            944445810         -1          1
  MdcLookupGeom           944445810         -1          1
run: 944447160
  MdcGeomStruct           944447160         -1          1
  MdcRawStruct            944447160         -1          1
  MdcMboReadout           944445810         -1          1
  MdcCalParRaw            944447160         -1          2
  MdcLookupGeom           944445810         -1          1
---------------------  input/output  ----------------------
first Input:
Oracle-Database: db-hades.gsi.de    Username: hades
detector I/Os:  HMdcParIo HMdcParIo HMdcParIo

second Input:
 none

Output:
HParRootFile**          oraPar.root
 HParRootFile*          oraPar.root
  KEY: HMdcGeomStruct   MdcGeomStruct;1 Mdc parameters for geometry structure
  KEY: HMdcRawStruct    MdcRawStruct;1  Mdc parameters for hardware structure
  KEY: HMdcMboReadout   MdcMboReadout;1 Mdc Mbo readout addresses
  KEY: HMdcCalParRaw    MdcCalParRaw;2  raw calibration parameters for Mdc
  KEY: HMdcCalParRaw    MdcCalParRaw;1  raw calibration parameters for Mdc
  KEY: HMdcLookupGeom   MdcLookupGeom;1 Mdc lookup table from raw to cal1
  KEY: HMdcDetector     Mdc;1
  KEY: HRun     944445810;1
  KEY: HRun     944447160;1
detector I/Os:  HSpecParIo HMdcParIo
-----------------------------------------------------------

Example2: Initialization of the same parameter containers and runs from Oracle as in example 1, but this time without an analysis.

   Hades* myHades = new Hades;
   HSpectrometer* spec=gHades->getSetup();
   HMdcDetector* mdc=new HMdcDetector;
   int mod[4]={0,1,0,0};
   mdc->setModules(2,mod);
   spec->addDetector(mdc);

   HParOraIo* ora=new HParOraIo;
   ora->open();
   if (!ora->check()) {
     printf("no connection to Oracle\n");
     return;
   }

   HRuntimeDb* rtdb=gHades->getRuntimeDb();
   rtdb->setFirstInput(ora);

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

Here the runs have to be added to the runtime database, but one does not know the corresponding run ids. The function Int_t HOraInfo::getRunId(Text_t*) takes the hld filename and returns the corresponding run id.
   HOraInfo* oraInfo=ora.getOraInfo();
   Int_t runId=oraInfo->getRunId("n99_085.hld");
   HRun* run=rtdb->addRun(runId);
   runId=oraInfo->getRunId("n99_086.hld");
   HRun* run=rtdb->addRun(runId);

Now the parameter containers "MdcGeomStruct" and "MdcRawStruct" must be created and initialized.
   HMdcGeomStruct* pargeom=new HMdcGeomStruct;
   rtdb->addContainer(pargeom);

   HMdcRawStruct* parraw=new HMdcRawStruct;
   rtdb->addContainer(parraw);

   if (!rtdb.initContainers(runId)) {
     rtdb->closeFirstInput();
     rtdb->closeOutput();
     return;
   }

Now all other parameter containers can be created.
   HMdcMboReadout* readout=new HMdcMboReadout;
   rtdb->addContainer(readout);

   HMdcCalParRaw* calpar=new HMdcCalParRaw;
   rtdb->addContainer(calpar);

   HMdcLookupGeom* lookup=new HMdcLookupGeom;
   rtdb->addContainer(lookup);

The next statement loops over all runs, initializes all containers and writes them to the output.
   if (!rtdb->readAll()) printf("********** ERROR **********\n\n");

Now print the runtime database info and delete the Hades object.
   rtdb->print();
   delete myHades;

This output ROOT file could be used for the analysis in example 1.

Example3: Initialization from several ascii files, saved in a ROOT file

The following example shows, what one has to do in a macro, if one needs for the analysis several versions of a container, but these versions have to be read from many different ascii files (or from many ROOT files).
In a first step a ROOT output file is created with the different versions inside. The version information must be stored explicitly. Then this ROOT file can be used as input in the further analysis.

Create Hades and define the setup.
   Hades* myHades = new Hades;
   HSpectrometer* spec=gHades->getSetup();
   HMdcDetector* mdc=new HMdcDetector;
   int mod[4]={0,1,0,0};
   mdc->setModules(2,mod);
   spec->addDetector(mdc);

Define the runs where a new initialization is needed.
   HRuntimeDb* rtdb=gHades->getRuntimeDb();
   rtdb->addRun(944445810);
   rtdb->addRun(944447160);
   rtdb->addRun(944450378);

Create an ascii I/O and open the first ascii file.
   HParAsciiFileIo* input=new HParAsciiFileIo;
   input->open("ascii1.tmp","in");

Create the ROOT file output.
   HParRootFileIo* output=new HParRootFileIo;
   output->open("manyfiles.root","RECREATE");
   rtdb->setOutput(output);

Create the containers and initialize them from the locally defined ascii input.
Write them to the output and store for the first run the output versions in the version table. (Because the MdcGeomStruct container is initialized only once and then stays constant, its output version is the same for all runs.)
Then close the input.
   Int_t version;
   HMdcGeomStruct* pargeom=new HMdcGeomStruct;
   rtdb->addContainer(pargeom);
   pargeom->init(input);
   version=pargeom.write(output);
   rtdb->setRootOutputVersion(944445810,"MdcGeomStruct",version);
   rtdb->setRootOutputVersion(944447160,"MdcGeomStruct",version);
   rtdb->setRootOutputVersion(944450378,"MdcGeomStruct",version);

   HMdcCalPar* calpar=new HMdcCalPar;
   rtdb->addContainer(calpar);
   calpar->init(input);
   version=calpar->write(output);
   rtdb->setRootOutputVersion(944445810,"MdcCalPar",version);

   input->close();

Now open the next ascii file, initialize the calibration parameter container, store the output version and close the input.
   input->open("ascii2.tmp","in");
   calpar->init(input);
   version=calpar->write(output);
   rtdb->setRootOutputVersion(944447160,"MdcCalPar",version);
   input->close();

Do the same for the third file.
   input->open("ascii3.tmp","in");
   calpar->init(input);
   version=calpar.write(output);
   rtdb->setRootOutputVersion(944450378,"MdcCalPar",version);
   input.close();


Now the version table and the setup information must still be stored in the output file.
   rtdb->writeVersions();
   rtdb->writeSetup();

   delete myHades;

The output file contains one version for the MdcGeomStruct container and three versions of calibration parameters. This file can be used now as input for the initialization.

Description of functions provided for parameter I/O


For complete list of all classes and their functions see Class index

1. Definition of the setup (the detectors and the modules in each detector)

The spectrometer object is created in the constructor of the class Hades and is accessible via the function
HSpectrometer* Hades::getSetup()
e.g. HSpectrometer* spectrometer=gHades->getSetup();

Each detector must be created in a macro by calling the constructor of the detector class. The setup of each detector is defined with the member function
void HDetector::setModules(Int_t sec,Int_t* modules) .
This function takes the number of a sector (starting with 0) and a locally defined Int_t array with the information which modules are defined in the sector. (The size of this array must be the maximum number of modules this detector could have in a sector, e.g. 4 modules in a MDC per sector. An active module is defined by a number not equal 0.)

Each detector must be added to the list of detectors in the spectrometer class by the function
void HSpectrometer::addDetector(HDetector* detector)

e.g. // here Mdc with 2nd module in 3rd sector
HMdcDetector mdc;
Int_t modules[4]={0,1,0,0};
mdc.setModules(2,modules);
spectrometer->addDetector(&mdc);


The setup information must be written to the ROOT output file. Normally this is done automatically by the runtime database. However in some cases it has to be done explicitly in a macro, e.g. if a special output file should be created not defined as output in the runtime database. One of the following functions can be called, which take a pointer to an I/O as argument:
Bool_t HSpectrometer::write(HParIo* io)
Bool_t HDetector::write(HParIo* io)

2. Definition of the runs

The class HRuntimeDb holds the list of runs used for initialization. Is has some functions to manage this list:
void HRuntimeDb::addRun(Int_t runId,Int_t refId=-1) to add a run at the end of the list.
runId is the id number of the run to be added. If all parameters should be the same as for another run (which must be a part of this list), the id of this reference run must be provided as second argument refId.

To remove a run from the list one uses
void HRuntimeDb::removeRun(Text_t* id) with the id of this run given as string.

The complete list can be cleared with
void HRuntimeDb::clearRunList().

The run is accessible via
HRun* HRuntimeDb::getRun(Int_t id) or
HRun* HRuntimeDb::getRun(Text_t* "id")
and the actual run (current list pointer) by HRun* HRuntimeDb::getCurrentRun() .

3. Definition of input(s)/output

Each I/O must be created in a macro and the connection must be opened (with Bool_t open(...) returning kTRUE if successful) and closed (with void close() ) again later. To check if a connection is open each I/O has a function Bool_t check() and a function void print() to get some information on the I/O) : Each I/O can be used as input and output in the runtime database for the automatic initialization or storage via
Bool_t HRuntimeDb::setFirstInput(HParIo* io)
Bool_t HRuntimeDb::setSecondInput(HParIo* io)
Bool_t HRuntimeDb::setOutput(HParIo* io)
.
In this case the corresponding close() functions in the runtime database should be used to take care of the automatic storage of the setup and version information:
void HRuntimeDb::closeFirstInput()
void HRuntimeDb::closeSecondInput()
void HRuntimeDb::closeOutput()


4. Parameter containers

The pointers of all parameter containers are stored in a list in the runtime database. If a container is created explicitly in a macro it must be added to this list with the function
Bool_t HRuntimeDb::addContainer(HParSet* container)

It can be accessed via its name with
HParSet* HRuntimeDb::getContainer(Text_t* name)
and then casted to the concrete type.

void HRuntimeDb::removeContainer(Text_t* name) removes a container from the list and deletes it.
void HRuntimeDb::clearContainerList(void) clears the list, but does not delete the containers.

Bool_t HRuntimeDb::initContainers(Int_t runId,Int_t refId=-1,const Text_t* fileName="")
initializes all containers for the run with id runId from the inputs defined in the runtime database. If refId, the id of the reference run, is not -1 this run is taken for initialization. Argument fileName is the optional name of the corresponding event file (not used for initialisation).
Bool_t HRuntimeDb::writeContainers(void) writes the containers to the output. The input/output versions are stored in the version table in the runtime database.

Each container has init() and write() functions to handle the I/O individually:
Bool_t init(HParIo* input) initializes the container from 'input'
Bool_t init(HParIo* input,Int_t* modules) initializes only the modules defined in the array 'modules' from 'input'.

Int_t write(HParIo* output) writes a container to 'output' returning the output version.

The init() functions should however be used directly only for ascii input.
The input/output versions are not stored automatically in the runtime database.

Once a container is initialized and a reinitialization is not needed or should be suppressed the status flag of the container can be set (and reset) with void setStatic(Bool_t flag=kTRUE) .