EIC Software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
EicRunSim.cxx
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file EicRunSim.cxx
1 //
2 // AYK (ayk@bnl.gov), 2014/08/22
3 //
4 // A trivial (for now) extension of FairRunSim class;
5 //
6 
7 #include <TRandom.h>
8 #include <TH2D.h>
9 #include <TStopwatch.h>
10 #include <TKey.h>
11 
12 #include <FairRuntimeDb.h>
13 #include <FairTrajFilter.h>
14 #include <FairParRootFileIo.h>
15 #include <FairPrimaryGenerator.h>
16 
17 #include <PndCave.h>
18 
19 #include <EicRunSim.h>
20 #include <EicMCApplication.h>
21 
23 
24 #define _OUTPUT_FILE_DEFAULT_ "simulation.root"
25 #define _MEDIA_FILE_DEFAULT_ "media.geo"
26 #define _CAVE_FILE_NAME_DEFAULT_ "cave.geo"
27 
28 // ---------------------------------------------------------------------------------------
29 
30 EicRunSim::EicRunSim(const char *engine): mInitState(stUndefined),
31  mSuppressSecondariesFlag(false), mFluxMonitorGrid(0),
32  mOutputFileName(_OUTPUT_FILE_DEFAULT_),
33  mMediaFileName(_MEDIA_FILE_DEFAULT_),
34  mSeed(_SEED_DEFAULT_),
35  mCaveFileName(_CAVE_FILE_NAME_DEFAULT_),
36  mCaveDefinedFlag(false),
37  mTimerFlag(true),
38  mField(0),
39  mIgnoreBlackHoleVolumes(false), mSuppressHitProductionFlag(false),
40  mSuppressFairRootSteppingCallFlag(false)
41 
42 {
43  if (mInstance) {
44  Fatal("EicRunSim::EicRunSim()", "Singleton instance already exists.");
45  return;
46  } //if
47 
48  if (engine) SetName(engine);
49 
50  mInstance = this;
51 } // EicRunSim::EicRunSim()
52 
53 // ---------------------------------------------------------------------------------------
54 
55 void EicRunSim::SetMaterials(const char *mediaFileName)
56 {
57  mMediaFileName = mediaFileName;
58 } // EicRunSim::SetMaterials()
59 
60 // ---------------------------------------------------------------------------------------
61 
62 void EicRunSim::SetOutputFile(const char *outputFileName)
63 {
64  mOutputFileName = outputFileName;
65 } // EicRunSim::SetOutputFile()
66 
67 // ---------------------------------------------------------------------------------------
68 
69 void EicRunSim::SetCaveFileName(const char *caveFileName)
70 {
71  mCaveFileName = caveFileName;
72 } // EicRunSim::SetCaveFileName()
73 
74 // ---------------------------------------------------------------------------------------
75 
77 {
78  // Unless cave was defined already, do it now;
79  if (!mCaveDefinedFlag) {
80  FairModule *Cave= new PndCave("CAVE");
82  FairRunSim::AddModule(Cave);
83 
84  mCaveDefinedFlag = true;
85  } //if
86 
87  FairRunSim::AddModule(module);
88 } // EicRunSim::AddModule()
89 
90 // ---------------------------------------------------------------------------------------
91 
93 {
94  if (!GetPrimaryGenerator()) {
96  SetGenerator(primGen);
97  } //if
98 
99  GetPrimaryGenerator()->AddGenerator(generator);
100 } // EicRunSim::AddGenerator()
101 
102 // ---------------------------------------------------------------------------------------
103 
104 void EicRunSim::AddField(FairField *field)
105 {
106  if (!GetField()) {
107  mField = new PndMultiField();
108  SetField(mField);
109  } //if
110 
111  mField->AddField(field);
112 } // EicRunSim::AddField()
113 
114 // ---------------------------------------------------------------------------------------
115 
117 {
118  // If failed once, fail forever;
119  if (mInitState == stFailed) return;
120 
121  // This basically means default EicRunSim::EicRunSim() constructor used and
122  // no SetName() call happened afterwards;
123  if (fName.IsNull()) {
124  printf("\n\n EicRunSim::Init(): no G3/G4 engine specified.\n\n");
126  return;
127  } //if
128 
129  gRandom->SetSeed(mSeed);
130 
131  // Deal the same way as with SetMaterials() and SetOutputFile() once ever need
132  // to set this flag to kFALSE;
133  SetStoreTraj(kTRUE);
134 
137 
138  // Basically want to expand original virtual FairMCApplication::Stepping() call;
139  fApp = new EicMCApplication("Fair","The Fair VMC App",ListOfModules, MatFname);
140  FairRunSim::Init();
141 
142  // FairRunSim::Init() has no return value -> assume success;
144 } // EicRunSim::Init()
145 
146 // ---------------------------------------------------------------------------------------
147 
148 void EicRunSim::Run(Int_t NEvents, Int_t NotUsed)
149 {
150  // This is indeed a clear hack;
151  //if (mJanaPluginMode) return;
152 
153  RunCoreStart(NEvents, NotUsed);
154 
155  // Yes, just exit; change the default behaviour if this ever becomes a problem;
156  exit(0);
157 } // EicRunSim::Run()
158 
159 void EicRunSim::RunCoreStart(Int_t NEvents, Int_t NotUsed)
160 {
161  // Attempt to call Init(0 if it has not happened so far);
162  if (mInitState == stUndefined) Init();
163 
164  // If initialization failed, do not do anything;
165  if (mInitState == stFailed) return;
166 
167  // Get pointer to the runtime database;
168  FairRuntimeDb *rtdb = GetRuntimeDb();
169  FairParRootFileIo* output = new FairParRootFileIo(kTRUE);
170  char simparamsFile[1024];
171  // Well, assume /tmp is always available?; switch to the working
172  // directory if this ever becomes an issue;
173  snprintf(simparamsFile, 1024-1, "/tmp/simparams-%05d.root", getpid());
174  output->open(simparamsFile);
175  rtdb->setOutput(output);
176 
177  // Help EicEventGenerator::ReadEvent() to open its current TTree in simulation.root
178  // rather than in temporary simparams.root file;
179  GetOutputFile()->cd();
180 
182  // Deal the same way as with SetMaterials() and SetOutputFile() once ever need
183  // to set this flag to kFALSE;
184  trajFilter->SetStorePrimaries(kTRUE);
185  trajFilter->SetStoreSecondaries(mSuppressSecondariesFlag ? kFALSE : kTRUE);
186 
187  // Yes, I'm mostly interested to see what is the actual per-event simulation time,
188  // so place Stopwatch initialization here;
189  {
190  TStopwatch timer;
191  if (mTimerFlag) timer.Start();
192 
193  // Call the core FairRoot routine (actual simulation run);
194  FairRunSim::Run(NEvents, NotUsed);
195 
196  if (mTimerFlag) {
197  // Print some info;
198  timer.Stop();
199  printf("\n Wall Time = %7.1f s, CPU Time = %7.1f s\n\n", timer.RealTime(), timer.CpuTime());
200  } //if
201  }
202 
203  // Save the parameters;
204  rtdb->closeOutput();
205 
206  // Fine, at this point both parameter database file and the output file
207  // are closed; re-open them again and copy over parameter file contents;
208  // yes, this is a hack, but is a simpliest way to get rid of simparams.root file;
209  {
210  TFile *fpar = new TFile(simparamsFile);
211  TFile *fsim = new TFile(mOutputFileName, "UPDATE");
212 
213  TIter next(fpar->GetListOfKeys());
214  TKey *key;
215  while ((key = (TKey*)next())) {
216  //printf("%s %s\n", key->GetName(), key->GetClassName());
217 
218  if (!strcmp(key->GetClassName(), "TProcessID")) continue;
219 
220  TObject *obj = fpar->Get(key->GetName());
221  assert(obj);
222 
223  fsim->cd();
224  obj->Write();
225  } //while
226 
227  fpar->Close();
228  fsim->Close();
229  }
230 
231  unlink(simparamsFile);
232 
233  // Yes, and get rid of this file as well; change the default behaviour if this
234  // ever becomes an issue;
235  if (!access(_GPHYSI_DAT_, W_OK)) unlink(_GPHYSI_DAT_);
236 
237  // Yes, just exit; change the default behaviour if this ever becomes a problem;
238  //++exit(0);
239 } // EicRunSim::RunCoreStart()
240 
241 // ---------------------------------------------------------------------------------------
242 
243 int EicRunSim::DefineFluxMonitorGrid(double rMax, unsigned rDim, double zMin, double zMax,
244  unsigned zDim)
245 {
246  if (mFluxMonitorGrid) {
247  Fatal("EicRunSim", "EicRunSim::DefineFluxMonitorGrid() called more than once.");
248  return -1;
249  } //if
250 
251  mFluxMonitorGrid = new FluxMonitorGrid(rMax, rDim, zMin, zMax, zDim);
252 
253  // Arrange a separate FairTask with the only purpose to call Write() at the end of
254  // simulation run; FairRunSim::Instance() pointer should indeed be always available, or?;
255  /*FairRunSim::Instance()->*/AddTask(new EicFluxMonitorTask(/*outputTree*/));
256 
257  return 0;
258 } // EicRunSim::DefineFluxMonitorGrid()
259 
260 // ---------------------------------------------------------------------------------------
261 
262 int EicRunSim::AddFluxMonitorParticleType(int pdg, double eMin, double eMax)
263 {
264  if (!mFluxMonitorGrid) {
265  Fatal("EicRunSim", "EicRunSim::AddFluxMonitorParticleType() called prior to "
266  "EicRunSim::DefineFluxMonitorGrid().");
267  return -1;
268  } //if
269 
270  return mFluxMonitorGrid->AddFluxMonitorParticleType(pdg, eMin, eMax);
271 } // EicRunSim::AddFluxMonitorParticleType()
272 
273 // ---------------------------------------------------------------------------------------
274 
275 FluxMonitorGrid::FluxMonitorGrid(double rMax, unsigned rDim, double zMin, double zMax, unsigned zDim):
276  mRmax(rMax), mRdim(rDim), mZmin(zMin), mZmax(zMax), mZdim(zDim), mRadiationMapType(false),
277  mNormalizedCrossSection(0.0), mTrials(0), mTotalOriginalStat(0)
278 {
279  // FIXME: sanity check, please;
280  mRbwidth = rMax /rDim;
281  mZbwidth = (zMax - zMin)/zDim;
282 
283  //mDensity = new double [mRdim*mZdim];
284  //for(unsigned iz=0; iz<mZdim; iz++)
285  //for(unsigned ir=0; ir<mRdim; ir++)
286  // mDensity[iz*mRdim+ir] = 0.0;
287 } // FluxMonitorGrid::FluxMonitorGrid()
288 
289 // ---------------------------------------------------------------------------------------
290 
291 int FluxMonitorGrid::AddFluxMonitorParticleType(int pdg, double eMin, double eMax)
292 {
293  if (eMin < 0.0 || eMin > eMax) {
294  Fatal("EicRunSim", "FluxMonitorGrid::AddFluxMonitorParticleType(): eMin >= eMax.");
295  return -1;
296  } //if
297 
298  mParticles.push_back(FluxMonitorParticleType(pdg, eMin, eMax));
299 
300  // Initialize flux array;
301  FluxMonitorParticleType *ptype = &mParticles[mParticles.size()-1];
302  ptype->mLength = new double [mRdim*mZdim];
303  ptype->mDensityLength = new double [mRdim*mZdim];
304  ptype->mEdep = new double [mRdim*mZdim];
305  for(unsigned iz=0; iz<mZdim; iz++)
306  for(unsigned ir=0; ir<mRdim; ir++)
307  ptype->mLength[iz*mRdim+ir] = ptype->mEdep[iz*mRdim+ir] =
308  ptype->mDensityLength[iz*mRdim+ir] = 0.0;
309 
310  return 0;
311 } // FluxMonitorGrid::AddFluxMonitorParticleType()
312 
313 // ---------------------------------------------------------------------------------------
314 
315 #include <TVirtualMC.h>
316 
317 void FluxMonitorGrid::AddEntry(int pdg, double eKin, double dE, TVector3 in, TVector3 out)
318 {
319  // No particle types required -> exit;
320  if (!mParticles.size()) return;
321 
322  // First figure out if this particle is of interest at all;
323  unsigned needed[mParticles.size()], nCounter = 0;
324  memset(needed, 0x00, sizeof(needed));
325  for(unsigned pt=0; pt<mParticles.size(); pt++) {
326  FluxMonitorParticleType *ptype = &mParticles[pt];
327 
328  // NB: energy range check here will work for mEmin=mEmin=0.0 case as well;
329  if ((!ptype->mPDG || pdg == ptype->mPDG) && eKin >= ptype->mEmin &&
330  (!ptype->mEmax || eKin <= ptype->mEmax)) {
331  needed[pt] = 1;
332  nCounter++;
333  } //if
334  } //for pt
335 
336  // Well, nCounter=0 means either wrong PDG or energy range out of interest;
337  if (!nCounter) return;
338 
339  // Then figure out which space cells (cylinders in case of axially-symmetric
340  // {RZ}-grid considered for now) were crossed by this track segment; assume it
341  // was a straight line; start with Z-slices; reorder in Z for simplicity if needed;
342  TVector3 *v1, *v2;
343  if (in.z() <= out.z()) {
344  v1 = &in; v2 = &out;
345  }
346  else {
347  v1 = &out; v2 = &in;
348  } //if
349  // Figure out Z-cell ID range; i1 and i2 are ordered here, for sure;
350  int iz1 = (int)floor((v1->z() - mZmin)/mZbwidth), iz2 = (int)floor((v2->z() - mZmin)/mZbwidth);
351  // Check out-of-range condition;
352  if (iz1 > mZdim || iz2 < 0) return;
353  double dz = v2->z() - v1->z();
354  // Calculate vector along [v1,v2] straight line segment and its length;
355  TVector3 dv = *v2 - *v1; double dvlen = dv.Mag(); //assert(dvlen);
356 
357  //printf("\nZ (%3d %3d): %9.3f -> %9.3f; R: %9.3f -> %9.3f (L = %9.3f)\n",
358  // iz1, iz2, v1->z(), v2->z(),
359  // sqrt(v1->x()*v1->x()+v1->y()*v1->y()), sqrt(v2->x()*v2->x()+v2->y()*v2->y()),
360  // dv.Mag());
361 
362  // Loop through all indices between iz1 and iz2; it looks like there will be
363  // at most as many pieces as iz2-iz1+1, right?;
364  for(int iqz=iz1; iqz<=iz2; iqz++) {
365  if (iqz < 0) continue;
366  if (iqz >= mZdim) break;
367 
368  // Z-coordinate of the left and right segment piece points;
369  double zl = iqz == iz1 ? v1->z() : mZmin + (iqz+0)*mZbwidth; if (zl < mZmin) zl = mZmin;
370  double zr = iqz == iz2 ? v2->z() : mZmin + (iqz+1)*mZbwidth; if (zr > mZmax) zr = mZmax;
371  //printf(" %3d -> %9.3f .. %9.3f\n", iqz, zl, zr);
372 
373  // Calculate respective 3D points;
374  TVector3 vl = *v1 + ((zl - v1->z())/dz) * dv; //vl.Print();
375  TVector3 vr = *v1 + ((zr - v1->z())/dz) * dv; //vr.Print();
376  TVector3 dvlr = vr - vl;
377 
378  // Fine, this segment piece crossed iqz-th Z-slice along the straight line
379  // segment between 3D points 'vl' and 'vr'; figure out which radial bins
380  // were crossed; work in 2D now;
381  {
382  std::vector<std::pair<double, double> > parts;
383  // Find the closest approach point to (0,0) on straight line {vl,vr} in XY-projection;
384  TVector2 wl(vl.x(), vl.y()), wr(vr.x(), vr.y());
385  TVector2 dw = wr - wl;
386  double t = -(wl * dw)/dw.Mod2(), rl = wl.Mod(), rr = wr.Mod();
387  //printf(" t(PCA) = %9.3f; (%9.3f %9.3f %9.3f)\n", t, rl, (wl + t*dw).Mod(), rr);
388 
389  // Now, if 0<=t<=1, should consider 2 parts separately; otherwise
390  // radius changes monotonously from 'v1' to 'v2' -> one part;
391  if (t <= 0.0 || t >= 1.0) {
392  // Want to order vertices in R in order to simplify logic later;
393  if (rl < rr)
394  parts.push_back(std::pair<double, double>(0.0, 1.0));
395  else
396  parts.push_back(std::pair<double, double>(1.0, 0.0));
397  //printf(" ---> so 1 part!\n");
398  }
399  else {
400  // Here ordering is trivial of course ('t' comes first - PCA);
401  parts.push_back(std::pair<double, double>(t, 0.0));
402  parts.push_back(std::pair<double, double>(t, 1.0));
403  //printf(" ---> so 2 parts!\n");
404  } //if
405 
406  // Loop through either 1 or 2 parts, does not matter;
407  for(unsigned i=0; i<parts.size(); i++) {
408  std::pair<double, double> *part = &parts[i];
409 
410  // 2D ends of this part and their radii; they are ordered in R;
411  TVector2 _u1 = wl + part->first*dw, _u2 = wl + part->second*dw;
412  double r1 = _u1.Mod(), r2 = _u2.Mod();
413 
414  // Inner radius too big -> skip;
415  if (r1 > mRmax) continue;
416 
417  int ir1 = (int)floor(r1/mRbwidth), ir2 = (int)floor(r2/mRbwidth);
418  //printf(" %3d %3d\n", ir1, ir2);
419  // Loop through all indices between ir1 and ir2; it looks like there will be
420  // at most as many pieces as ir2-ir1+1, right?;
421  for(int iqr=ir1; iqr<=ir2; iqr++) {
422  if (iqr >= mRdim) break;
423 
424  // Either take one of the ends or calculate crossing point with the
425  // circle of respective radius; NB: use W-vectors (not U-ones) since these
426  // are those who are related to the t-parameterization;
427  double t1, t2;
428  double A = dw.Mod2();
429  double B = 2 * wl * dw;
430  if (iqr == ir1)
431  t1 = part->first;
432  else {
433  double R = (iqr+0)*mRbwidth;
434  double C = wl.Mod2() - R*R;
435 
436  // FIXME: unify quadratic equation solution later;
437  double D = sqrt(B*B - 4*A*C);
438  double roots[2] = {(-B-D)/(2*A), (-B+D)/(2*A)};
439  // Need root in the [first .. second] range;
440  int id = -1;
441  for(unsigned irt=0; irt<2; irt++) {
442  //printf(" R = %7.3f; r#%d -> %7.3f\n", R, irt, roots[irt]);
443  if ((roots[irt] >= part->first && roots[irt] <= part->second) ||
444  (roots[irt] >= part->second && roots[irt] <= part->first)) {
445  assert(id == -1);
446  id = irt;
447  } //if
448  } //for irt
449  assert(id != -1);
450 
451  t1 = roots[id];
452  } //if
453  if (iqr == ir2)
454  t2 = part->second;
455  else {
456  double R = (iqr+1)*mRbwidth;
457  double C = wl.Mod2() - R*R;
458 
459  // FIXME: unify quadratic equation solution later;
460  double D = sqrt(B*B - 4*A*C);
461  double roots[2] = {(-B-D)/(2*A), (-B+D)/(2*A)};
462  // Need root in the [first .. second] range;
463  int id = -1;
464  for(unsigned irt=0; irt<2; irt++)
465  if ((roots[irt] >= part->first && roots[irt] <= part->second) ||
466  (roots[irt] >= part->second && roots[irt] <= part->first)) {
467  assert(id == -1);
468  id = irt;
469  } //for irt..if
470  assert(id != -1);
471 
472  t2 = roots[id];
473  } //if
474  //printf("t1,2: %f %f\n", t1, t2);
475 
476  // Fine, so now t-parameters of both ends are known; return back to 3D
477  // and calculate respective points;
478  TVector3 s1 = vl + t1 * dvlr; //s1.Print();
479  TVector3 s2 = vl + t2 * dvlr; //s2.Print();
480  double crlen = (s2 - s1).Mag();
481 
482  // Fine, so iqz & iqr are known, as well as the crossing length;
483  // loop through all the booked particle types and fill out respective arrays;
484  for(unsigned pt=0; pt<mParticles.size(); pt++) {
485  FluxMonitorParticleType *ptype = &mParticles[pt];
486 
487  if (!needed[pt]) continue;
488 
489  //if (len > 20.) {
490  //printf("%d, @@@ %f\n", pdg, len);
491  //in.Print(); out.Print();
492  //printf("IZ: %3d %3d; IR: %3d %3d\n", iz1, iz2, ir1, ir2);
493 
494  //exit(0);
495  //} //if
496 
497  //printf("%f\n", dE);
498  ptype->mLength[iqz*mRdim+iqr] += crlen;
499 
500  if (mRadiationMapType) {
501  Float_t fA, fZmat, fDensity, fRadl, fAbsl;
502 
503  gMC->CurrentMaterial(fA, fZmat, fDensity, fRadl, fAbsl);
504  //printf("%f\n", fDensity);
505 
506  ptype->mEdep[iqz*mRdim+iqr] += dvlen ? dE*(crlen/dvlen) : dE;
507  ptype->mDensityLength[iqz*mRdim+iqr] += fDensity*crlen;
508  } //if
509  } //for pt
510  } //for iqr
511  } //for i
512  }
513  } //for iqz
514 } // FluxMonitorGrid::AddEntry()
515 
516 // ---------------------------------------------------------------------------------------
517 
518 // Well, automatic determination of cross-section from .root files requires
519 // eic-smear support compiled in; in fact any support of ROOT event format requires it;
520 #ifdef _EICSMEAR_
521 #include <EicEventGenerator.h>
522 #endif
523 
525 {
526  // Try to get luminosity info from the .root file with MC events if not provided
527  // via command line call FluxMonitorGrid::SetNormalization(); NB: it is pretty easy
528  // to screw up with normalization in these calculations, so consider to use built-in
529  // capability provided below; this ONLY WORKS if 0) EicEventGenerator() was used
530  // in simulation.C script rather than any other FairGenerator-derived class,
531  // 1) .root file input with MC events is used rather than ASCII file (since
532  // ASCII generator files do not contain normalization info), 2) it is a single ROOT
533  // file (so no chains), 3) file was PROPERLY converted by eic-smear BuildTree call
534  // (so the 4-th parameter with matching logfile name was given); this functionality
535  // is technically available for PYTHIA (as well as perhaps Pepsi, Djangoh and Milou);
536 #ifdef _EICSMEAR_
538  // Check that EicEventGenerator is used in simulation.C;
540 
541  if (generator) {
542  const TChain *chain = generator->GetInputTree();
543 
544  // Check that ROOT input is used rather than ASCII, and only one file;
545  if (chain && chain->GetListOfFiles()->GetEntriesFast() == 1) {
546  TObjString *crossSectionString(NULL), *trialsString(NULL);
547 
548  TFile *file = chain->GetFile();
549  file->GetObject("crossSection", crossSectionString); assert(crossSectionString);
550  file->GetObject("nEvents", trialsString); assert(trialsString);
551 
552  //TTree* tree(NULL);
553  //file->GetObject("EICTree", tree);
554  //mTotalOriginalStat = tree->GetEntriesFast();
555 
556  mNormalizedCrossSection = atof(crossSectionString->GetString().Data());
557  mTrials = mTotalOriginalStat = atoi(trialsString->GetString().Data());
558  printf("%7.2f %d %d\n", mNormalizedCrossSection, mTrials, mTotalOriginalStat);
559  } //if
560  } //if
561  } //if
562 #endif
563 
564  // At this point normalization should be available, either this or that way;
565  // FIXME: do this check better later; the below 2D histograms will be normalized to
566  // 1 fb^-1 integrated luminosity; FIXME: well, 1 inverse femtobarn is hardcoded here;
568  double lumi_exp = 1.0/1E-15, lumi_mc =
569  (1.0*evStat/mTotalOriginalStat)*(mTrials/(mNormalizedCrossSection*1E-6));
570  double norm = (lumi_exp/lumi_mc);
571 
572  double cVolumes[mRdim];
573 
574  // Calculate single cylindrical volume volumes;
575  for(unsigned ir=0; ir<mRdim; ir++) {
576  double r1 = ir*mRbwidth, r2 = (ir+1)*mRbwidth;
577 
578  cVolumes[ir] = TMath::Pi()*(r2*r2-r1*r1)*mZbwidth;
579  } //for ir
580 
581  FairRun *fRun = FairRun::Instance();
582  // I guess there is no need to save/restore current directory here?;
583  fRun->GetOutputFile()->cd();
584 
585  // Conversion factor for radiation dose calculation;
586  double gev2joule = 1.6E-10;
587 
588  // Loop through all the booked particle groups, declare 2D histograms, etc;
589  for(unsigned pt=0; pt<mParticles.size(); pt++) {
590  FluxMonitorParticleType *ptype = &mParticles[pt];
591 
592  double density[mZdim][mRdim];
593  for(unsigned iz=0; iz<mZdim; iz++)
594  for(unsigned ir=0; ir<mRdim; ir++)
595  density[iz][ir] =
596  ptype->mLength[iz*mRdim+ir] ? ptype->mDensityLength[iz*mRdim+ir]/ptype->mLength[iz*mRdim+ir] : 0.0;
597 
598  char htitle[1024] = "", hname[1024], pname[1024] = "neutron", lumi[1024];
599  snprintf(hname, 1024-1, "flux%02d", pt);
600  // FIXME: pdg hardcoded ...;
601  if (ptype->mPDG != 2112) snprintf(pname, 1024-1, "PDG#%d", ptype->mPDG);
602  snprintf(lumi, 1024-1, "%5.1f fb^{-1} integrated luminosity", lumi_exp*1E-15);
603 
604  if (mRadiationMapType)
605  snprintf(htitle, 1024-1, "Radiation dose in [J/cm^{3}] for %s", lumi);
606  else
607  snprintf(htitle, 1024-1, "%s flux above %5.1f keV in [n/cm^{2}] for %s", pname,
608  ptype->mEmin*1E6, lumi);
609  TH2D *hh = new TH2D(hname, htitle, mZdim, mZmin, mZmax, mRdim, 0.0, mRmax);
610  hh->GetXaxis()->SetTitle("Z coordinate (along the beam line), [cm]");
611  hh->GetYaxis()->SetTitle("Radial coordinate, [cm]");
612 
613  for(unsigned iz=0; iz<mZdim; iz++)
614  for(unsigned ir=0; ir<mRdim; ir++) {
615  // Yes, will be different estimates for different particle types;
616  // just because statistically these particles sample different
617  // amount of material (otherwise would get huge spikes at the
618  // border of heavy materials and air if a given cylindrical cell
619  // is shared between these media);
620  double cMass = cVolumes[ir] * density[iz][ir];
621 
622  //double flux = ptype->mFlux[iz*mRdim+ir], quantity = //density[iz][ir];//mDensity[iz*mRdim+ir];
623  //mRadiationMapType ? (cMass ? flux/cMass : 0.0) :
624  //(flux/cVolumes[ir])/norm;
625 
626  //hh->SetBinContent(iz+1, ir+1, cMass ? ptype->mEdep[iz*mRdim+ir]/cMass : 0.0);
627  //+hh->SetBinContent(iz+1, ir+1, ptype->mEdep[iz*mRdim+ir]/cVolumes[ir]);
628  hh->SetBinContent(iz+1, ir+1, norm*(mRadiationMapType ? gev2joule*ptype->mEdep[iz*mRdim+ir] :
629  ptype->mLength[iz*mRdim+ir])/cVolumes[ir]);
630  } //for iz..ir
631 
632  hh->Write();
633  } //for pt
634 } // FluxMonitorGrid::FillOutHistograms()
635 
636 // ---------------------------------------------------------------------------------------
637 
639 {
640  printf("FluxMonitorTask called!\n");
641 
642  EicRunSim *fRun = EicRunSim::Instance();
643  // In fact EicRunSim is the only user of this task; yet check its presence;
644  if (fRun && fRun->GetFluxMonitorGrid()) fRun->GetFluxMonitorGrid()->FillOutHistograms(mStat);
645 
647 } // EicFluxMonitorTask::FinishTask()
648 
649 // ---------------------------------------------------------------------------------------
650