Deprecated/UpgradeASCIITests/SdPMTSimulatorASCII/SdPMTSimulator.cc
Go to the documentation of this file.
1 #include <evt/Event.h>
2 #include <sevt/SEvent.h>
3 #include <sevt/PMT.h>
4 #include <sevt/PMTSimData.h>
5 #include <utl/ErrorLogger.h>
6 #include <utl/RandomEngine.h>
7 #include <fwk/CentralConfig.h>
8 #include <fwk/RandomEngineRegistry.h>
9 #include <utl/Reader.h>
10 #include <utl/config.h>
11 
12 // Root stuff for diagnostic histograms
13 #include <TH1.h>
14 #include <TFile.h>
15 
16 #include "SdPMTSimulator.h"
17 
18 using namespace SdPMTSimulatorASCII;
19 using namespace std;
20 using namespace utl;
21 using namespace fwk;
22 using namespace sevt;
23 
24 using CLHEP::RandGeneral;
25 
26 
28  fChargeDist(0),
29  fLimitStationsPerCycle(false),
30  fNStationsPerCycle(numeric_limits<unsigned int>::max()),
31  fAveragePulseShape(0),
32  fWantRootHistos(false),
33  fChargeHisto(0),
34  fAnodePulseHisto(0),
35  fSaturationFunction(0),
36  fRandomEngine(0)
37 {
38 }
39 
41 {
42  delete fChargeDist;
43  delete fAveragePulseShape;
44  delete fChargeHisto;
45  delete fAnodePulseHisto;
46  delete fSaturationFunction;
47 }
48 
49 
52 {
53  Branch topB = CentralConfig::GetInstance()->GetTopBranch("SdPMTSimulatorASCII");
54  vector<int> components;
55 
56  // WCD
57  if (topB.GetChild("fundamentalComponents"))
58  topB.GetChild("fundamentalComponents").GetData(components);
59  for (unsigned int i = 0; i != components.size(); ++i)
61 
62  // ASCII
63  components.clear();
64  if (topB.GetChild("fundamentalComponents_ASCII"))
65  topB.GetChild("fundamentalComponents_ASCII").GetData(components);
66  for (unsigned int i = 0; i != components.size(); ++i)
68 
69  Branch elecB = topB.GetChild("electronicsParameters");
70 
71  // Set up the average anode pulse shape from data in file
72  Branch anodePulseBranch = elecB.GetChild("anodePulseShape");
73 
74 //#warning XML for this two quantities should be replaced by the tabulated function
75  vector<double> anodeTimeBins;
76  anodePulseBranch.GetChild("timeBins").GetData(anodeTimeBins);
77  vector<double> anodePulseHeight;
78  anodePulseBranch.GetChild("pulseSize").GetData(anodePulseHeight);
79 
80  if (anodeTimeBins.empty() || anodeTimeBins.size() != anodePulseHeight.size()) {
81  ERROR("Malformed <anodePulseShape> configuration.");
82  return eFailure;
83  }
84 
85  {
86  const unsigned int naph = anodePulseHeight.size();
87 
88  // invert the raw pulse shape from scope trace and ignore below baseline
89  for (unsigned int i = 0; i < naph; ++i)
90  anodePulseHeight[i] = (anodePulseHeight[i] >= 0) ? 0 : -anodePulseHeight[i];
91 
92  double anodeIntegral = 0;
93  for (unsigned int i1 = 0, i2 = 1; i2 < naph; ++i1, ++i2) {
94  const double dt = anodeTimeBins[i2] - anodeTimeBins[i1];
95  anodeIntegral += 0.5*(anodePulseHeight[i1] + anodePulseHeight[i2])*dt;
96  }
97 
98  // normalize the pulse shape
99  for (unsigned int i1 = 0, i2 = 1; i2 < naph; ++i1, ++i2) {
100  const double dt = 1/*anodeTimeBins[i2] - anodeTimeBins[i1]*/;
101  anodePulseHeight[i1] *= dt / anodeIntegral;
102  }
103 
104  // shift the times so that they start at zero
105  const double offset = anodeTimeBins[0];
106  for (unsigned int i = 0; i < naph; ++i)
107  anodeTimeBins[i] -= offset;
108  }
109 
110  fAveragePulseShape = new TabulatedFunction(anodeTimeBins, anodePulseHeight);
111 
112  // Find the maximum pulse height and corresponding bin. This is used later
113  // to center the pulse maximum on the PE release time bin
114  double currentMax = 0;
116  fIt != fAveragePulseShape->End(); ++fIt)
117  if (fIt->Y() >= currentMax) {
118  currentMax = fIt->Y();
119  fMaxPulseHeight = fIt;
120  }
121 
122  // Set up the SPE charge distribution
123  Branch chargeB = elecB.GetChild("chargeDistributionSPE");
124 
125  chargeB.GetChild("minCharge").GetData(fMinCharge);
126  chargeB.GetChild("maxCharge").GetData(fMaxCharge);
127 
128  vector<double> chargeProbability;
129  chargeB.GetChild("relativeProbability").GetData(chargeProbability);
130 
131  const unsigned int chargeProbabilityN = chargeProbability.size();
132 
133  fRandomEngine = &RandomEngineRegistry::GetInstance().Get(RandomEngineRegistry::eDetector);
134  fChargeDist = new RandGeneral(fRandomEngine->GetEngine(), &chargeProbability[0], chargeProbabilityN);
135 
136  elecB.GetChild("PMTGainWCD").GetData(fPMTGain_WCD);
137  elecB.GetChild("PMTGainASCII").GetData(fPMTGain_ASCII);
138 
139  topB.GetChild("RootHistogramControl").GetData(fWantRootHistos);
140 
141  // Fill diagnostic histograms, if requested
142  if (fWantRootHistos) {
143  topB.GetChild("RootHistogramFilename").GetData(fRootHistoFilename);
144  INFO("Will write diagnostic histograms for this module");
145 
146  // check SPE distribution
147  fChargeHisto = new TH1D("chargeDist", "charge distribution", 20,0,4);
148  for (int itest = 0; itest < 100000; ++itest) {
149  const double x = fChargeDist->fire();
150  const double val = fMinCharge + (fMaxCharge - fMinCharge)*x;
151  fChargeHisto->Fill(val);
152  }
153 
154  // check average pulse shape
155  const int nPoints = fAveragePulseShape->GetNPoints();
156  fAnodePulseHisto = new TH1D("anodePulseHisto", "anode pulse histo",
157  nPoints,
158  (*fAveragePulseShape)[0].X()/nanosecond,
159  (*fAveragePulseShape)[nPoints-1].X()/nanosecond);
160 
162  it != fAveragePulseShape->End(); ++it)
163  fAnodePulseHisto->Fill(it->X()/nanosecond, it->Y());
164  }
165 
166  // Read the voltage values for PMT saturation
167  AttributeMap useAtt;
168  useAtt["use"] = string("yes");
169 
170  Branch satB = elecB.GetChild("saturationCurve", useAtt);
171  if (satB) {
172  vector<double> vIn;
173  satB.GetChild("inputVoltage").GetData(vIn);
174  vector<double> vOut;
175  satB.GetChild("outputVoltage").GetData(vOut);
176  satB.GetChild("Current2VoltageMultiplier").GetData(fCurrent2VoltageMultiplier);
177  if (vIn.size() != vOut.size()) {
178  ERROR("Lists of input/output voltages for saturation curve must have same size");
179  return eFailure;
180  }
181  ostringstream info;
182  info << "saturation curve [V]: ";
183  for (unsigned int i = 0; i < vIn.size(); ++i)
184  info << vIn[i]/volt << " -> " << vOut[i]/volt << " ";
185  INFO(info);
186  // convert voltages to "current" (i.e. smeared&amplified pe per ns bin):
187  const double v2c = 1/fCurrent2VoltageMultiplier;
188  for (unsigned int i = 0; i < vIn.size(); ++i) {
189  vIn[i] *= v2c;
190  vOut[i] *= v2c;
191  }
192  // input/output of this function is "current" (i.e. pe per ns bin)
193  fSaturationFunction = new TabulatedFunction(vIn, vOut);
194  } else
195  INFO("No PMT saturation simulation");
196 
197  Branch limitStations = topB.GetChild("LimitStationsPerCycle", useAtt);
198  if (limitStations) {
199  limitStations.GetData(fNStationsPerCycle);
200  fLimitStationsPerCycle = true;
201  }
202 
203  fFirstCycle = true;
204  return eSuccess;
205 }
206 
207 
208 void
210 {
211  const double dt = pe.GetBinning();
212  const double pulseStopTime = (*fAveragePulseShape)[fAveragePulseShape->GetNPoints()-1].X();
213  double avgPulseShapeNorm = 0;
214  vector<double> avgPulseShape;
215  avgPulseShape.reserve(int(pulseStopTime / dt) + 1);
216  for (double time = 0; time < pulseStopTime; time += dt) {
217  const double pulse = fAveragePulseShape->Y(time);
218  avgPulseShapeNorm += pulse;
219  avgPulseShape.push_back(pulse);
220  }
221 
222  const double pulseMaxTime = fMaxPulseHeight->X();
223  const double scaleFactorMin = PMTGain * fMinCharge / avgPulseShapeNorm;
224  const double scaleFactorMax = PMTGain * fMaxCharge / avgPulseShapeNorm;
225 
226  for (TimeDistributionI::SparseIterator peIt = pe.SparseBegin(), end = pe.SparseEnd();
227  peIt != end; ++peIt) {
228 
229 //#warning implement central-limit normal distribution for large npe
230  double charge = 0;
231  for (int i = 0, n = peIt->get<1>(); i < n; ++i)
232  charge += fChargeDist->fire();
233 
234  const double realCharge = scaleFactorMin + scaleFactorMax*charge;
235 
236  const double offset = peIt->get<0>() - pulseMaxTime;
237 
238  // place scaled pulse shape centered with max at current time bin
239  int i = -1;
240  for (double time = 0; time <= pulseStopTime; time += dt)
241  // center pulse max on pe release time. (may not be the most accurate thing to do ...)
242  base.AddTime(time + offset, realCharge * avgPulseShape[++i]);
243 
244  }
245 }
246 
247 
248 void
250  const TimeDistributionD& totalBaseSignal)
251 {
252  for (int i = baseSignal.GetStart(), n = baseSignal.GetStop(); i <= n; ++i) {
253  double& sig = baseSignal[i];
254  const double tot = totalBaseSignal[i];
255  const double maxDist = max(sig, tot);
256  const double scaling =
257  (maxDist > 0) ? fSaturationFunction->Y(maxDist) / maxDist : 1;
258  sig *= scaling;
259  }
260 }
261 
262 
265 {
266  if (!event.HasSEvent()) {
267  ERROR("Event does not have an SEvent");
268  return eFailure;
269  }
270 
271  SEvent& sEvent = event.GetSEvent();
272 
273  if (fStationIterator == sEvent.StationsEnd()) {
274  ostringstream info;
275  info << "all stations processed.";
276  INFO(info);
277  fFirstCycle = true;
278  fStationIterator = sEvent.StationsBegin();
279  // this check is to be backward compatible with
280  // module sequences that don't put SdPMTSimulator in a <loop>
282  return eBreakLoop;
283  }
284  }
285 
286  if (fFirstCycle) {
287  fStationIterator = sEvent.StationsBegin();
288  }
289 
290  unsigned int fStationsProcessed = 0;
291 
292  for (; fStationIterator != sEvent.StationsEnd(); ++fStationIterator) {
293 
294  if (fStationsProcessed == fNStationsPerCycle) {
295  fFirstCycle = false;
296  return eSuccess;
297  }
298 
299  for (Station::PMTIterator pIt = fStationIterator->PMTsBegin(sdet::PMTConstants::eAnyType);
300  pIt != fStationIterator->PMTsEnd(sdet::PMTConstants::eAnyType); ++pIt) {
301 
302  if (!pIt->HasSimData())
303  continue;
304 
306  const unsigned int nFundComponents=((pIt->GetType() == sdet::PMTConstants::eWaterCherenkovLarge) ? fFundamentalComponents.size() : fFundamentalComponents_ASCII.size() );
307  const double PMTGain=((pIt->GetType() == sdet::PMTConstants::eWaterCherenkovLarge) ? fPMTGain_WCD : fPMTGain_ASCII);
308 
309  PMTSimData& pSim = pIt->GetSimData();
310  if (!pSim.HasPETimeDistribution(totalComp))
311  continue;
312 
313  if (pSim.HasBaseSignal(totalComp))
314  continue;
315 
316  // no total PMT base signal exists yet
317  pSim.MakeBaseSignal(totalComp);
318 
319  for (unsigned int i = 0; i != nFundComponents; ++i) {
321  if (!pSim.HasPETimeDistribution(sigComp) ) continue;
322  if (!pSim.HasBaseSignal(sigComp)) pSim.MakeBaseSignal(sigComp);
323  TimeDistributionD& baseSignal = pSim.GetBaseSignal(sigComp);
324  ConvertPEToBaseSignal(pSim.GetPETimeDistribution(sigComp), baseSignal,PMTGain);
325  }
326 
327  // Make total or add the fundamental components
328  TimeDistributionD& totalBaseSignal = pSim.GetBaseSignal(totalComp);
329  if ( nFundComponents == 0) {
330  ConvertPEToBaseSignal(pSim.GetPETimeDistribution(totalComp), totalBaseSignal, PMTGain);
331  }
332  else {
333  for (unsigned int i = 0; i != nFundComponents; ++i) {
335  if (!pSim.HasPETimeDistribution(sigComp) ) continue;
336  TimeDistributionD& componentSignal = pSim.GetBaseSignal(sigComp);
337  for (TimeDistributionD::SparseIterator peIt = componentSignal.SparseBegin(), end = componentSignal.SparseEnd();
338  peIt != end; ++peIt) {
339  totalBaseSignal[peIt->get<0>()] += peIt->get<1>();
340  }
341  }
342  }
344  SimulateSaturation(totalBaseSignal, totalBaseSignal);
345 
346  }//Loop over PMTs
347 
348  ++fStationsProcessed;
349 
350  }//Loop over stations
351 
352  return eSuccess;
353 }
354 
355 
358 {
359  if (fWantRootHistos) {
360  TFile outFile(fRootHistoFilename.c_str(), "RECREATE");
361 
362  fChargeHisto->Write();
363  fAnodePulseHisto->Write();
364 
365  //fPEHistos->Write();
366  outFile.Write();
367  outFile.Close();
368 
369  delete fChargeHisto;
370  fChargeHisto = 0;
371  delete fAnodePulseHisto;
372  fAnodePulseHisto = 0;
373  }
374 
375  // some of this root objects get owned by TFile
376  delete fAveragePulseShape;
377  fAveragePulseShape = 0;
378  delete fChargeDist;
379  fChargeDist = 0;
380  delete fSaturationFunction;
382 
383  return eSuccess;
384 }
385 
386 
387 // Configure (x)emacs for this file ...
388 // Local Variables:
389 // mode: c++
390 // End:
std::vector< sevt::StationConstants::SignalComponent > fFundamentalComponents
Branch GetTopBranch() const
Definition: Branch.cc:63
int GetStart() const
First slot with data.
unsigned int GetNPoints() const
StationIterator StationsEnd()
End of all stations.
Definition: SEvent.h:59
Report success to RunController.
Definition: VModule.h:62
RandomEngineType & GetEngine()
Definition: RandomEngine.h:32
bool HasBaseSignal(const StationConstants::SignalComponent source=StationConstants::eTotal) const
Check if signal at PMT base already exists (optionally for a given source)
Definition: PMTSimData.h:105
Interface class to access to the SD part of an event.
Definition: SEvent.h:39
fwk::VModule::ResultFlag Run(evt::Event &event)
Run: invoked once per event.
Class to hold collection (x,y) points and provide interpolation between them.
std::map< std::string, std::string > AttributeMap
Definition: Branch.h:24
Histogram class for time distributions with suppressed empty bins.
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
void SimulateSaturation(utl::TimeDistributionD &baseSignal, const utl::TimeDistributionD &totalBaseSignal)
bool HasPETimeDistribution(const StationConstants::SignalComponent source=StationConstants::eTotal) const
Check if a PE release time distribution exists (optionally for a given source)
Definition: PMTSimData.h:65
double X() const
Definition: Pair.h:31
fwk::VModule::ResultFlag Init()
Initialize: invoked at beginning of run (NOT beginning of event)
#define max(a, b)
Class representing a document branch.
Definition: Branch.h:107
Break current loop. It works for nested loops too!
Definition: VModule.h:66
double GetBinning() const
Size of one slot.
void MakeBaseSignal(const StationConstants::SignalComponent source=StationConstants::eTotal)
Create a TimeDistributionD representing signal at PMT base (optionally for a give source) ...
Definition: PMTSimData.cc:19
constexpr double nanosecond
Definition: AugerUnits.h:143
int GetStop() const
Last slot with data (1 less than First slot if no data)
std::vector< sevt::StationConstants::SignalComponent > fFundamentalComponents_ASCII
fwk::VModule::ResultFlag Finish()
Finish: invoked at end of the run (NOT end of the event)
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
SparseIterator SparseEnd() const
void ConvertPEToBaseSignal(const utl::TimeDistributionI &pe, utl::TimeDistributionD &baseSignal, double PMTGain)
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
StationIterator StationsBegin()
Beginning of all stations.
Definition: SEvent.h:57
Class to hold simulated data at PMT level.
Definition: PMTSimData.h:40
total (shower and background)
Report failure to RunController, causing RunController to terminate execution.
Definition: VModule.h:64
SparseIterator SparseBegin() const
Iterator over time slots with data in them (skips empty slots).
utl::TimeDistributionI & GetPETimeDistribution(const StationConstants::SignalComponent source=StationConstants::eTotal)
Simulated photoelectron time distribution.
Definition: PMTSimData.h:54
utl::TimeDistributionD & GetBaseSignal(const StationConstants::SignalComponent source=StationConstants::eTotal)
Get simulated signal at the PMT base, optionally for a given source.
Definition: PMTSimData.h:94
double Y(const double x) const
Get or interpolate the Y value that corresponds to parameter x.
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
void AddTime(const double time, const T weight=T(1))
Add an entry (optionally weighted) for the given time. Slot will be computed.
bool HasSEvent() const
boost::transform_iterator< InternalMapFunctor, InternalConstIterator, Tuple > SparseIterator
const double volt
Definition: GalacticUnits.h:38

, generated on Tue Sep 26 2023.