#define MAXTRACKS 12 #include "ZMutility/ZMenvironment.h" #include "PhysicsVectors/PhysicsVectors.h" ZM_USING_NAMESPACE( zmpv ) #ifdef USE_ROOT #include "HepTuple/HepRootFileManager.h" #endif #include "HepTuple/HepHist1D.h" #include "HepTuple/HepHist2D.h" #include "HepTuple/HepHistProf.h" #include "HepTuple/HepNtuple.h" ZM_USING_NAMESPACE( zmht ) #include "CLHEP/Random/Randomize.h" #include "CLHEP/Units/PhysicalConstants.h" #include "ZMutility/FixedTypes.h" #include "ZMutility/iostream" #include "ZMutility/fstream" ZM_USING_NAMESPACE( zmfxt ) #include "ZMutility/iostream" USING( std::cin ) USING( std::cerr ) USING( std::endl ) #include #include #ifndef ISOCXX__ISOCXX #include #endif #include USING( std::vector ) #include USING( std::string ) // Declare the HepFileManager pointer at this level to // make it available at file scope. HepFileManager *tManager; // Define a handy function to compute the energy of a particle for a given // momentum and mass. Float8 energy( SpaceVector p, Float8 mass ) { return sqrt((p.dot(p)+mass*mass)); } int main(Int4 argc, char** argv) { Int4 nevents=3000; bool first = false; void generate( Int4, bool, string ); bool newFile( string ); // Sort out which flavor of manager the user wants. // Default to HBook if nothing is said. string fileName; string topdir = "zippo"; string mgr_type = "ROOT"; // Initialize a manager and open an output file. Note that this is now the // only direct invocation of an manager-specific method. Using it here // triggers everything else even though all subsequent references below are // to things like HepHistogram. // Note that the top directory on the output file will have the same name as // the file itself. There's no choice in this but it isn't a big deal // since the name of the top directory isn't actually recorded anywhere. //fileName = "/var/tmp/hepToyz.root"; fileName = "hepToyz.root"; topdir = "hepToyz.root"; first = newFile( fileName ); HepFileManager::mode_ mymode = static_cast (HepFileManager::HEP_REFRESH | HepFileManager::HEP_UPDATE); tManager = new HepRootFileManager( fileName, mymode, topdir ); // Book and fill some histograms in a function named generate. generate(nevents,first,topdir); delete tManager; return 0; } void generate(Int4 nevents, bool first, string topdir) { Int4 numTracks; Int4 sumTracks=0; Int4 sumPairs=0; Int4 StartSeed=5736321; Int4 LuxLevel=3; Float4 W=1.0; Float8 Etamax=5.0; Float8 piMass=0.13956995; Float8 KMass=0.493677; Float8 ResMass=50.00; Float8 ResWidth=8.0; Float8 Ptmean = 10.0; Float8 Pt, Eta, Phi, Eta1, Eta2; Float8 Pmag, Px, Py, Pz, Cost; Float8 PtArray[MAXTRACKS]; Float8 EtaArray[MAXTRACKS]; Float8 PhiArray[MAXTRACKS]; Float8 PxArray[MAXTRACKS]; Float8 PyArray[MAXTRACKS]; Float8 PzArray[MAXTRACKS]; Float8 defaultValue = -999.9; char resume; RanluxEngine someEngine; RandFlat Rflt(someEngine); RandGauss Rgaus(someEngine); RandExponential Rexp(someEngine); RandBreitWigner RBW(someEngine); void pxyz(Float8, Float8, RandFlat &, Float8 *, Float8 *, Float8 *, Float8 *); // Book a gang of histograms. This time, we declare them as HepHistograms // and let the virtual functions sort out what kind of histograms we really // mean to produce. // // All the uproar with " p = first ? tManager-> ... : tManager-> ..." // is a trick to switch between a new run and a continuation run on the // basis of a single switch variable and still keep everything in scope. // A simple // if(first) { // HepHist1D & ppt = tManager-> hist1D(... // : // } else { // HepHist1D & ppt = tManager-> retrieveHist1D(... // : // } // doesn't do it because the { }'s define a scope for the declarations // inside, but it's the wrong scope. Consequently, we resort to this // monstrosity. cerr << "\nGenerate some fake events and populate some histograms." << endl; if( first ) tManager->mkdir("junk"); tManager->cd("junk"); HepHist1D & ppt= first ? tManager->hist1D("Pt distribution", 50, 0.0f, 50.0f) : tManager->retrieveHist1D("Pt distribution" ); HepHist1D & peta= first ? tManager->hist1D("Eta distribution", 60, -6.0f, 6.0f) : tManager->retrieveHist1D("Eta distribution" ); HepHist1D & pphi= first ? tManager->hist1D("Phi distribution", 64, -3.2f, 3.2f) : tManager->retrieveHist1D("Phi distribution" ); HepHist1D & pmom= first ? tManager->hist1D("P distribution", 50, 0.0f,100.0f) : tManager->retrieveHist1D("P distribution" ); HepHist1D & ppx= first ? tManager->hist1D("Px distribution", 50, -25.0f, 25.0f) : tManager->retrieveHist1D("Px distribution" ); HepHist1D & ppy= first ? tManager->hist1D("Py distribution", 50, -25.0f, 25.0f) : tManager->retrieveHist1D("Py distribution" ); HepHist1D & ppz= first ? tManager->hist1D("Pz distribution", 50, -50.0f, 50.0f) : tManager->retrieveHist1D("Pz distribution" ); HepHist1D & pcos= first ? tManager->hist1D("Cos(opening angle)", 50, -1.0f, 1.0f) : tManager->retrieveHist1D("Cos(opening angle)" ); HepHist1D & pmas= first ? tManager->hist1D("K pi Effective Mass", 50, 0.0f,100.0f) : tManager->retrieveHist1D("K pi Effective Mass" ); HepHistProf & prof= first ? tManager->histProf("Mass(Cost) Profile",50, 0.0f,100.0f, -1.0f, 1.0f," ") : tManager->retrieveHistProf("Mass(Cost) Profile" ); HepHist2D & psct= first ? tManager->hist2D("Mass vs cos(Theta)", 50, 0.0f,100.0f, 50, -1.0f, 1.0f) : tManager->retrieveHist2D("Mass vs cos(Theta)" ); HepHist1D & pntk= first ? tManager->hist1D("Number of tracks per event",20, 0.0f, 20.0f) : tManager->retrieveHist1D("Number of tracks per event"); // Instantiate an ntuple, declare it to be columnwise (this is default // anyhow) and tell the manager where to find the data items when we tell // it to capture. This is a handy way to do this. We establish a reference // to the data item here and give the item a value when the program logic // warrants it. After all items have been assigned a value, we tell the // ntuple machinery to grab everything. This works because the location // of the data doesn't change - just the value sitting in that location. // There's a lot of action here, so we walk through it carefully. // The whole business with the number and size of buffers is currently up // for discussion so we don't mess with any of that here. It's too much like // asking for trouble and we have enough without asking for more. HepNtuple & tupl = first ? tManager->ntuple("Kinematics_CWN") : tManager->retrieveNtuple("Kinematics_CWN"); HepHist1D & cmppx= first ? tManager->hist1D("CM pion Px distribution", 50, -50.0f, 50.0f) : tManager->retrieveHist1D("CM pion Px distribution" ); HepHist1D & cmppy= first ? tManager->hist1D("CM pion Py distribution", 50, -50.0f, 50.0f) : tManager->retrieveHist1D("CM pion Py distribution" ); HepHist1D & cmppz= first ? tManager->hist1D("CM pion Pz distribution", 50, -50.0f, 50.0f) : tManager->retrieveHist1D("CM pion Pz distribution" ); tupl.setColumnWise(); tupl.setDiskResident(); // If this is the first of a series of runs, establish the random number // seed. Otherwise, restore the state as of the end of the previous run. if( first ) { someEngine.setSeed( StartSeed, LuxLevel ); } else { someEngine.restoreStatus("hepToyz.config"); } if( first ) { cerr << "\nHistograms and one CWNtuple instantiated." << endl; } else { cerr << "\nHistograms and one CWNtuple re-instantiated." << endl; } // N. B. In addition to defining the ntuple block names and column names, // these declaration also indirectly declare the data types of the // corresponding items, either through the type of the data item, // the type of the default value or the type of the referenced item. // This is fairly subtle and needs some care. if( first ) { // Define a block named Tracks and a column named Ntracks. In addition, // declare this column to contain an index variable and specify it's range. tupl.columnAt("TRACKS::Ntracks",&numTracks,(Int4)1); tupl.span("TRACKS::Ntracks",0,MAXTRACKS); // specify nametag // Declare a column array named Pt and say that it is a variable length // array indexed by Ntracks and has maximum dimension MAXTRACKS. Repeat // this for each column within the Tracks block. Note that we specifically // cast the default value as a Float8. This fixes the type of the data. // There are a number of variations on how to declare indexed column arrays. // We use several of them here to demonstrate some of the possibilities. tupl.columnArray("TRACKS::Pt", 1, defaultValue); tupl.dimension("TRACKS::Pt",MAXTRACKS); // specify nametag tupl.index("TRACKS::Pt","Ntracks"); tupl.columnArray("TRACKS::Eta", 1, defaultValue); tupl.dimension(MAXTRACKS); // default nametag to "current" tupl.index("TRACKS::Eta","Ntracks"); tupl.columnArray("TRACKS::Phi", 1, defaultValue); tupl.dimension(MAXTRACKS); tupl.index("TRACKS::Phi","Ntracks"); // specify nametag tupl.columnArray("TRACKS::Px", 1, defaultValue); tupl.dimension(MAXTRACKS); tupl.index("Ntracks"); // default nametag to "current" // Two-fold method cascade since each returns another reference tupl.columnArray("TRACKS::Py", 1, defaultValue).dimension(MAXTRACKS); tupl.index("TRACKS::Py","Ntracks"); // Three-fold method cascade since each returns another reference tupl.columnArray("TRACKS::Pz", 1, defaultValue).dimension(MAXTRACKS).index("Ntracks"); } // For a re-instantiation, most of the needed information is obtained from // the file itself. However, we always need to tell the code where to find // the data items and what default values to use. That information is not // and, in the case of the pointers to the data, can not be recovered from // the file. Float8* currentDefault = new Float8; tupl.columnAt("TRACKS::Pt", &PtArray[0] ); if( tupl.columnDefault("TRACKS::Pt", currentDefault ) ) { cerr << "\nDefault value for TRACKS::Pt has been previously set to " << *currentDefault << endl; } else { cerr << "\nNo default value has been defined for TRACKS::Pt. Set it now to " << defaultValue << endl; tupl.setColumnDefault( "TRACKS::Pt", defaultValue ); } tupl.columnAt("TRACKS::Eta",&EtaArray[0] ); if( tupl.columnDefault("TRACKS::Eta", currentDefault ) ) { cerr << "Default value for TRACKS::Eta has been previously set to " << *currentDefault << endl; } else { cerr << "No default value has been defined for TRACKS::Eta. Set it now to " << defaultValue << endl; tupl.setColumnDefault( "TRACKS::Eta", defaultValue ); } tupl.columnAt("TRACKS::Phi",&PhiArray[0] ); if( tupl.columnDefault("TRACKS::Phi", currentDefault ) ) { cerr << "Default value for TRACKS::Phi has been previously set to " << *currentDefault << endl; } else { cerr << "No default value has been defined for TRACKS::Phi. Set it now to " << defaultValue << endl; tupl.setColumnDefault( "TRACKS::Phi", defaultValue ); } tupl.columnAt("TRACKS::Px", &PxArray[0] ); if( tupl.columnDefault("TRACKS::Px", currentDefault ) ) { cerr << "Default value for TRACKS::Px has been previously set to " << *currentDefault << endl; } else { cerr << "No default value has been defined for TRACKS::Px. Set it now to " << defaultValue << endl; tupl.setColumnDefault( "TRACKS::Px", defaultValue ); } tupl.columnAt("TRACKS::Py", &PyArray[0] ); if( tupl.columnDefault("TRACKS::Py", currentDefault ) ) { cerr << "Default value for TRACKS::Py has been previously set to " << *currentDefault << endl; } else { cerr << "No default value has been defined for TRACKS::Py. Set it now to " << defaultValue << endl; tupl.setColumnDefault( "TRACKS::Py", defaultValue ); } tupl.columnAt("TRACKS::Pz", &PzArray[0] ); if( tupl.columnDefault("TRACKS::Pz", currentDefault ) ) { cerr << "Default value for TRACKS::Pz has been previously set to " << *currentDefault << endl; } else { cerr << "No default value has been defined for TRACKS::Pz. Set it now to " << defaultValue << endl; tupl.setColumnDefault( "TRACKS::Pz", defaultValue ); } // Finally, print out the block descriptor string that this produced. cerr << "\nPrint the chForm string for Block TRACKS to see what we got." << endl; cerr << tupl.blockFormat("TRACKS") << endl; // Release everything so we can define values for all quantities. tupl.clearDataBlock("TRACKS"); // Put a useless histogram in another subdirectory and make some entries. // This is just to verify that all that machinery works as advertised. if( first) tManager->mkdir("trash"); tManager->cd("trash"); HepHist1D & xxx = first ? tManager->hist1D( "Gaussian distribution", 60, -6.0f, 6.0f ) : tManager->retrieveHist1D("Gaussian distribution"); for( Int4 l = 0; l < 10000; ++l ) { xxx.accumulate((Float4)Rgaus.fire(0.5,1.25),W); } // The first time for a newly opened file, write everything instead of // doing a simple release. This seems to prepare the file to receive // periodic updates. if( first ) { tManager->write(); } else { xxx.release(); pntk.reset(); } tManager->cd( string("..") ); string dirlist = tManager->ls("//"+topdir,"r"); cerr << "\nMake a full directory listing\n" << dirlist << endl; // Establish a list of tracks (more accurately, a list of pointers to tracks). vector Tracks; // Make some "events". Generate pseudo tracks using random numbers. for( Int4 j=0; j Eta ) Eta = Eta2; if( Rflt.fire(-0.5,0.5) < 0.0 ) Eta=-Eta; } else { Eta=Rflt.fire(2.0*Etamax)-Etamax; } peta.accumulate((Float4)Eta,W); // Compute the three vector components, put them in a SpaceVector, add // it to the list, get its magnitude and histogram all that. The // transformation from (pt,eta) to (px,py,pz) is unique up to the // decomposition of pt into px and py. pxyz( Pt, Eta, Rflt, &Phi, &Px, &Py, &Pz); SpaceVector * pvec = new SpaceVector(Px, Py, Pz); Tracks.push_back(pvec); // Catch the data for the ntuple arrays and store it away. PtArray[k] = Pt; EtaArray[k] = Eta; PhiArray[k] = Phi; PxArray[k] = Px; PyArray[k] = Py; PzArray[k] = Pz; // For the last two tracks in this set, we assume it is an unstable particle // that decays into a pion and a kaon, work out the kinematics of the // decay and add the decay products to the list. if( k == numTracks-2 ) { double Mres = RBW.fire( ResMass, ResWidth, 3.0*ResWidth ); SpaceVector Pres( Px, Py, Pz ); double Eres = energy( Pres, Mres ); LorentzVector CMres( Pres, Tcomponent( Eres )); SpaceVector beta = Pres/Eres; CMres.boost( beta ); double Epi = ( Mres*Mres + piMass*piMass - KMass*KMass )/(2.0*Mres); double Ppi = sqrt( Epi*Epi - piMass*piMass ); double cosTheta = Rflt.fire( -1.0, 1.0 ); double Pzpi = Ppi*cosTheta; double Ptpi = sqrt( Ppi*Ppi - Pzpi*Pzpi ); double CMPhi = Rflt.fire( -pi, pi ); double Pxpi = Ptpi*cos( CMPhi ); double Pypi = sqrt( Ptpi*Ptpi - Pxpi*Pxpi ); if( Rflt.fire( -1.0, 1.0 ) < 0.0 ) Pypi = -Pypi; LorentzVector CMpi( Pxpi, Pypi, Pzpi, Tcomponent( Epi )); cmppx.accumulate((Float4)Pxpi,W); cmppy.accumulate((Float4)Pypi,W); cmppz.accumulate((Float4)Pzpi,W); double EK = ( Mres*Mres + KMass*KMass - piMass*piMass )/(2.0*Mres); LorentzVector CMK( -Pxpi, -Pypi, -Pzpi, Tcomponent( EK )); CMpi.boost( -beta ); CMK.boost( -beta ); SpaceVector * Labpi = new SpaceVector(CMpi.getV() ); SpaceVector * LabK = new SpaceVector(CMK.getV() ); Tracks.push_back( Labpi ); Tracks.push_back( LabK ); } } // Finished making tracks for this event. Make some histogram and ntuple // entries. Capture a bunch of ntuple entries before they go out of scope. // Note that we explicitly specify the event-by-event length for the // indexed arrays. tupl.capture("TRACKS::Ntracks",numTracks); tupl.capture("TRACKS::Pt" ,&(PtArray[0]), numTracks); tupl.capture("TRACKS::Eta",&(EtaArray[0]), numTracks); tupl.capture("TRACKS::Phi",&(PhiArray[0]), numTracks); tupl.capture("TRACKS::Px", &(PxArray[0]), numTracks); tupl.capture("TRACKS::Py", &(PyArray[0]), numTracks); tupl.capture("TRACKS::Pz", &(PzArray[0]), numTracks); tupl.storeCapturedData(); tupl.clearData(); if (j==0) { cerr << "\nPrint the (possibly) reordered chForm string for Block TRACKS." << endl; cerr << tupl.blockFormat("TRACKS") << endl; } // Run an iterator over the track list and plot things that depend // only on the properties of that one track. typedef vector sp_vector; for( sp_vector::const_iterator iter(Tracks.begin()); iter != Tracks.end(); iter++) { SpaceVector *ptrk = *iter; Pmag=ptrk->mag(); Px=ptrk->x(); Py=ptrk->y(); Pz=ptrk->z(); pphi.accumulate((Float4)Phi,W); pmom.accumulate((Float4)Pmag,W); ppx.accumulate((Float4)Px,W); ppy.accumulate((Float4)Py,W); ppz.accumulate((Float4)Pz,W); } // Now do something that depends on more than one track. Compute the // opening angle between all distinct track pairs and histogram it. // Run two iterators over the same list of tracks and sift out the distinct // track pairs. This is a little messy since the list is really a list of // pointers to tracks. Hence the iterator is a pointer to a pointer and // that's why we have to dereference it twice to get a track. for( sp_vector::const_iterator ptrk1(Tracks.begin()); ptrk1 != Tracks.end(); ptrk1++) { UnitVector p1Hat(**ptrk1); // Establish another iterator and loop over the same list, looking // for a distinct "track 2". // Run the second iterator only until the two pointers are equal. This // should prevent generating pairs that are permutations of each other. // Note that this works only because a vector container is ordered. for( sp_vector::const_iterator ptrk2(Tracks.begin()); ptrk2 != ptrk1; ptrk2++) { Float8 Mass; sumPairs++; UnitVector p2Hat(**ptrk2); Cost = p1Hat.dot(p2Hat); pcos.accumulate((Float4)Cost,W); // Assuming one track is a pion and the other a K, compute the effective mass. LorentzVector v1( **ptrk1, Tcomponent( energy( **ptrk1, piMass ))); LorentzVector v2( **ptrk2, Tcomponent( energy( **ptrk2, KMass ))); Mass = v1.invariantMass(v2); pmas.accumulate((Float4)Mass,W); psct.accumulate((Float4)Mass,(Float4)Cost,W); prof.accumulate((Float4)Mass,(Float4)Cost,W); // Switch the mass assignments, recompute Mass and enter it again v1.set( **ptrk1, Tcomponent( energy( **ptrk1, KMass ))); v2.set( **ptrk2, Tcomponent( energy( **ptrk2, piMass ))); Mass = v1.invariantMass(v2); pmas.accumulate((Float4)Mass,W); psct.accumulate((Float4)Mass,(Float4)Cost,W); prof.accumulate((Float4)Mass,(Float4)Cost,W); } } // Clear out the track list (but don't delete it) and go do it all again. // Recall we instantiated the track vectors on the free store so the // SpaceVector destructor doesn't see them. It is our responsibility to make // them go away. // We don't need to delete the other SpaceVectors. They were instantiated // on the stack and are going out of scope here. In that case, the destructor // takes care of them. for( sp_vector::const_iterator iter( Tracks.begin() ); iter != Tracks.end(); iter++ ) { delete *iter; } Tracks.clear( ); // Part way through the event loop, write the file and put in a pause // so we can go see if the file is complete. if( (j+1)%1000 == 0 ) { cerr << "\ntManager->write() after " << j+1 << " events" << endl; tManager->write(); cerr << "Type a carriage return to continue " << endl; resume = cin.get(); pntk.reset(); } } tupl.release(); // Finished generating tracks. Summarize, clean up and return. cerr << "\nTotal number of tracks generated: " << sumTracks << endl; cerr << "\nTotal number of tracks pairs: " << sumPairs << endl; // Shut down the corresponding RZ file, wrap up and bail out. cerr << "\nHistograms filled. Write the output file." << endl; // Save the state of the RandomEngine someEngine.saveStatus("hepToyz.config"); tManager->cd( string("//")+topdir ); return; } void pxyz(Float8 Pt, Float8 Eta, RandFlat & Rand, Float8 *pphi, Float8 *ppx, Float8 *ppy, Float8 *ppz) { // Given pt and eta, compute px, py and pz. This transformation // is unique up to the decomposition of pt into px and py. // Note that we had to pass a reference to the random number stream // to this function. Otherwise, the function wouldn't know anything // about random numbers. The point is that the generator is NOT global. // It's just another object to be passed around. Float8 Sign, absEta, Theta, absTheta, Pmag, Phi; Float8 Px, Py; Sign = 1.0; if(Eta < 0.0) Sign = -1.0; absEta = Sign*Eta; absTheta = exp(-absEta); Theta = 2.0*Sign*atan(absTheta); Pmag = Pt/sin(Theta); *ppz = Sign*sqrt(Pmag*Pmag-Pt*Pt); Phi = Rand.fire(-pi,pi); *pphi = Phi; Px = cos(Phi)*Pt; *ppx = Px; Py = sqrt(Pt*Pt-Px*Px); if( Rand.fire(-0.5,0.5) > 0.0 ) Py = -Py; *ppy = Py; return; } bool newFile( string topdir ) { // Sort out if the file exists already. Try to open it // and see if the attempt fails. std::ifstream test(topdir.c_str(),std::ios::in); if( test ) { test.close(); return false; } else { return true; } }