Jump to content

Adjusting Variables at Run-Time: Difference between revisions

From Luter 345 Experiments
en>Miller
 
Brash99 (talk | contribs)
m 1 revision imported
 
(No difference)

Latest revision as of 17:58, 24 December 2024

Summary[edit | edit source]

When running simulations, it is common to want to change some aspects of the simulation. This can be cumbersome, because changing any of the source files requires the program to be recompiled. A convenient solution to this problem is to create a Variables class and input file that contain any variables that may need to be manipulated. Then the main method must be adjusted to read in these variables at run time. This way, the input file can be manipulated between runs without having to recompile.

The Main[edit | edit source]

The existence of a main method is essential to the inter-workings of the controlling class. This is true for most major object oriented programming languages like Java and C++. Geant follows suit by requiring the user to create their own main method. Below I will highlight the key items that are required in a minimal main method.

1. Importance of G4RunManager

  • Constructs major manager classes of GEANT4 - Completed in the Constructor
 G4RunManager* runManager = new G4RunManager;
  • Manages initialization procedures including methods in user initialization classes - Invoke Initialize()
 // Initialize G4 kernel
 runManager->Initialize();
  • Manages event loops - Invoke beamOn()
  • Terminates major manager classes of GEANT4 - Completed in the Destructor
 delete runManager;

2. Mandatory User Classes

  • Detector Construction - Materials & Geometry
runManager->SetUserInitialization(new S1DetectorConstruction());
  • Physics List - Particles & Physics Processes
 // Physics list
 G4VModularPhysicsList* physicsList = new QBBC;
 physicsList->SetVerboseLevel(0);
 runManager->SetUserInitialization(physicsList);
  • Primary Generator Action - Make Primary Event
//User action initialization
 runManager->SetUserInitialization(new S1ActionInitialization());

Input Files[edit | edit source]

Example: Initialization File

#X-length of both scintillators (in cm)
scintX 30.5
#Y-length of both scintillators (in cm)
scintY 17.0
#Z-length of both scintillators (in cm)
scintZ 1.0
#height(POSITION) of bottom scintillator (in cm)
bottomScintPos -30.0
#height(POSITION) of top scintillator (in cm)
topScintPos 30
#thickness of the Lead layer (in cm)
blockThickness 15.0
#the material the block will be made of (must be a valid G4Material name)
blockMaterial G4_Pb
#name of the output file(ONLY THE BASE NAME, THICKNIESS WILL BE ADDED AS A SUFFIX IN THE PROGRAM)
#outFileBase S1_thickness_
outFileBase S1_uncert_
#the run number (for finding an uncertainty in positron count)
runNum 10

As seen above, the initialization file is essentially a list of all variables in the program that may be necessary to change between runs. The benefit to using this type of file is that it can be changed without having to recompile the program. Obviously it is crucial to know what type these variable should be, what units the number should be in if applicable, and what order these variables should be in. The hash-tagged lines are comments, and should be used gratuitously if some other person may end up using the program so the requirements of the input file are clear.

Input files get read at run-time by the variables class via the main file. In other words, the main calls a read file function of the variables class that then reads and saves the appropriate variables. Typically this is the first, or one of the first, actions to occurs in the main. A quick example of the line in the main is provided below, and a more robust explanation of the variables class is provided in another section farther below.

int main(int argc,char** argv)
{
     //The first object to load should be the variables, since they are read externally
     //Create a variables object to read and store the variables
     Variables = new S1Variables();

     //Insert the input file here, ours is called S1.ini
     G4String dataname = "S1.ini";

     //call the function to read the data from the file
     Variables->loadFromFile(dataname);

Variables Class[edit | edit source]

The variables class is the class that allows use of an input file and other classes to use those variables. In this class, all desired variables (ones that will be used throughout the simulation, often in different classes) should be defined with appropriate getters and setters, as well as a read function. This read function is the crux of the whole input file process, and thus will be focused upon heavily here.

Below are a few example sections of a variables class, and explanations of the code are provided after.

//this example requires the use of a string stream, a c++ class
#include <sstream>

//the following are definitions of some functions to read in the variables from the file 
#define READBOOL(myx) \
                               if (buf == #myx) \
                               { \
                                       ss >> wert; \
                                       myx = ReadBoolean(wert); \
                               }

#define READDOUBLE(x) \
                               if (buf == #x) \
                               { \
                                       ss >> doublewert; \
                                       x = doublewert; \
                               }

#define READINT(x) \
                               if (buf == #x) \
                               { \
                                       ss >> intwert; \
                                       x = intwert; \
                               }

#define READSTRING(x) \
                               if (buf == #x) \
                               { \
                                       ss >> x; \
                               }

#define READVECTOR(v) \
                               if (buf == #v) \
                               { \
                                       G4double x,y,z; \
                                       if (ss >> x >> y >> z) \
                                       v = G4ThreeVector(x, y, z); \
                               }

//this is the default constructor
S1Variables::S1Variables()
{
     //Any defaults should be set in this space in case loadFromFile() fails
}

G4bool S1Variables::loadFromFile(G4String FileName)
{
     //reads from some input file(S1.ini), which should list all variables in appropriate order
     ifstream infile;
     G4String line;
     infile.open(FileName);
     if (infile)
     {
          G4String outFileBase;
          G4String outFileTag;
          while (!infile.eof())
          {
               if (getline(infile,line))
               {
                     G4String buf;
                     G4String wert;
                     G4double doublewert;
                     G4int intwert;
                     stringstream ss(line);
                     ss >> buf;
                     READDOUBLE(scintX);
                     READDOUBLE(scintY);
                     READDOUBLE(scintZ);
                     READDOUBLE(bottomScintPos);
                     READDOUBLE(topScintPos);
                     READINT(blockThickness);
                     READSTRING(blockMaterial);
                     READSTRING(outFileBase);
                     READSTRING(outFileTag);
                     READINT(runNum);
               }
          }

As stated earlier, all the variable names are already defined in the associated header file but are not initialized until the readfile function is executed. If this function fails, as occasionally happens when performing file io, it is always a good idea to initialize with some defaults in the constructor so the program can will not fail altogether. The defined functions at the top are crucial to the function as well as the string stream class, and explanations of these features will not be discussed here, as better and more in depth discussions can be found on any number of c++ reference pages.

From the piece of the example presented, it is clear how this function works: An io stream is opened and that stream passes lines to the string stream, which is then parsed by the string stream and the corresponding data is read and saved by the predefined functions at the top. For this to work properly it is of great importance that the input file being read adhere to strict structure rules, and the example input file found in another section above can be used as reference.

Once the above code is executed the variables have been read and can now be used throughout the simulation. In some cases the variables may require further manipulation. One common example of this is an output file name. In the code presented above, there are two variables read in called "outFileBase" and "outFileTag" that were used, along with some other variables found above, to create a file name that would later hold the nTuple produced by the simulation. Below is how this process was coded, albeit in a round-about way. Again for other methods of concatenation use your favorite c++ reference site.

stringstream holdRunNum("");
stringstream holdThick("");
holdRunNum << runNum;
holdThick << blockThickness;
outFile = (outFileBase + holdThick.str() + outFileTag + holdRunNum.str() + ".root");

Here "outFile" is the actual variable held by the variables class, but there is no convenient way to craft the complete file name prior to reading the variables in. If running in batch mode this is a simple trick to automate the file naming process according to a convention, which makes further use of those files simple (i.e. looping through files in a ROOT macro). I will admit this string stream method is a poor method to use for concatenation so it may be better to find a new method when crafting your own simulation; However, it does work so if in a bind, feel free to utilize.