/* This program will write a GAF = Generic ADAMO File based on the DDL structures in toyMC.ddl. The output GAF will be called toyMC.events. */ /* Note: When called from C, all ADAMO routines must be CAPITALIZED. */ /* ============================================================== */ /* INCLUDES AND DECLARATIONS */ /* ============================================================== */ #include #include #include /* ALL ADAMO routines require you to include the file partap.h It contains numerous ADAMO-specific definitions that you will need. We also need cfortran.h so that the Fortran in ADAMO can talk to the C in this program. */ #include "partap.h" #include "cfortran.h" /* Now include the structure (WCB in Fortran) definitions for all the tables you want to use. These include files were all created by the makeddl script, using the MAD program. */ #include "mcEvKey.h" #include "mcEvent.h" #include "mcBeam.h" #include "mcTrack.h" #include "mcVert.h" #include "mcHit.h" #include "dataPuls.h" #include "dataHodo.h" #include "dataCalo.h" /* Define some parameters */ const float ebeam = 27.5; const float q2min = 1.0; const float q2max = 20.0; const int nevents = 10; /* Declare some more variables */ int i, igaf, idfl, iok, ievent; int decay_vertex, idet; char cline[81]; /* Note that the variable "cline" will hold a string with 80 or fewer characters (last element of array is filled with null character) */ /* Declare function "notrandom" */ float notrandom(void); /* Start main body of program */ void main() { /* ============================================================== */ /* INITIALIZE ADAMO */ /* ============================================================== */ /* This is really easy. The INITAP routine created by MAD initializes ADAMO and also calls the CREOBJ routine to set up your personal data structures. */ INITAP(); /* ============================================================== */ /* OPEN A NEW GAF FILE */ /* ============================================================== */ /* We now create our GAF file using the routine CREGAF. */ sprintf(cline,"NAME=toyMC.events,DRIVER=FZ,FILFOR=EXCH"); CREGAF(igaf,mcEvKey,cline,iok); /* Now for some notes. A lot of this is generally useful for many ADAMO commands, so I won't need to repeat it again. */ /* The First Argument: igaf ------------------------ igaf will come back with the ADAMO id of your file. You will use this in all subsequent commands to refer to the GAF. ADAMO objects like tables, dataflows, selectors, ... are almost always referred to with such identifier numbers, as you will see. */ /* The Second Argument: mcEvKey ---------------------------- mcEvKey specifies the Key Table identifier for your GAF. Key tables were explained in the DDL file. The first variable in the structure mcEvKey (or any other table structure) is simply called mcevkey -- the table name in lowercase. After you run CREOBJ to create this and all your other tables, this first variable in each structure is automatically loaded up with the table identifier number. */ /* Let me prove this to you, and at the same time introduce another command, GETTDF, which returns the idetifier number of a table or dataflow given its name. */ printf("\n ========== TABLE IDENTIFIERS =========="); printf("\n\n What is the table identifier for mcEvKey\?"); printf("\n mcEvKey = %d", mcEvKey.mcevkey); printf("\n gettdf = %d", GETTDF("mcEvKey") ); /* The Third Argument: cline ------------------------- The third argument to CREGAF is a string, containing a comma-separated list of options. The first is the name of the file. The second and third specify the file format. The format of the gaf is determined by the DRIVER you select. The usual ones are RZ, FZ, and IE. Only the IE driver produces ASCII output that you can read, the other two produce compressed binary output. The usual format choice in HERMES software is FZ, with the modifier 'FILFOR=EXCH' (which modifies the way the driver behaves). However, the DAD library, which is a homegrown HERMES addition to ADAMO, supplies additional drivers. The vast majority of HERMES files are actually in DAD format. We will learn about that shortly. For now, we use one of the standard ADAMO drivers. */ /* Just as an illustration, this is what cline would look like if you wanted to write your file in IE format instead. */ /* sprintf(cline,"NAME=toyMC.ie,DRIVER=IE"); */ /* The Fourth Argument: iok ------------------------ iok comes back with a status code. If it is NON-zero, an error occurred. A good programmer checks each and every status code, and reponds. For example, here we can print out what happened, and stop the program. */ if (iok != 0) { printf("CREGAF failed"); exit(0); } /* You will see iok appear in many later comands. I will ignore all error checking after this for brevity. */ /* ================================================================= */ /* ********** START LOOP OVER EVENTS ********** */ /* ================================================================= */ for (ievent = 1; ievent <= nevents; ievent++) { printf("\n\n <<<<< EVENT NUMBER %d >>>>>", ievent); /* ================================================================= */ /* CLEAR ALL YOUR TABLES */ /* ================================================================= */ /* Introducing CLETAB and COUTAB ----------------------------- This example shows how CLETAB can be used to clear a single table, or an entire dataflow. We also introduce the function COUTAB, which counts the number of rows in a table. */ if (ievent == 2) { printf("\n\n ========== CLEARING TABLES 1 =========="); printf("\n\n Before clearing anything,"); printf(" mcEvent has %d rows,", COUTAB(mcEvent) ); printf(" mcTrack has %d rows", COUTAB(mcTrack) ); /* Here we clear one table, mcTrack */ CLETAB(mcTrack); printf("\n After clearing only mcTrack,"); printf(" mcEvent has %d rows,", COUTAB(mcEvent) ); printf(" mcTrack has %d rows", COUTAB(mcTrack) ); } /* Before loading up our event, though, we should clear ALL the tables. Fortunately, we grouped our tables into two dataflows, mcEvData and mcEvDigits. Let's clear mcEvData, which contains the mcEvent and mcTrack tables. */ idfl = GETTDF("mcEvData"); CLETAB(idfl); if (ievent == 2) { printf("\n After clearing dataflow mcEvData,"); printf(" mcEvent has %d rows,", COUTAB(mcEvent) ); printf(" mcTrack has %d rows", COUTAB(mcTrack) ); } /* Let's not forget to clear the tables from dataflow mcEvDigits ... In FORTRAN you can also use the integer function GETTDF directly as an argument to CLETAB as shown below, but for some reason it doesn't work in C so I've commented it out... */ /* CLETAB( GETTDF("mcEvDigits") ); */ /* Instead, let's do this... */ idfl = GETTDF("mcEvDigits"); CLETAB(idfl); /* Now a warning: If you recall from the ddl file, we created a third dataflow called 'mcEvents'. This dataflow simply contained the other two. Calling CLETAB on dataflow mcEvents will FAIL !!! CLETAB only clears the tables that are located IMMEDIATELY inside a dataflow, it will NOT navigate down a tree of dataflows! */ /* ================================================================ */ /* FILL YOUR TABLES WITH INFORMATION I: mcEvent and mcBeam */ /* ================================================================ */ /* Let's store some information to table mcEvent. We will spend some time with this table, to demonstrate a few things. */ /* Introducing the structure ------------------------- First, fill the structure with the information for one row. The variables of the structure are just a STORAGE AREA for one row's worth of table information. */ /* ... The ID column of the table always contains the row number you ... are working on. */ mcEvent.ID = 1; /* ... Store some random numbers in the kinematic fields. */ mcEvent.Nu = notrandom()*ebeam; mcEvent.Q2 = q2min + (q2max-q2min)*notrandom(); /* ... Remember that the mcEvent table has a link to the mcBeam table. ... This is a trivial link, since mcBeam only ever has one row, so ... there is only row we could possibly link to. ... The link is encoded as an ordinary integer variable in the ... structure. All we need to do is set this variable to the mcBeam ... row number we want to link to. */ mcEvent.mcBeam = 1; /* Introducing INSTAB ------------------ Now that the mcEvent structure contains all the information we need for this row, it's time to store the row in the table. Just putting information in the structure does NOT change the table, which is stored in ADAMO memory. We must explicitly tell ADAMO to load this row to the table, using the subroutine INSTAB. */ /* ... I'll prove it to you: although we've loaded the structure with ... information, ADAMO still has an empty table */ if (ievent == 1) { printf("\n\n ========== LOADING TABLES =========="); printf("\n\n Before INSTAB,"); printf(" mcEvent has %d rows", COUTAB(mcEvent) ); } /* ... INSTAB loads the structure information into memory. */ INSTAB(mcEvent); /* ... Let's check that the new row was indeed loaded. */ if (ievent == 1) { printf("\n After INSTAB,"); printf(" mcEvent has %d rows", COUTAB(mcEvent) ); } /* Introducing DELTAB and REPTAB ----------------------------- We now demonstrate the use of DELTAB, to delete a table row. */ if (ievent == 1) { /* ... All that DELTAB needs to know is: "which row do I delete?" ... It gets this information from the ID column of the structure. ... mcEvent_ID is ALREADY 1. */ DELTAB(mcEvent); printf("\n After DELTAB,"); printf(" mcEvent has %d rows", COUTAB(mcEvent) ); /* ... The information in the structure is still in place, so let's just ... reload the row to the table. */ INSTAB(mcEvent); printf("\n After the second INSTAB,"); printf(" mcEvent has %d rows", COUTAB(mcEvent) ); } /* Finally, we demonstrate REPTAB, to replace an existing row. */ if (ievent == 1) { REPTAB(mcEvent); printf("\n After REPTAB,"); printf(" mcEvent still has %d rows", COUTAB(mcEvent) ); } /* Loading the beam information to mcBeam is easy. */ mcBeam.ID = 1; mcBeam.ZVx = notrandom()*40. - 20.; mcBeam.EBe = ebeam; INSTAB(mcBeam); /* =============================================================== */ /* FILL YOUR TABLES WITH INFORMATION II: mcTrack and mcVert */ /* =============================================================== */ /* Now we move on to mcTrack, a table that will have more than one row. For each event, we will generate 4 tracks: the scattered beam positron, a K0_s particle, and two charged pions which are the decay products of the K0_s. In this section, we will also load up mcVert with the start and stop positions of each track. */ /* Introducing NULL variables -------------------------- As we load up the first track, we demonstrate the use of the intrinsic ADAMO variable INULL. INULL is simply an integer parameter whose value is set to some large number in the all-important ADAMO include file partap.h. Take a look at partap.h to see what I mean. Anyway, INULL is just a 'secret code' that you can use in any integer fields of your structure to indicate that the information in this field is null (i.e. unset, or irrelevant). The first track is the scattered beam positron. You recall that mcTrack has a named link called 'Parent', which we can use to link tracks corresponding to decay products to their parent particle. The scattered beam positron obviously has no parent, so we set the Parent link to INULL. */ mcTrack.ID = 1; mcTrack.iCharge = 1; C2FCBSTR("POSI",mcTrack.cName,0); /* see note below */ mcTrack.P = (ebeam - mcEvent.Nu); mcTrack.Parent = INULL; /* As you can see in partap.h, there are two similar 'secret codes' called RNULL and CNULL. You use these to set real and character variables respectively to null. */ /* Finally, a comment is in order about the strange command C2FCBSTR that appears above. All it is doing is copying the string "POSI" into the variable mcTrack.cName. However, this latter variable is part of an ADAMO table. Since ADAMO itself is written in Fortran, this variable will be stored in a F77 common block internal to ADAMO. The command C2FCBSTR comes from the cfortran.h file, and is required to properly store a C character string in any F77 character variable within a common block. If you use a regular C command like strcpy or sprintf to load up mcTrack.cName , bad things will happen and you will almost certainly get a memory-related error. */ /* Introducing NEXT ---------------- We are not quite done with this first row of mcTrack. We have to set the start and stop points of the track in mcVert, and link them to mcTrack. No problem. Remember, the structure information has to be explicitly loaded to the tables using INSTAB. */ /* The starting point of the scattered positron track is just the beam vertex. */ mcVert.ID = 1; mcVert.X = 0.; mcVert.Y = 0.; mcVert.Z = mcBeam.ZVx; mcTrack.startVert = 1; INSTAB(mcVert); /* For the stop point, we will just generate some random information and load it to the SECOND row of mcVert. Now, we KNOW this is going to be the second row, so we could explicitly set mcVert.ID = 2. But why keep track of row numbers ourselves? We can use instead another 'secret code' from partap.h, which is the variable NEXT. When you set the ID column of the table (i.e. the row number) to NEXT, INSTAB knows that you want to load the WCB information to the next free row in the table. */ mcVert.ID = NEXT; mcVert.X = notrandom()*10.; mcVert.Y = notrandom()*10.; mcVert.Z = notrandom()*800.; if (ievent == 1) { printf("\n\n ========== USING NEXT =========="); printf("\n\n Before INSTAB,"); printf(" mcVert.ID = %d (which is NEXT)", mcVert.ID); } INSTAB(mcVert); /* After INSTAB, you can see that ADAMO has conveniently set the ID variable of the structure to the new row number for you. In our case, this is 2. */ if (ievent == 1) { printf("\n After INSTAB,"); printf(" mcVert.ID = %d (should be 2)", mcVert.ID); } /* With this mcVert row number in hand, we can fill the last link of mcTrack, and finally store the first track to the table. */ mcTrack.stopVert = mcVert.ID; INSTAB(mcTrack); /* Introducing PRITAB and PRIROW ----------------------------- Let's take this opportunity to introduce some useful commands for printing the contents of your tables. Let's get ADAMO to display on screen the current content of the mcVert table. */ if (ievent == 1) { printf("\n\n ========== PRINTING FACILITIES =========="); /* The subroutine PRITAB can be used to print all or part of a table. Following is the most common invocation of PRITAB. It uses more 'secret codes' from partap.h to print the entire table. These codes are ID, MINC, MAXC, and ALLCOL. */ printf("\n\n Example 1: PRITAB the whole table \n"); PRITAB(mcVert,ID,MINC,MAXC,ALLCOL); /* You can also use the third and fourth arguments to PRITAB to specify a range of rows to print. For example, let's print out only the second row. */ printf("\n\n Example 2: PRITAB the second row \n"); PRITAB(mcVert,ID,2,2,ALLCOL); /* Alternatively, the subroutine PRIROW can be used to print the second row. The last parameter of this subroutine must be set to the secret code VER. This specifies 'vertical' printing mode, and as the ADAMO manual says, no other modes are supported at present. */ printf("\n\n Example 3: Use PRIROW to do the same thing \n"); PRIROW(mcVert,ID,2,VER); } /* Introducing CLENXT ------------------ */ if (ievent == 1) { printf("\n\n ========== USING CLENXT ========== "); /* Before we continue with the remaining tracks, we need to learn something more about how NEXT works. An internal 'next row' counter is maintained for each table, and is incremented whenever you add a new row to the table. But what happens when you delete rows? Unfortunately, the NEXT counter is NOT modified when you do this! Let's demonstrate. We will delete the second row of mcVert, and then insert it again using mcVert.ID = NEXT. As you will see, the second row of the table is left blank, and your information gets stored as the THIRD row! Oops... */ mcVert.ID = 2; DELTAB(mcVert); mcVert.ID = NEXT; INSTAB(mcVert); printf("\n\n We just deleted and reinserted row #2..."); /* Remember, after callling INSTAB, mcVert_ID is loaded with the new row number. This row number is now 3, not 2: */ printf("\n After INSTAB,"); printf(" mcVert.ID = %d", mcVert.ID); printf("\n Table mcVert now looks like this: \n"); PRITAB(mcVert,ID,MINC,MAXC,ALLCOL); /* We need to fix this. First we delete row 3. */ DELTAB(mcVert); /* We still want to reinsert our row #2. We COULD explicitly set mcVert.ID = 2, and then call INSTAB. But what we really need to do is CLEAR the internal next counter after our deletions. CLENXT does this for us. */ CLENXT(mcVert); printf("\n We just used CLENXT to reset the next counter"); mcVert.ID = NEXT; INSTAB(mcVert); printf("\n After INSTAB,"); printf(" mcVert.ID = %d", mcVert.ID); printf("\n Table mcVert now looks like this: \n"); PRITAB(mcVert,ID,MINC,MAXC,ALLCOL); } /* Introducing INSENT and INSREL ----------------------------- Next we load up track 2, which will be an unstable K0_s particle. This particle also originates from the primary scattering vertex, and will decay after about 1 m of flight. */ mcTrack.ID = NEXT; mcTrack.iCharge = 0; C2FCBSTR("K0_S",mcTrack.cName,0); mcTrack.P = mcEvent.Nu * notrandom(); mcTrack.Parent = INULL; /* ... We have to generate the stop vertex and store it in mcVert. ... Let's also store this mcVert row number for future reference, ... since we will need it as the start point for the decay pions. */ mcVert.ID = NEXT; mcVert.X = notrandom()*10.; mcVert.Y = notrandom()*10.; mcVert.Z = notrandom()*100.; INSTAB(mcVert); decay_vertex = mcVert.ID; /* ... Finally store the vertex links. This track starts at the primary ... vertex, which is already stored as row 1 of mcVert. We just ... created the stop vertex. */ mcTrack.startVert = 1; mcTrack.stopVert = decay_vertex; /* OK, now some new ADAMO commands. All information for this track is in the structure, so we could just call INSTAB(mcTrack) like before. However, let's illustrate how INSENT works: */ if (ievent == 1) { printf("\n\n ========== INSENT AND INSREL =========="); INSENT(mcTrack); /* NOW what did we do? INSENT is sort of a subset of INSTAB. Instead of loading ALL information to the table, it only loads ESET information. In other words, it loads all table entries that are NOT relationships. Let's take a look at mcTrack row 2 ... you'll see that although we HAVE set the mcTrack.startVert and mcTrack.stopVert links in the structure, they did NOT get loaded up to the table in memory. */ printf("\n\n After INSENT,"); printf(" mcTrack row #2 still has NULL links: \n"); PRITAB(mcTrack,ID,2,2,ALLCOL); /* The subroutine INSREL is the OTHER subset of INTAB. It loads only relationships (RSET information). The 3 arguments of INSREL are: (arg1) The id of table 1, FROM WHICH the link originates (arg2) The link variable from table 1 (arg3) The id of table 2, TO WHICH the link points These three arguments are not enough to specify which row from table 1 is to linked to which row from table 2. INSREL gets that information from the ID columns of the structures. */ /* ... link current mcTrack row to mcVert row 1 via startVert */ mcVert.ID = 1; INSREL(mcTrack,mcTrack.startVert,mcVert); /* ... link current mcTrack row to mcVert row 'decay_vertex' via ... stopVert */ mcVert.ID = decay_vertex; INSREL(mcTrack,mcTrack.stopVert,mcVert); /* The links are now stored to the table. Let's check. */ printf("\n After INSREL,"); printf(" mcTrack row #2 has filled links: \n"); PRITAB(mcTrack,ID,2,2,ALLCOL); } /* I won't demonstrate REPENT, REPREL, DELENT, or DELREL. As you can guess, they are the ESET and RSET subsets of REPTAB and DELTAB. */ /* Introducing NULWIN ------------------ Tracks 3 and 4 are the decay pions from the K0_s. Before we generate these tracks, a small demonstration. NULWIN is a useful subroutine that clears all the variables in a structure in one shot. */ if (ievent == 1) { printf("\n\n ========== USING NULWIN =========="); /* ... before we call NULWIN, the information in the structure variables ... is simply what is left over from the last row you stored */ printf("\n\n Before NULWIN,"); printf(" mcTrack.iCharge = %d", mcTrack.iCharge); /* ... After NULWIN, all variables are set to INULL, RNULL, or CNULL, ... depending on their type */ NULWIN(mcTrack); printf("\n After NULWIN,"); printf(" mcTrack.iCharge = %d", mcTrack.iCharge); } /* Anyway, let's store the last two tracks, and make stop vertices for them. */ for (i = 1; i <= 2; i++) { mcTrack.ID = NEXT; if (i == 1) mcTrack.iCharge = -1; else mcTrack.iCharge = 1; C2FCBSTR("PION",mcTrack.cName,0); mcTrack.P = mcEvent.Nu * notrandom(); mcTrack.Parent = 2; mcTrack.startVert = decay_vertex; mcVert.ID = NEXT; mcVert.X = notrandom()*10.; mcVert.Y = notrandom()*10.; mcVert.Z = notrandom()*800.; INSTAB(mcVert); mcTrack.stopVert = mcVert.ID; INSTAB(mcTrack); } /* ================================================================= */ /* FILL YOUR TABLES WITH INFORMATION III: mcHit */ /* ================================================================= */ /* In this section, we will store detector hits and digitizations. This is just a toy Monte Carlo, so for brevity we will only consider the hits produced by track #1, the scattered beam positron. Also, we will generate only one hit in each of the following detectors: FC, BC, H1, H2, and CALO. As explained in the ddl file, each hit is associated with a 'digitization', which is a simulation of the detector's response. The digitizations are stored in different tables depending on which type of detector was hit. */ /* So here we go. We loop over the 5 detectors, generating one hit and associated digitization per detector. */ for (idet = 1; idet <= 5; idet++) { /* Generate a random position for the hit. (This is just a silly toy program, these positions are goofy of course :)) */ mcHit.ID = NEXT; mcHit.X = notrandom()*200.; mcHit.Y = notrandom()*200.; mcHit.Z = notrandom()*800.; /* The mcHit table has a couple links. One of them is to mcTrack. We are only generating hits for the first track, so this link just points to row 1. */ mcHit.mcTrack = 1; /* Generalized relationships ------------------------- Now let's generate the digitization for this hit. The first two hits correspond to the wire chambers FC and BC Wire chamber digitizations go to table dataPuls. */ if (idet >= 1 && idet <= 2) { dataPuls.ID = NEXT; dataPuls.iWire = (int) ( (notrandom()*500.) + 1. ); dataPuls.iTDC = (int) (notrandom()*8192.); if (idet == 1) C2FCBSTR("FC",dataPuls.cName,0); else C2FCBSTR("BC",dataPuls.cName,0); INSTAB(dataPuls); /* Now that the digitization is stored to dataPuls, we can complete the current row of the mcHit table by linking it to the new digitization. mcHit is linked to dataPuls, dataHodo, and dataCalo via a GENERALIZED RELATIONSHIP called 'digiTable'. Not only do we have to supply the row number to link to, we also have to show which TABLE we are linking to. The structure provides two variables for this: */ /* ... this character variable gets the NAME of the table to link to */ C2FCBSTR("dataPuls",mcHit.digiTable,0); /* ... this integer variable gets the row number to link to */ mcHit.digiTable_ = dataPuls.ID; /* The structure is now complete. Store this row to mcHit. */ INSTAB(mcHit); } /* end first if statment */ /* Hits 3 and 4 correspond to the hodoscopes H1 and H2. Hodoscope digitizations go to table dataHodo. */ else if (idet >= 3 && idet <= 4) { dataHodo.ID = NEXT; dataHodo.iWire = (int) ( (notrandom()*42.) + 1. ); dataHodo.iTDC = (int) (notrandom()*8192.); dataHodo.iADC = (int) (notrandom()*8192.); if (idet == 3) C2FCBSTR("H1",dataHodo.cName,0); else C2FCBSTR("H2",dataHodo.cName,0); INSTAB(dataHodo); /* Remember INSENT and INSREL? They were subsets of INSTAB, for storing only ESET or RSET information. Well, there is one more subset of INSTAB, called INSGEN. This is used to store GENERALIZED RELATIONSHIPS. Let's illustrate this different manner of storing our new row to mcHit. */ /* ... store the ESET information */ INSENT(mcHit); /* ... store the link to mcTrack, which is a normal relationship */ mcTrack.ID = 1; INSREL(mcHit,mcHit.mcTrack,mcTrack); /* ... Store the generalized link to dataHodo. ... INSGEN needs the target row number to be stored in dataHodo.ID. ... This is already true, since we just loaded up the new row to ... dataHodo. */ INSGEN(mcHit,mcHit.digiTable,dataHodo); } /* end second if statement */ /* Finally, hit 5 correspond to the calorimeter. Calo digitizations go to table dataCalo. */ else { dataCalo.ID = NEXT; dataCalo.iCell = (int) ( (notrandom() * 840.) + 1. ); dataCalo.rPuls = notrandom()*ebeam; C2FCBSTR("CALO",dataCalo.cName,0); INSTAB(dataCalo); C2FCBSTR("dataCalo",mcHit.digiTable,0); mcHit.digiTable_ = dataCalo.ID; INSTAB(mcHit); } /* end second else statement */ } /* end for loop over 5 detectors */ /* Let's prove all this worked by printing out the mcHit table */ if (ievent == 1) { printf("\n\n ========== GENERALIZED RELATIONSHIPS"); printf(" ========== \n\n"); PRITAB(mcHit,ID,MINC,MAXC,ALLCOL); } /* ================================================================== */ /* STORE THE EVENT RECORD TO THE GAF */ /* ================================================================== */ /* We have finally loaded all the information for this event to our tables. It's time to store this record to the GAF: although the tables are now stored in memory, they have not yet been sent to the output file. We do this using INSGAF and the key table. Remember, the key table? It is a special table which does two things: (1) It contains enough information to uniquely identify each record (in our case, this is just the event number, and a run number) (2) Its last variable cName is used to tell INSGAF which dataflow to store to the GAF. We want to load all tables to the GAF, so we specify the uber-dataflow mcEvents here. */ mcEvKey.ID = 1; mcEvKey.iRun = 1; mcEvKey.iEvent = ievent; C2FCBSTR("mcEvents",mcEvKey.cName,0); INSGAF(igaf,mcEvKey,iok); /* Note that we did not have to call INSTAB to store this information to the key table. The key table is NOT an ordinary table. Its one row of information appears in a special spot at the top of each GAF record. It does not get stored as a regular table. */ /* ================================================================= */ /* ********** END LOOP OVER EVENTS ********** */ /* ================================================================= */ } /* exciting eh? */ /* ================================================================= */ /* CLOSE THE GAF */ /* ================================================================= */ printf("\n\n"); CLOGAF(igaf,iok); /* ================================================================= */ /* END MAIN BODY OF PROGRAM */ /* ================================================================= */ } /* four comment lines for one squiggly bracket? pretty silly... */ /* ================================================================= */ /* CODING EXAMPLE: the NOTRANDOM number generator :) */ /* ================================================================= */ /* Before defining the function, declare a few more variables */ const float increment = 0.09; float current_number; int first_call = 1; int first_demo = 1; float demo; double junk; /* Write code for notrandom function... */ float notrandom(void) { if (first_call == 1) { current_number = 0.; first_call = 0; } current_number = current_number + increment; if (current_number > 1. && first_demo == 1) { printf("\n\n ========= NOTRANDOM coding example ========="); printf("\n These three commands do the same thing"); demo = modf(current_number, &junk); printf("\n Using mod: %f", demo); demo = current_number - (int) (current_number); printf("\n Using int: %f", demo); if (current_number > 1.) { demo = current_number - 1.0; printf("\n Using if: %f", demo); } first_demo = 0; } current_number = modf(current_number, &junk); return current_number; }