A tutorial based on my talk given on June 13, 1997
at the MINOS Week-in-the-woods (Ely, MN)
The definitive location for information on ADAMO is the URL maintained at CERN by the Information and Programming Technology Group: http://www1.cern.ch/Adamo/ADAMO_ENTRY.html
Advantages of ADAMO:
As well a handling the structures internally in memory, ADAMO also supplies routines for input/output via the Generic ADAMO File (GAF):
The term cursor is often used in association with indices. Cursors delimit a range of rows in a table as ordered along a particular index.
The ADAMO user under fortran77 does not directly see any specially designed attribute types. All are mapped to standard types such as real, integer, character; restrictions on the ranges are managed internally by ADAMO and may trigger warning messages during validity checks.
The definition of a ESet (in our case PlanePos and partially PlaneSpec) describes the contents of each entity in the set. A list of attributes with accompanying types (and range) and descriptions are collected together as a unit.
The relationships are defined in the RSet section. These describe which tables are related and by what name. These entries also document the connectivity: whether a relationship is required for each source table; and whether more than one source entity can link to the same destination entry.
SUBSCHEMA MINOSGeom : 'Detector configuration' AUTHOR 'Robert Hatcher' VERSION '1.0' DATE '1996.05.24' REVIEWER ' ' DEFINE ATTRIBUTE GlobalPos = REAL :'global co-ordinate'; ShapeName = CHA4 'BOX '|'TUBE'|'PGON' :'restricted list of GEANT shapes'; END ATTRIBUTE DEFINE ESET PlanePos = ( ISuper = INTE [1,15] : 'supermodule number', IModule = INTE [1,300] : 'module number', InMdl = INTE [1,16] : 'pasv/actv plane pair in module', PairZmin = GlobalPos : 'upstream extent of pair', PairZmax = GlobalPos : 'downstream extent (including air gap)', PasvName = CHA4 : 'name of passive plane', XYPasv(2) = GlobalPos : 'X/Y (cm) center of passive plane', ZFrntPasv = GlobalPos : 'Z (cm) front face of passive plane', ActvName = CHA4 : 'name of active plane', XYActv(2) = GlobalPos : 'X/Y (cm) center of active plane', ZFrntActv = GlobalPos : 'Z (cm) front face of active plane', Rotm = CHA4 : 'rotation of active plane' ); PlaneSpec = ( PlnName = CHA4 : 'plane name (ie. key,instance,PL)', Shape = ShapeName : 'GEANT shape name', Width = REAL : 'face-to-face width of plane (cm)', Thickness = REAL : 'z thickness of plane (cm)', .... ); END ESET DEFINE RSET (PlanePos [1,1] -> [1,*] PlaneSpec BY PasvSpec) : 'point to the PlaneSpec for the Passive plane of pair'; (PlanePos [1,1] -> [1,*] PlaneSpec BY ActvSpec) : 'point to the PlaneSpec for the Active plane of pair'; END RSET END SUBSCHEMA
This example table is both too long and too wide for a normal screen so ADAMO attempts to format it by first grouping by ranges of roughly 50 rows, and then splitting the width into groups of attributes that will fit into 80 columns. ADAMO routines provide a means of tweaking the formatting (without actually affecting the underlying data). By displaying the information this way the imposed tabular format becomes quite apparent.
|-------------------------------------------------------------------| | Table: PlanePos ADAMO/TAP | | Count: 600 | | Page ( 1, 1) | | Printed along: ID [MINC,MAXC] | |-------------------------------------------------------------------| |ID |IS|IMo|In|PairZmin|PairZmax|Pasv|XYPasv(1)|(2) |ZFrntPasv| |----|--|---|--|--------|--------|----|---------|---------|---------| | 1| 1| 1| 1| 250.000| 257.060|B1PL| .000E+00| .000E+00| 250.200 ! | 2| 1| 1| 2| 257.060| 264.120|B1PL| .000E+00| .000E+00| 257.260 ! | 3| 1| 1| 3| 264.120| 271.180|B1PL| .000E+00| .000E+00| 264.320 ! | 4| 1| 1| 4| 271.180| 278.240|B1PL| .000E+00| .000E+00| 271.380 ! |--------------------------------------------------------------| | Table: PlanePos | | Page ( 2, 1) | |--------------------------------------------------------------| |ID |Actv|XYActv(1)|(2) |ZFrntActv|Rotm#ActvSpec|PasvSpec| |----|----|---------|---------|---------|----#--------|--------| | 1|F1PL| .000E+00| .000E+00| 254.600 |U # 2| 1| | 2|F1PL| .000E+00| .000E+00| 261.660 |V # 2| 1| | 3|F1PL| .000E+00| .000E+00| 268.720 |U # 2| 1| | 4|F1PL| .000E+00| .000E+00| 275.780 |V # 2| 1|
The filename and common exactly match the name of the ESet, including case. The first variable also matches the name of the ESet and will automatically be filled by ADAMO with an internal reference that it will use to reference the table; this token is then passed by the user to various ADAMO routines. Do not modify the value in that variable or be prepared to face the dire consequences (e.g. filling your disk with error messages). The variable ending with 9999 is a fence to catch attempts to write beyond the end of the common.
INTEGER PlanePos, PlanePos_9999 INTEGER PlanePos_ID, + PlanePos_ISuper, PlanePos_IModule, PlanePos_InMdl, + PlanePos_ActvSpec, PlanePos_PasvSpec CHARACTER*4 PlanePos_PasvName, PlanePos_ActvName, PlanePos_Rotm REAL PlanePos_PairZmin, PlanePos_PairZmax, + PlanePos_XYPasv, PlanePos_ZFrntPasv, + PlanePos_XYActv, PlanePos_ZFrntActv COMMON /PlanePos/ PlanePos, PlanePos_ID, + PlanePos_ISuper, PlanePos_IModule, PlanePos_InMdl, + PlanePos_PairZmin, PlanePos_PairZmax, + PlanePos_PasvName, PlanePos_XYPasv(2), PlanePos_ZFrntPasv, + PlanePos_ActvName, PlanePos_XYActv(2), PlanePos_ZFrntActv, + PlanePos_Rotm, PlanePos_ActvSpec, PlanePos_PasvSpec, + PlanePos_9999
Here's a KUMAC file for opening the GAF file and reading in the first event:
macro read_gaf 1=minos_events.fz_gaf *- decompose the filename filename = [1] ndot = $WORDS([filename],'.') extension = $WORD([filename],[ndot],1,'.') message filename [filename] extension [extension] *- attempt close in case of previous open gaf/close mygaf *- open the file, use appropriate driver case [extension] in (fz_gaf) gaf/open mygaf NAME=[filename],DRIVER=FZ,filfor=EXCH,recfor=EXCH (ie_gaf) gaf/open mygaf NAME=[filename],DRIVER=IE (*) message read_gaf.kumac: Unknown file extension [extension] message read_gaf.kumac: Attempt to open with FZ driver gaf/open mygaf NAME=[filename],DRIVER=FZ,filfor=EXCH,recfor=EXCH endcase *- read geometry tables (first entry in gaf) gaf/next mygaf gaf/accept mygaf *- read an event dataflow gaf/next mygaf gaf/accept mygaf return
TIP> exec read_gaf gminos_22110_1.fz_gafYou can dump individual tables using commands like:
TIP> table/print all FLSDigitwhere FLSDigit is an example ESet. I recommend people take a look at PlanePos and PlaneSpec to get an idea of the structures.
The pair of commands
gaf/next mygaf gaf/accept mygafwill read in the next event.
$ tip !-------------------------------------------------------- !ADAMO/TIP : Table Interaction and Plotting = ADAMO + PAW !-------------------------------------------------------- ****************************************************** * * * W E L C O M E to P A W * * * * Version 2.04/15 14 March 1994 * * * ****************************************************** Workstation type (?=HELP) <CR>=1 : Version 1.20/11 of HIGZ started *** Using default PAWLOGON file "/user4/hatcher/.pawlogon.kumac" PAW Logon ADAMO/TAP version 3.3 with GAF version 3.2 starting ORACLE Driver added ... try to execute your TIP kumac file ... *** Unknown file tip.kumac Enter HELP TIP for help TIP> exec read_gaf minos_events_r11101.fz_gaf File not yet opened by you Record found: MinosGeom Type : GEOM Version : 1 RunNo : 11101 EventNo : 0 Record found: GeantEvent Type : GEVT Version : 1 RunNo : 11101 EventNo : 1 TIP> table/list |-----------------------------------------------------------| | Page ( 1, 1) | |-----------------------------------------------------------| |Name |ID |Count|LaSeN|IniCr|MaxCr|Open|Division| |----------------|----|-----|-----|-----|-----|----|--------| |BeamSystem | 43| 1| 1|=====|=====| T| 4000002| |CellPos | 36| 776| 776|=====|=====| T| 4000002| |DigitPln | 44| 0| 0|=====|=====| T| 4000002| |FLSDigit | 45| 0| 0|=====|=====| T| 4000002| |FLSHit | 46| 0| 0|=====|=====| T| 4000002| |GeomMisc | 37| 1| 1|=====|=====| T| 4000002| |HitPln | 47| 29| 29|=====|=====| T| 4000002| |LSTHit | 48| 245| 245|=====|=====| T| 4000002| |LSTProto | 49| 0| 0|=====|=====| T| 4000002| |LSTStrip | 50| 0| 0|=====|=====| T| 4000002| |LSTWire | 51| 0| 0|=====|=====| T| 4000002| |Material | 31| 32| 101|=====|=====| T| 4000002| |MdlOrg | 38| 24| 24|=====|=====| T| 4000002| |Mixture | 32| 63| 63|=====|=====| T| 4000002| |NeuKin | 52| 1| 1|=====|=====| T| 4000002| |NeuVtx | 53| 1| 1|=====|=====| T| 4000002| |PlanePos | 39| 600| 600|=====|=====| T| 4000002| |PlaneSpec | 40| 2| 2|=====|=====| T| 4000002| |Rotm | 33| 6| 301|=====|=====| T| 4000002| |StdHep | 54| 31| 31|=====|=====| T| 4000002| |StdHepHead | 55| 1| 1|=====|=====| T| 4000002| |SubVolSpec | 41| 0| 0|=====|=====| T| 4000002| |SuperModule | 42| 3| 3|=====|=====| T| 4000002| |TMedium | 34| 33| 101|=====|=====| T| 4000002| |TPar | 35| 1| 1|=====|=====| T| 4000002| |-----------------------------------------------------------| TIP> gaf/next mygaf Record found: GeantEvent Type : GEVT Version : 1 RunNo : 11101 EventNo : 2 TIP> gaf/accept mygaf TIP> table/print all StdHep |----------------------------------------------------------------------------| | Table: StdHep ADAMO/TAP | | Count: 6 | | Page ( 1, 1) | | Printed along: ID [MINC,MAXC] | |----------------------------------------------------------------------------| |ID |Ist|IdHEP |Jmo|(2)|Jda|(2)|PHEP(1) |(2) |(3) |(4) |(5) | |----|---|------|---|---|---|---|--------|--------|--------|--------|--------| | 1| 3| 14| 0| 0| 0| 0| .000| .326| 5.616| 5.625| .000| | 2| 3|******| 0| 0| 0| 0| .000| .000| .000| 52.103| 52.103| | 3| 3| 2112| 0| 0| 0| 0| .074| .202| -.058| .931| .904| | 4| 0|******| 0| 0| 0| 0| -.074| -.202| .058| 51.172| 51.172| | 5| 0| 13| 0| 0| 0| 0| .060| 1.120| 5.299| 5.417| .106| | 6| 0| 2212| 0| 0| 0| 0| .014| -.592| .259| 1.139| .938| |----------------------------------------------------------------------------| |-------------------------------------------| | Table: StdHep | | Page ( 2, 1) | |-------------------------------------------| |ID |VHEP(1) |(2) |(3) |(4) | | |----|--------|--------|--------|--------| | | 1| -76.6| 2600.7| 38691.7| .0| | | 2| -76.6| 2600.7| 38691.7| .0| | | 3| -76.6| 2600.7| 38691.7| .0| | | 4| -76.6| 2600.7| 38691.7| .0| | | 5| -76.6| 2600.7| 38691.7| .0| | | 6| -76.6| 2600.7| 38691.7| .0| | |-------------------------------------------| TIP> quit TIP closing down ... ... done. Exiting from PAW.
ADAMO subroutine/function | |
partap.inc constant | |
window common variable | |
table attribute (column) |
All references to particular tables (eg. FLSDigit, PlanePos, PlaneSpec, RecoVtx) are simply for illustration purposes.
#include "FLSDigit.inc" #include "PlanePos.inc" #include "PlaneSpec.inc" #include "RecoVtx.inc" #include "partap.inc"The first four lines make the window common for various tables available to the current routine. The partap.inc include file declares the type of ADAMO functions and defines some special values used in ADAMO interactions. A complete list of the variable names to avoid are:
ALL | ALLCOL | AND | CANY | CNULL | DIF | HOR | IANY | ID | INS | INULL |
KEE | MAXC | MINC | NEXT | OR | ORD | RANY | REP | RNULL | UNO | VER |
call PRITAB(FLSDigit,ID,MINC,MAXC,ALLCOL)
ndigit = COUTAB(FLSDigit)Note that this need not be the same as the highest row number (ie. the largest value of FLSDigit_ID).
FLSDigit_ID = idigit call GETTAB(FLSDigit) call FETTAB(FLSDigit,ID,idigit)
Tables are automatically ordered along every attribute (ie. column), of
which ID is only one index.
For example, to loop over all the entries in the FLSDigit
table from the lowest value in the
ADC(1) attribute to the highest one can write code similar to:
integer indx_adc1
indx_adc1 = GETIND(FLSDigit,'ADC(1)')
do idigit = 1, ndigit
call FETTAB(FLSDigit,indx_adc1,idigit)
Remember, ndigit is the number of
entries in the table and is returned by the
COUTAB function.
The indx_adc1 variable carries the
internal information that ADAMO needs to determine which ordering to use; the
partap constant ID is but a special
value defined for all tables.
While at first the two methods seem to give identical results
when FETTAB is called with the
ID index,
the
difference is not simply in the syntax. The two parallel each other
if-and-only-if
the table is filled consecutively from ID=1 with no gaps.
In the case of
GETTAB one is retrieving the
row with ID = idigit,
while for FETTAB one gets the
idigit-th entry in the table, independent
of the actual row number (eg. ID). Thus if the possibility
exists that entries in the table might be deleted, it is better to use
FETTAB to perform loops. Such
a loop from 1 to COUTAB will access
each available row. On the other hand, for a table with gaps in the
row numbers the loop
ndigit = COUTAB(FLSDigit)
do idigit = 1, ndigit
FLSDigit_ID = idigit
call GETTAB(FLSDigit)
enddo
will fail to span all the available rows and will attempt to access rows
that do not exist. Such accesses generate messages such as:
!ADAMO/TAP/GETTAB/Run-time error Table : FLSDigit ! Row : 99 !-------------------------------> Row does not exist
integer indx_fls_pec
character*16 attnam(3)
character*4 order(3)
attnam(1) = 'IPln'
attnam(2) = 'IExtr'
attnam(3) = 'ICell'
order(1) = 'ASC'
order(2) = 'ASC'
order(3) = 'ASC'
call CREIND(FLSDigit,indx_fls_pec,'INDX_FLS_PEC',3,attnam,order)
do idigit = 1, ndigit
call FETTAB(FLSDigit,indx_fls_pec,idigit)
It is recommended that users not use the name
index
for holding the values from CREIND
or GETIND;
index
is the name of a standard fortran language string function. The user
declaration with override the default, but it will also generate
compiler warning messages and lead to reader confusion.
Created table indices are persistent. This means that the ordering is
maintained even if rows are added or deleted or the table is cleared and
and re-filled (ie. by the next event). One can remove user defined
orderings with the command:
call DROIND(FLSDigit,'INDX_FLS_PEC')
where 'INDX_FLS_PEC' matches the
label used in creating the index. Thus a routine should either have
a matching DROIND for every
CREIND, or the
CREIND should only be called once.
The indx_fls_pec variable holds a
"handle" identifying the index. Users must be careful to either use
the fortran SAVE statement to ensure that the value is static
across invocations of the subroutine, or they should use
indx_fls_pec = GETIND(FLSDigit,'INDX_FLS_PEC')
to restore the value each time the subroutine is called. Without a
statement similar to
SAVE indx_fls_pec,
this local variable will be put
on the heap or stack and its value is not guaranteed to be retained
after exiting the subroutine.
integer indx_ipln, klo, khi, k indx_ipln = GETIND(FLSDigit,'IPln') FLSDigit_IPln = my_plane call SELTAB(FLSDigit,indx_ipln,klo,khi) do k = klo, khi call FETTAB(FLSDigit,indx_ipln,k)The klo, khi variables are referred to as cursors; they delimit a range when the table is viewed as ordered along a particular index. Be aware that cursors are not transferrable to other indices. They are only meaningful for the index used in the SELTAB call.
If the index used in the SELTAB call involves more than one attribute, then the user should set all the relevant values in the window common before the SELTAB call.
Caveats: the ordering index in SELTAB and FETTAB must be the same; the retrieved order of digitizations within a plane is undefined. One is guarenteed to loop over all that match the criteria, but the ambiguity of ordering is resolved by the ID attribute, and so it depends on how they were entered into the table. The technique demonstrated in the next sub-section could be used in conjuction with the a user-created multi-attribute ordering to get the exact sequence desired.
The code fragment:
FLSDigit_ADC(1) = 2.3
call SELTAB(FLSDigit,indx_adc1,klo1,khi1)
c klo1 contains the next row with a value > 2.3
FLSDigit_ADC(1) = 5.6
call SELTAB(FLSDigit,indx_adc1,klo2,khi2)
c khi2 contains the last row with a value < 5.6
do k = klo1, khi2
call FETTAB(FLSDigit,indx_ipln,k)
loops over all digitizations with ADC(1) in the range [2.3,5.6].
Note than one can also use these cursors to print a range of a table:
call PRITAB(FLSDigit,indx_adc1,klo1,khi2,ALLCOL)
Actually, each entry in the PlanePos table represents information about two planes, the active detector plane and the passive absorber immediately upstream (towards the neutrino source). In most cases the absorber plane is the iron magnet plane, but in the case of a geometry that involves two active detectors for every iron plane a thin sheet of "air" is inserted between the two.
To determine the properties of a plane one needs to follow the relationship
logical ok
real zfront, zback
c find the front and back z position of passive plane "ipln"
call FETTAB(PlanePos,ID,ipln)
call NATREL(PlanePos,PlanePos_PasvSpec,PlaneSpec,ok)
c if ok=.true. then the PlaneSpec window contains the information about
c the absorber plane
zfront = PlanePos_ZFrntPasv
zback = zfront + PlaneSpec_Thickness
c find the front and back z position of active plane "ipln"
call FETTAB(PlanePos,ID,ipln)
call NATREL(PlanePos,PlanePos_ActvSpec,PlaneSpec,ok)
c if ok=.true. then the PlaneSpec window contains the information about
c the active detector plane
zfront = PlanePos_ZFrntActv
zback = zfront + PlaneSpec_Thickness
call NULWIN(RecoVtx)sets all the integer variables to INULL, the real variables to RNULL, and the character variables to CNULL. These values are parameterized in the include file partap.inc.
call NULWIN(RecoVtx) RecoVtx_ID = NEXT RecoVtx_X = x_guess ... = ... RecoVtx_IPln = ipln_vtx_guess RecoVtx_Routine = 'my vertex finder' call INSTAB(RecoVtx)
call FETTAB(RecoVtx,ID,i) RecoVtx_Routine = 'xyzzy' call REPTAB(RecoVtx)
call CLETAB(LSTStrip)Be sure that is really what you want to do. This should not be done to general Reco tables by individual reconstruction algorithms (though it is okay for tables specific to an algorithm). In general, the Reco tables should be a place to accumulate results for a variety of alternative algorithms and no one routine should wipe the table. In the reco_minos framework all such tables should get cleared in the routine clear_reco which is called before the user hook reco_event.
FLSDigit_ID = idigit call DELTAB(FLSDigit) call CLENXT(FLSDigit)The CLENXT call ensures that the internal state of the table is consistent; this means that the counter containing maximum number entries is updated, and future calls to INSTAB with the *_ID = NEXT correctly adds to the end of the table.
One must also be careful when deleting rows from within a loop.
This must be done while looping backwards so as not to upset the loop.
do idigit = COUTAB(FLSDigit), 1, -1
call FETTAB(FLSDigit,ID,idigit)
if (FLSDigit_ADC(1) .lt. 3.0) call DELTAB(FLSDigit)
enddo
call CLENXT(FLSDigit)
Removing multiple rows with a common property can be more easily
accomplished using the DEMTAB routine.
FLSDigit_IPln = my_plane call SELTAB(FLSDigit,indx_ipln,klo,khi) call DEMTAB(FLSDigit,indx_ipln,klo,khi) call CLENXT(FLSDigit)Again, it is wise to follow up deletions with a call to CLENXT in anticipation of future INSTAB calls.