| GSI
| HADES
| HADES@Catania
| HADES@Cracow
| HADES@Giessen
| HADES@GSI
| HADES@Santiago
| HADES@TUM
| CERN
|
 |
Initialization in the HADES analysis program
status report by Ilse Koenig
GSI May 10, 2000
(documentation for HYDRA version v401 and below:
initialization_1999)
|
| Computing
| HYDRA
| class docu
| CVS
| Oracle
| ROOT
|
Content:
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 together form 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:
- To avoid the need of many different parameter files
- To avoid to keep the connection to Oracle open all the time during a long analysis of
many files
- To be sure to have all initialization parameters available e.g. for running a batch job
overnight
- 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 by the different detectors.
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 in the different sectors execpt the START detector
consisting of two modules directly in the Cave.
The actually used detector configuration is created via a ROOT macro.
In the following example a 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.
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 magages the automatic initialization and saving in 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
- Oracle database
- ROOT file
- ascii file
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.
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:
- The containers should not hold parameters which are fixed, together with parameters
which change very often (maybe from run to run).
- Parameters needed by several tasks should be stored separately from the parameters
used only by one task.
- To garantee data consistency, the reading of redundant information into two different
containers is not allowed. Only one container should be initialized via reading,
the other one (or the redundant part) by copiing. (Otherwise only Oracle would
garantee data consistency.)
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.
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 6 arrays
(for the 6 layers) pointing to arrays of pointers with their size depending on the number of
wires in the corresponding MDC layer. These arrays finally hold pointers to the objects which
contain the real parameters slope and offset for this 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. It's not possible to plot the data directly. However the class
provides a function to fill the parameters on request into a ROOT NTuple and then use the Draw()
function of the NTuple.
A disadvantage of this structure is the need of in this case 5 classes (number 0of levels + 1)
because ROOT does not support template classes.
The class design for I/O was developed according to the following user requests:
- Provide interfaces to Oracle, ROOT files and ascii files
- The actually used I/O is defined in a Macro or on ROOT interpreter level
- The interface to Oracle is a shared library completely separated from the other analysis
code to avoid precompiler flags. (To compile Oracle-related code one needs the Oracle
precompiler.)
- Each detector has its own classes containing the concrete functions
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.
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. 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).
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
referencial constraints. The tables were designed under the aspect to minimize the storage
of redundant information to garantee data consistency and to save storage space. (Redundancy
should be avoided because it could spoil the data integrity, but in very rare cases needed to
gain performance.) For time dependent informations a version management is needed which fullfills
the following requirements:
- It must be possible to get a consistent set of informations at any date (e.g.the start
of a certain run).
- To keep the history, no information, even if it is wrong, should be overwritten without
trace, which means that only inserts should be made, no deletes or updates. It must be
possible to get an answer to the question: 'Which data were used when analysing this
run half a year ago?' (The calibration might have been optimized several times since
this date. Maybe some bugs have been detected and corrected in the mean time.)
As a result the following concept has been developped 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 an other 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
special for this parameter container is valid for a certain time range (the maximum common time
range of different time dependent informations). All runs with a start date in this time
range would get 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. 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 its lack of flexibility and bad performance (at least a factor 100 slower).
In the ROOT file parameter containers are stored as objects. Every time an objects is
written, its 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-function
provided by ROOT reads the objects with the highest version. A retrieval of an other 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.
An important request for the implementation was to avoid reading the same informations 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:
- A reinitialization of a container must be avoided, if the parameters in this container
do not change.
- A container, which has not changed, doesn't need to be written out.
- To store the complete runtime database in one ROOT file this version information must
be stored also in the ROOT file. (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 sequencially. The event file must not be a
listmode data file. The datasource retrieves the run_id from the event header and signalizes,
when a new run starts. Hades 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 in 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 this 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.)
Each time Hades asks the runtime database to initialize the containers providing the run id
and maybe additionally a reference run id, the following actions are taken:
- The runtime database adds this run to the list of runs (if not already in the list).
- It loops over all parameter containers and checks, if the parameter container has changed.
If so, it first checks, if the container with this input version (or this combination of
versions when initialized from two inputs) has already been written out once before. In
this case the output is suppressed and only the corresponding output version is stored
in the version table. Otherwise it calls the write function of the container.
- The write function of the container calls the corresponding write function in
the actual output, which then actually writes the data, sets the output version in the
parameter container and resets the 'changed' flag.
Then the runtime database adds the new output version in the version table.
- Next it loops over all containers and calls their init() function (function in the base
class), if the container has not been explicitly set fixed (status flag is kTRUE) before.
- If the parameter container was initialized formerly from two inputs, the
versions in the parameter container are resetted. This leads always to a
reinitialization.
Then the read function in the first input is called.
- Here a check is made, if the container has to be reinitialized )by
comparing the needed version for this run with the actual version in
the container). Eventually new parameters are read from
this input. The corresponding input version (or -1 if not found) is stored
in the container. The read function returns kTRUE, if the container has been
initialized completely, otherwise kFALSE
- If the initialization is not complete (one or more modules of the current setup are
not initialized) the read function in the second input is called.
Remark: Initialization of a container from two inputs is actually not supported
by RICH and Shower code.
- If the initialization was successful, the 'changed' flag of the parameter
container is set to kTRUE (which will trigger the write before the next
initialization).
For each successfully initialized container the input versions are copied to the version
table in the runtime database.
If no error occured, Hades now starts analyzing the events of this run. Otherwise the program
stops.
After all event files have been analyzed, the database contains a list in which for all runs
the input and output versions of all containers are stored. This list is written also 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 called in a
macro after defining the parameter I/O, the setup, all parameter containers and the list of
runs, but this time not by defining the event files in the data source but by adding explicitly
the run ids in the runtime database.
The run id is a very unhand number (creation date in number of seconds since 01/01/1970 GMT).
The oracle interface class
HOraInfo or
provides a function, which returns the run id corresponding to a HLD-filename.
The complete runtime database is created and written to a ROOT file without an event loop.
This can be used to create a ROOT file, which contains all parameters, one needs for maybe very
different configurations, but connecting only once to the database. Such a ROOT file can then
also be sent to people, who have no direct access to Oracle.
(see Example2)
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 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).
The following macros show examples of how to create and fill a runtime database using
different parameter sets for different runs of event data.
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:
- adds the first run in the runtime database
- 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 the most other
parameter containers. This container is initialized only once and is not allowed
to change during the analysis.
- 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 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.
- 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 is 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
-----------------------------------------------------------
   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 in the runtime database. But you do 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.
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.
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
with 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)
which the id of this run 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) :
- Oracle I/O (class HParOraIo )
The connection is opened by
Bool_t HParOraIo::open() (connect the user "Hades" with the
database "Hades" in read-only mode)
Bool_t >HParOraIo::open(char* user) (connects "user" with
database "Hades"; asks for the password)
Bool_t HParOraIo::open(char* dbName,char* user) (connects
"user" with database "dbName"; asks for the password)
- ROOT file I/O (class HParRootFileIo )
It is opened by
Bool_t HParRootFileIo::open(Text_t* filename, Option_t* option="READ",Text_t* ftitle="",Int_t compress=0)
with the options
- "READ" (opens an existing file only for reading; this is the default)
- "NEW" (creates a new file; if the file exists already it will not be opened)
- "UPDATE" (opens an existing file with read and write access)
- "RECREATE" (creates always a new file and overwrites an existing file)
ROOT has only one global file pointer which must point to the actually accessed ROOT file.
This pointer is set with the function
void HParRootFileIo::cd() .
- Ascii file I/O (class HParAsciiFileIo )
An ascii file is opened by
Bool_t HParAsciiFileIo::open(Text_t* filename, Text_t* status)
with status "in" or "out".
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 and. If refId, the id of the reference run is not -1 this run is taken for initialization. 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) .