program read !===================================================================== ! DECLARATIONS !===================================================================== implicit none #include "partap.inc" #include "mcEvKey.inc" #include "mcEvent.inc" #include "mcBeam.inc" #include "mcTrack.inc" #include "mcVert.inc" #include "mcHit.inc" #include "dataPuls.inc" #include "dataHodo.inc" #include "dataCalo.inc" integer i, i1, i2, j1, j2, igaf, idfl, isel, iok, ievent integer ind, indname, indname1, indname2 integer inddigi, inddigi2 integer decay_vertex, idet real values(4) logical ok character*32 colnam(2) character*4 corder(2) character*80 cline !===================================================================== ! INITIALIZE ADAMO !===================================================================== call INITAP !===================================================================== ! OPEN AN EXISTING GAF FILE !===================================================================== ! The routine OPEGAF opens an existing GAF. As you can see, the syntax ! of this routine is very similar to that of CREGAF. cline = 'NAME=toyMC.events,DRIVER=FZ,FILFOR=EXCH' call OPEGAF (igaf,cline,iok) if (iok.ne.0) stop 'OPEGAF failed.' ! Let's demonstrate the command LISTAB here, which simply lists all ! tables defined in a given dataflow (here, our uber-dataflow mcEvents). print *, 'LISTAB of mcEvents' call LISTAB (gettdf('mcEvents')) !===================================================================== ! READ THE FIRST RECORD FROM THE GAF !===================================================================== ! Using NEXGAF and FETGAF ! ----------------------- ! NEXGAF and FETGAF are the two routines you need to read in ! each record of the GAF. write(6,900) 'NEXGAF AND FETGAF' ! As you can see in the IE version of your GAF, the key table ! information appears at the top of each record, in a simple row. ! NEXGAF retrieves this information, and loads the ! key table information to the key table WCB (or WIN). call NEXGAF (igaf, mcEvKey, iok) ! We'll check the error code variable iok now. This is more than just an ! error check ... it will also tell us if we have reached the end of ! the GAF. The end-of-file code is iok = -1. if (iok.ne.0) goto 999 ! Now we use some of the key table information from this record. ! We store the event number in a convenient variable ievent, and inform ! the user. Also, remember that the last entry in any key table MUST ! be a CH32 variable, and is used to hold the name of the DATAFLOW ! which is stored in the record. We already know that the dataflow ! name is mcEvents, since we WROTE this GAF. When you are reading ! in someone else's GAF, however, it is important to know that the ! dataflow name is available here. ievent = mcEvKey_ievent write(6,'(/20x,a,i3,1x,a)') '<<<<< EVENT NUMBER ', + ievent, '>>>>>' write(6,905) 'mcEvKey_cName = ', mcEvKey_cName, + ' (should be the dataflow name, mcEvents)' idfl = GETTDF (mcEvKey_cName) ! Now that we know the dataflow identifier, we can call FETGAF to actually ! read the tables from this record into memory. ! Note that FETGAF can read in any record you choose. It determines ! which record you want from the information in the key table WIN. ! Since we just loaded this WIN with NEXGAF, we will be getting the ! NEXT record in the GAF. call FETGAF (igaf, mcEvKey, idfl, iok) !===================================================================== ! INTRODUCING INDEXES !===================================================================== write(6,900) 'USING INDEXES' ! An index is a logical ordering of the rows in a table. ! You ask ADAMO to create these indexes for you, given the sorting ! criteria you want. Just like tables, GAFs, and dataflows, indexes ! are stored with a name and an identifier number. ! The simplest indexes are based on the values in a single table column. ! The simplest index of all is based on the ID column ... this is the ! natural sorting order of the table, by row number. This index is ! used so often in ADAMO that partap.inc provides you with a 'secret code' ! for it, the parameter ID. ! We saw the use of PRITAB in the write-gaf example code. However, we ! didn't go into much detail. Here once more is the standard way ! to print the entire mcTrack table: write(6,901) 'mcTrack sorted by ID' call PRITAB (mcTrack,ID,MINC,MAXC,ALLCOL) ! Now look more closely at the second argument. There is the secret code ! ID that I just mentioned! The second argument is actually looking for ! an index identifier. We have used the ID index, and so the table is printed ! out sorted by row number. ! Now let's create a simple index, sorted on the column 'cName'. ! GETIND can create these simple indexes on any (single) column name. indname = GETIND (mcTrack,'cName') write(6,901) 'mcTrack sorted by index on cName' call PRITAB (mcTrack,indname,MINC,MAXC,ALLCOL) ! This time, the rows of the table are printed out in a different order: ! they are arranged so that the particle names are in alphabetical order. ! Finally, we introduce CREIND, which can create more complex indexes ! sorted on more than one column. You supply the column names ! in a vector (here colnam), by order of precedence for your sort. ! You also supply a vector of the same length containing either 'ASC' ! (ascending) or 'DESC' (descending), to specify the order in which ! each column should be sorted. The 3rd last argument to CREIND ! specifies the number of entries in your vectors. colnam(1) = 'cName' colnam(2) = 'iCharge' corder(1) = 'ASC' corder(2) = 'DESC' call CREIND (mcTrack,indname2,'NameOrder2',2,colnam,corder) write(6,901) + 'mcTrack sorted by ASC order on cName, then DESC on iCharge' call PRITAB (mcTrack,indname2,MINC,MAXC,ALLCOL) !===================================================================== ! RETRIEVING INFORMATION FROM TABLES !===================================================================== ! We have read in the first record of the GAF, so the information is all ! stored in ADAMO memory. But to work with it, we need to retrieve ! this information row by row into our WCB variables. There are ! three basic routines that do this for us: FETTAB, GETTAB, and FETCOL. ! Introducing FETTAB ! ------------------ ! Very simple. FETTAB takes three arguments: the table identifier, ! an index identifier, and a 'cursor' number. This cursor specifies: ! which row do you want from the index you suppplied? ! In this first example, we use the row-sorted index ID, and request ! that the first row be pulled into the WCB. ! (The subroutine printwin_mctrack is just a little utility routine ! whose code is at the end of this file. It prints out some of the ! variables from the WCB to the screen, in a more compressed format ! than PRITAB.) write(6,900) 'USING FETTAB' write(6,901) 'Retrieving mcTrack #1, from index ID' call FETTAB (mcTrack,ID,1) call printwin_mctrack ! Let's try that again using a different index: the index on the cName ! column which we defined earlier. Cursor number 1 means the first entry ! in this index, which is NOT row #1. write(6,901) 'Retrieving mcTrack #1, from index on cName' call FETTAB (mcTrack,indname,1) call printwin_mctrack ! Introducing GETTAB ! ------------------ ! GETTAB retrieves one row from a table in a simpler way. It does ! NOT allow you to use indices. Instead, you set the row number that ! you want in the ID variable of the WIN, and simply call GETTAB with ! the table name. GETTAB retrieves the row you requested. write(6,900) 'USING GETTAB' write(6,901) 'Retrieving mcTrack row #2' mcTrack_ID = 2 call GETTAB (mcTrack) call printwin_mctrack ! The principal virtue of the simpler GETTAB is that it is FASTER than ! FETTAB. Keep this in mind if you are writing code that does lots of ! table accesses and is running slowly. Of course you can only use it ! if you have no need of the indexing capabilities supplied by FETTAB. ! Introducing FETCOL ! ------------------ ! FETCOL retrieves table information by COLUMN rather than by row. ! Since the WCB is row-oriented, the information is NOT placed there, ! but rather is stored in a vector that you supply. Here, we retrieve ! the momentum ('P') column from all rows of table mcTrack and store ! the values in the real vector 'values'. This vector was declared ! at the top of the program with 4 entries, since we already know ! there can be at most 4 tracks in the mcTrack table. In general: ! be sure your vector is LONG ENOUGH ... write(6,900) 'USING FETCOL' write(6,901) 'Retrieving momenta from mcTrack, sorted by ID' call FETCOL (mcTrack,'P',ID,MINC,MAXC,values) do i = 1, 4 write(6,906) 'Momentum #', i, ' = ', values(i) enddo ! Arguments 3, 4, and 5 of FETCOL are the same as those from PRITAB: ! you can supply an index, and a first and last cursor on this index. ! In this example, we retrieve the momenta using an index on the ! momentum column itself, and retrieve only the last two rows. ! This should give us the highest two momenta in the table. ! Note that although we are retrieving the 3rd and 4th entries of the ! index, the values go to the first two elements of the array 'values'. ! To illustrate this, we will first clear the values array. do i = 1, 4 values(i) = 0 enddo write(6,901) 'Retrieving highest two momenta from mcTrack' ind = GETIND (mcTrack,'P') call FETCOL (mcTrack,'P',ind,3,4,values) do i = 1, 2 write(6,906) 'Momentum #', i, ' = ', values(i) enddo ! Note that FETCOL is also faster than FETTAB. !===================================================================== ! NAVIGATING RELATIONSHIPS !===================================================================== ! Example 1: navigating mcHit -> mcTrack ! -------------------------------------- write(6,900) 'NAVIGATING RELATIONSHIPS' ! Remember all those nice relationships that we set up in the DDL, relating ! rows from one table to rows from another table? We're now going to learn ! how to NAVIGATE (i.e. follow) these relationships, using NATREL ! and NAFREL. This concept is sufficiently important (and sufficiently ! tricky to wrap your brain around ...) that I will supply two examples. ! In the first example, we will navigate the relationship mcHit -> mcTrack. ! Each hit (i.e. each row in the mcHit table) is associated with ! the particle track that caused it. If you recall, we set up our GAF ! so that only track #1 (the scattered positron) caused any hits, so ! we already know what our answers will be: all hits are linked to track #1. ! NATREL navigates in the TO direction, which means FORWARDS along the link. ! The link points from mcHit to mcTrack. So we specify a row in mcHit, ! and then ask NATREL to tell us which row in mcTrack it is linked TO. write(6,901) 'Navigate mcHit #1 FORWARDS to mcTrack', + ' using NATREL' mcHit_ID = 1 call NATREL (mcHit, mcHit_mcTrack, mcTrack, ok) call printwin_mctrack ! As you will see in the output, NATREL retrieved row #1 of mcTrack for us ! and stored it to the WIN. ! A couple of notes: ! - We did not have to load up the mcHit WIN with the full information ! from mcHit row #1. NATREL only looks at the ID column (it doesn't ! need to know anything else, after all, just a row number). ! - Please note that unlike our previous error codes, the error code ! here is 'ok', which is not an integer variable but a LOGICAL variable. ! It is perfectly possible that mcHit row #1 is not linked to ANYTHING ! in mcTrack (the link could be NULL). If ADAMO is unable to navigate ! the link, the 'ok' variable will simply return FALSE. This is not ! an error, really, just information you might need. ! OK, that was straightforward. Now we will learn about the second ! routine, NAFREL. This one you DO have to wrap your brain around, a bit. ! This routine navigates BACKWARDS along a link. ! Our link is mcHit -> mcTrack. NAFREL will start with a row from mcTrack, ! and search BACKWARDS along the link to find all rows from mcHit that ! are linked to it. Unlike forward navigation, this routine may well ! come up with more than one linked row. ! In the following example, we start with mcTrack row 1 (the scattered ! positron) and find out which hits it caused. We know already that ! this should give us all 5 rows of the mcHit table. write(6,901) 'Navigate mcTrack #1 BACKWARDS to mcHit', + ' using NAFREL' call NULWIN (mcTrack) mcTrack_ID = 1 ind = GETIND (mcHit,'mcTrack') call NAFREL (mcTrack, mcHit_mcTrack, mcHit, ind, i1, i2) do i = i1, i2 call FETTAB (mcHit, ind, i) call printwin_mchit enddo ! Yup, you will see from the output that all 5 rows of mcHit were found. ! Some more notes: ! - The starting point this time was row 1 from mcTrack. Again, ONLY ! the ID column mcTrack_ID has to be set, with this information. ! I proved it to you this time by first clearing the mcTrack WIN using ! a call to NULWIN. ! - The output of NAFREL is contained in the cursor variables i1 and i2. ! These specify the range of rows on mcHit that were found by the ! navigation. But: range of rows on what index? You have to give ! NAFREL exactly the right index as its fourth argument: an index ! sorted on the RELATIONSHIP COLUMN itself. If you think about this ! for a minute, it makes sense. There is no reason that the rows ! NAFREL finds will be contiguous within any other index ... ! - NAFREL found 5 rows for us this time. It did not load up any of these ! rows to the mcHit WIN, because it does not know which one you want. ! We had to load them up one at a time ourselves using FETTAB. ! If NAFREL had found only ONE row, however, then the cursors ! i1 and i2 would have come back with the same values, AND the mcHit ! WIN would have been automatically loaded up with this one row. ! How convenient! You will see this behaviour in operation later on, ! with some other (similar) commands that return cursors on an index. ! - Finally, what if NAFREL had found NO matching rows in mcHit? ! In this case, it would have returned cursor values i1 and i2 such ! that i1 > i2. This is how NAFREL (and all other commands that ! return cursors on an index) indicate that no match was found. ! It is a nice way of returning this information, since a loop over ! the range i1 --> i2 will simply not execute. But sometimes you may ! have reason to check explicitly for this failure condition. ! Example 2: navigating mcTrack -> mcTrack BY Parent ! -------------------------------------------------- ! This second example is really not any more complicated that the first one. ! This time we will navigate forwards and backwards along the Parent ! link, from one row of mcTrack to another. We will explore this relationship ! using track #2, the unstable K0_s particle, and tracks #3-4, the ! charged pions to which the K0_s decays. The K0_s is the Parent of ! both pions. ! In the forward direction, we start with one of the child particles, ! the pi+ (row#4) and navigate FORWARDS to its parent. write(6,901) 'Navigate mcTrack #4 FORWARDS to mcTrack', + ' by Parent, using NATREL' mcTrack_ID = 4 call NATREL (mcTrack,mcTrack_Parent,mcTrack,ok) call printwin_mctrack ! Let me just show you something before we continue, maybe this will ! help to clarify what is going on. We did not actually need to ! call NATREL to do this for us. We could have done it 'by hand' as follows: write(6,901) 'Navigate mcTrack #4 FORWARDS to mcTrack', + ' by Parent, BY HAND' mcTrack_ID = 4 call GETTAB (mcTrack) mcTrack_ID = mcTrack_Parent call GETTAB (mcTrack) call printwin_mctrack ! Makes sense? The point is this: whenever you have an EXACTLY one-to-one ! relationship from table1 --> table2 (they could be the same table), ! and you only want to navigate forwards along it (i.e. from table1 to table2), ! you actually don't need NATREL or NAFREL or anything else. The link variable ! in table1 simply gives you the corresponding row number in table2, so you ! can just load it up yourself. You'll often see such examples in existing ! codes. But note: this simple technique fails of course if the relationship ! is NOT one-to-one. ! One last forwards example illustrates how the status variable 'ok' ! can sometimes come back false. Let's start with the K0_s this time, ! and try to navigate to ITS parent particle. The K0_s does not HAVE ! any parent ... the link variable mcTrack_Parent is equal to INULL ! for the K0_s track. write(6,901) 'NATREL failure example: finding Parent of K0_s' mcTrack_ID = 2 call NATREL (mcTrack,mcTrack_Parent,mcTrack,ok) write(6,908) 'NATREL came back with ok = ', ok ! Finally, we go BACKWARDS. We start with the K0_s track, and ! navigate the Parent link backwards to its CHILD tracks. ! Hopefully, we will recover the two pions. write(6,901) 'Navigate mcTrack #2 BACKWARDS to mcTrack', + ' by Parent, using NAFREL' mcTrack_ID = 2 ind = GETIND (mcTrack,'Parent') call NAFREL (mcTrack, mcTrack_Parent, mcTrack, ind, i1, i2) do i = i1, i2 call FETTAB (mcTrack, ind, i) call printwin_mctrack enddo ! As before, NAFREL needs an index on the relationship column in question, ! and returns two cursors on that index. !===================================================================== ! USING SELTAB !===================================================================== write(6,900) 'USING SELTAB' ! SELTAB is a very useful function that adds a selection capability ! to the index mechanism. The concept is this: an index on a particular ! column X sorts a table for you based on the values of X. ! Obviously, any rows that have the SAME value of X will end up ! in a contiguous block in the index. SELTAB now allows you to SELECT ! the row or rows from the table that have some PARTICULAR value of X. ! The answer comes back as a cursor (range of rows) on the index. ! Remember our generalized relationship? ! mcHit -> dataPuls | dataHodo | dataCalo BY digiTable ! This links hits to digitizationss, where the digitizations can appear ! in one of three different tables. Two WCB variables characterize ! the generalized relationship: ! mcHit_digiTable contains the NAME of the link table (dataPuls, ...) ! mcHit_digiTable_ contains the ROW NUMBER within the link table ! In this example, we are going to find all the rows of mcHit ! which are linked to digitizations in the dataHodo table. ! What we want to do, then, is SELECT all mcHit rows which have ! mcHit_digiTable = 'dataHodo'. SELTAB can do this easily. write(6,901) 'Find all hodoscope hits using SELTAB' inddigi = GETIND (mcHit, 'digiTable') call NULWIN (mcHit) mcHit_digiTable = 'dataHodo' call SELTAB (mcHit, inddigi, i1, i2) do i = i1, i2 call FETTAB (mcHit, inddigi, i) call printwin_mchit enddo ! That was straightforward. As I mentioned, you have to use an index ! on the selection column ... otherwise the selected rows won't necessarily ! appear in a contiguous block which can be identified by the two cursors. ! Also note: the only thing SELTAB needs to know is the value you want ! to select, in this case mcHit_digiTable = 'dataHodo'. I used NULWIN ! again simply to demonstrate that no other variables in the WIN need ! to be filled. ! Just to show you what we did, let's do it again, but BY HAND this time: ! we can just cycle through the table rows, picking out the ones ! we want. How inelegant ... but it works too. write(6,901) 'Find all hodoscope hits by BRUTE FORCE SEARCH' do i = 1, COUTAB(mcHit) mcHit_ID = i call GETTAB (mcHit) if (mcHit_digiTable.eq.'dataHodo') call printwin_mchit enddo ! Finally, a failure example. What happens if SELTAB cannot find ! ANY rows matching your selection criteria? Let's try it: we'll ! ask SELTAB to find all rows with mcHit_digiTable = 'toad' :) write(6,901) 'SELTAB failure example: finding hits in toad' mcHit_digiTable = 'toad' call SELTAB (mcHit, inddigi, j1, j2) write(6,909) 'SELTAB came back with cursor 1 = ', j1, + 'cursor2 = ', j2 ! As you will see from the output, SELTAB indicates its failure by ! coming back with j1 (low end cursor) > j2 (high end cursor). !===================================================================== ! NAVIGATING GENERALIZED RELATIONSHIPS !===================================================================== write(6,900) 'NAVIGATING GENERALIZED RELATIONSHIPS' ! As our last lesson in navigation, we need to learn about NATGEN ! and NAFGEN. These do the same as NATREL and NAFREL, but for generalized ! relationships which can point to rows in different tables. ! What we will do is navigate from mcHit to dataHodo. ! The syntax of NATGEN is identical to that of NATREL: it needs ! the identifiers of the two tables, and the relationship variable ! along which you want to navigate. This variable is mcHit_digiTable. ! (Note: it is NOT mcHit_digiTable_). ! At this point in the program, we already know which rows in mcHit ! DO in fact link to dataHodo. The cursor variables i1 and i2 currently ! hold the row range, on the index inddigi. So let's loop over those ! rows, and retrieve the corresponding rows from dataHodo. write(6,901) 'Navigate mcHit #1 FORWARDS to dataHodo', + ' BY digiTable, using NATGEN' do i = i1, i2 call FETTAB (mcHit, inddigi, i) call NATGEN (mcHit, mcHit_digiTable, dataHodo, ok) call printwin_datahodo enddo ! NOTE: As usual, all NATGEN needs to know is the ID number on mcHit from ! which you want to start your navigation. However, in my do loop, I did ! NOT just set mcHit_ID = i before calling NATGEN. i1 and i2 are NOT cursors ! on the ID index but on a different index --> they are NOT row numbers. ! I used FETTAB and the proper index inddigi to pull each mcHit row (including ! its ID value) into the WIN before calling NATGEN. ! Now a failure example. What happens if I navigate from the CALORIMETER ! hit to dataHodo? The calorimeter hit (row #5) is linked to dataCalo. ! This will surely fail ... First, let's find the CALO hit. We already ! know it is row #5, but's let's use SELTAB, for practice. write(6,901) 'NATREL failure example: navigating a CALO hit', + ' to dataHodo, using NATGEN' mcHit_digiTable = 'dataCalo' call SELTAB (mcHit, inddigi, i1, i2) call printwin_mchit ! I didn't check, but you can be sure SELTAB foudn exactly one row, ! number 5. Cursors i1 and i2 came back equal to each other. ! As I mentioned before, under this circumstance, the ADAMO routine will ! conviently load this unique row into the WIN for you. I proved that ! by printing out the mcHit WIN. ! Finally, we navigate from this calorimeter hit to dataHodo. ! You will see in the output that ok comes back FALSE. call NATGEN (mcHit, mcHit_digiTable, dataHodo, ok) write(6,908) 'NATGEN came back with ok = ', ok ! Now, navigating generalized relationships BACKWARDS. NAFGEN is ! again very similar to NAFREL. Recall that when using NAFREL, ! you had to first set up and index on the relationship column. ! Same here, EXCEPT, you need a more complex index, on BOTH columns ! of the generalized relationship. We already learned how to do this, ! using CREIND. colnam(1) = 'digiTable' colnam(2) = 'digiTable_' corder(1) = 'ASC' corder(2) = 'ASC' call CREIND (mcHit,inddigi2,'DigiOrder2',2,colnam,corder) ! The rest is just like NAFREL. In this example, we will navigate ! backwards from row #1 of dataHodo to the corresponding row of mcHit. write(6,901) 'Navigate dataHodo #1 BACKWARDS to mcHit', + ' BY digiTable, using NAFGEN' dataHodo_ID = 1 call NAFGEN (dataHodo, mcHit_digiTable, mcHit,inddigi2,i1,i2) call printwin_mchit ! Once more, I know that only one row has been found, so the mcHit WIN ! has been loaded up automatically. !===================================================================== ! LOOP OVER REMAINING EVENTS !===================================================================== ! We have already demonstrated everything we need to using the first ! record in the GAF, so there's really to read in the rest of them. ! But let's do it anyway. Remember, we do not usually know in advance ! how many records are in the GAF, so you have to watch the error code ! out of NEXGAF for the end-of-file signal. iok = 0 call NEXGAF (igaf, mcEvKey, iok) do while (iok.eq.0) write(6,'(/20x,a,i3,1x,a)') '<<<<< EVENT NUMBER ', + mcEvKey_ievent, '>>>>>' call FETGAF (igaf, mcEvKey, idfl, iok) call NEXGAF (igaf, mcEvKey, iok) enddo 999 continue !===================================================================== ! CLOSE THE GAF !===================================================================== call CLOGAF (igaf,iok) 900 format(/,20('='),1x,a,1x,20('=')/) 901 format(3x,a,a) 902 format(3x,a,i2,1x,a) 903 format(3x,a,i2,1x,a,i2,1x,a) 904 format(3x,a,i3,1x,a) 905 format(3x,a,a10,a) 906 format(8x,a,i1,a,f5.2) 907 format(8x,a,i1,a,f5.2) 908 format(8x,a,l1) 909 format(8x,a,i2,4x,a,i2) end !===================================================================== ! CODING EXAMPLES: PRINTING THE WINs of mcTrack, mcHit, and dataHodo !===================================================================== subroutine printwin_mcTrack implicit none #include "mcTrack.inc" write(6,900) mcTrack_ID, mcTrack_cName, mcTrack_iCharge, + mcTrack_P 900 format(8x,'mcTrack: ID = ',i2,4x,'cName = ',a4,4x, + 'iCharge = ',i2,4x,'P = ',f5.2) return end !--------------------------------------------------------------------- subroutine printwin_mcHit implicit none #include "mcHit.inc" write(6,900) mcHit_ID, mcHit_digiTable, mcHit_digiTable_, + mcHit_z 900 format(8x,'mcHit: ID = ',i2,4x,'digiTable = ',a8, + ',row ',i2,4x,'z = ',f6.1) return end !--------------------------------------------------------------------- subroutine printwin_dataHodo implicit none #include "dataHodo.inc" write(6,900) dataHodo_ID, dataHodo_cName, dataHodo_iWire, + dataHodo_iTDC 900 format(8x,'mcHit: ID = ',i2,4x,'cName = ',a4,4x, + ' iWire = ',i3,4x,'iTDC = ',i4) return end