Belle II Software  release-06-02-00
PXDBackgroundModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 #include <pxd/modules/pxdBackground/PXDBackgroundModule.h>
9 
10 #include <framework/datastore/DataStore.h>
11 #include <framework/datastore/StoreObjPtr.h>
12 #include <framework/datastore/StoreArray.h>
13 #include <framework/datastore/RelationArray.h>
14 
15 #include <framework/dataobjects/FileMetaData.h>
16 #include <framework/dataobjects/BackgroundMetaData.h>
17 #include <framework/gearbox/Const.h>
18 #include <mdst/dataobjects/MCParticle.h>
19 #include <pxd/dataobjects/PXDSimHit.h>
20 #include <pxd/dataobjects/PXDTrueHit.h>
21 #include <pxd/dataobjects/PXDDigit.h>
22 #include <pxd/dataobjects/PXDCluster.h>
23 #include <pxd/dataobjects/PXDEnergyDepositionEvent.h>
24 #include <pxd/dataobjects/PXDNeutronFluxEvent.h>
25 #include <pxd/dataobjects/PXDOccupancyEvent.h>
26 #include <cmath>
27 #include <fstream>
28 #include <boost/format.hpp>
29 
30 #include "TVector3.h"
31 
32 using namespace std;
33 using boost::format;
34 using namespace Belle2;
35 using namespace Belle2::PXD;
36 
37 
38 
39 //-----------------------------------------------------------------
40 // Register the Module
41 //-----------------------------------------------------------------
42 REG_MODULE(PXDBackground)
43 
44 
45 //-----------------------------------------------------------------
46 // Implementation
47 //-----------------------------------------------------------------
48 
50  Module(), m_outputDirectoryName(""),
51  m_doseReportingLevel(c_reportNTuple),
52  m_nfluxReportingLevel(c_reportNTuple),
53  m_occupancyReportingLevel(c_reportNTuple),
54  m_componentName(""), m_componentTime(0), m_integrationTime(20),
55  m_nielNeutrons(new TNiel(c_niel_neutronFile)),
56  m_nielProtons(new TNiel(c_niel_protonFile)),
57  m_nielPions(new TNiel(c_niel_pionFile)),
58  m_nielElectrons(new TNiel(c_niel_electronFile))
59 {
60  //Set module properties
61  setDescription("PXD background module");
62  setPropertyFlags(c_ParallelProcessingCertified); // specify this flag if you need parallel processing
63  // FIXME: This information can in principle be extracted from bg files, though not trivially.
64  addParam("componentName", m_componentName, "Background component name to process", m_componentName);
65  addParam("componentTime", m_componentTime, "Background component time", m_componentTime);
66  addParam("integrationTime", m_integrationTime, "PXD integration time", m_integrationTime);
67  addParam("doseReportingLevel", m_doseReportingLevel, "0 - no data, 1 - summary only, 2 - summary + ntuple", m_doseReportingLevel);
68  addParam("nfluxReportingLevel", m_nfluxReportingLevel, "0 - no data, 1 - summary only, 2 - summary + ntuple",
69  m_nfluxReportingLevel);
70  addParam("occupancyReportingLevel", m_occupancyReportingLevel, "0 - no data, 1 - summary only, 2 - summary + ntuple",
71  m_occupancyReportingLevel);
72  addParam("outputDirectory", m_outputDirectoryName, "Name of output directory", m_outputDirectoryName);
73 }
74 
75 const TVector3& PXDBackgroundModule::pointToGlobal(VxdID sensorID, const TVector3& local)
76 {
77  static TVector3 result(0, 0, 0);
78 
79  const PXD::SensorInfo& info = getInfo(sensorID);
80  result = info.pointToGlobal(local);
81  return result;
82 }
83 
84 const TVector3& PXDBackgroundModule::vectorToGlobal(VxdID sensorID, const TVector3& local)
85 {
86  static TVector3 result(0, 0, 0);
87 
88  const PXD::SensorInfo& info = getInfo(sensorID);
89  result = info.vectorToGlobal(local);
90  return result;
91 }
92 
93 PXDBackgroundModule::~PXDBackgroundModule()
94 {
95 }
96 
97 void PXDBackgroundModule::initialize()
98 {
99  //Register collections
100  StoreObjPtr<FileMetaData> storeFileMetaData(m_storeFileMetaDataName, DataStore::c_Persistent);
101  StoreObjPtr<BackgroundMetaData> storeBgMetaData(m_storeBgMetaDataName, DataStore::c_Persistent);
102  StoreArray<MCParticle> storeMCParticles(m_storeMCParticlesName);
103  StoreArray<PXDSimHit> storeSimHits(m_storeSimHitsName);
104  StoreArray<PXDTrueHit> storeTrueHits(m_storeTrueHitsName);
105  StoreArray<PXDDigit> storeDigits(m_storeDigitsName);
106  StoreArray<PXDCluster> storeClusters(m_storeClustersName);
107 
108  RelationArray relDigitsMCParticles(storeDigits, storeMCParticles);
109  RelationArray relDigitsTrueHits(storeDigits, storeTrueHits);
110  RelationArray relMCParticlesTrueHits(storeMCParticles, storeTrueHits);
111  RelationArray relTrueHitsSimHits(storeTrueHits, storeSimHits);
112 
113  // Add two new StoreArrays
114  StoreArray<PXDEnergyDepositionEvent> storeEnergyDeposits(m_storeEnergyDepositsName);
115  storeEnergyDeposits.registerInDataStore();
116  StoreArray<PXDNeutronFluxEvent> storeNeutronFluxes(m_storeNeutronFluxesName);
117  storeNeutronFluxes.registerInDataStore();
118  StoreArray<PXDOccupancyEvent> storeOccupancyEvents(m_storeOccupancyEventsName);
119  storeOccupancyEvents.registerInDataStore();
120 
121  //Store names to speed up creation later
122  m_storeFileMetaDataName = storeFileMetaData.getName();
123  m_storeBgMetaDataName = storeBgMetaData.getName();
124  m_storeMCParticlesName = storeMCParticles.getName();
125  m_storeSimHitsName = storeSimHits.getName();
126  m_storeTrueHitsName = storeTrueHits.getName();
127  m_storeDigitsName = storeDigits.getName();
128  m_relDigitsMCParticlesName = relDigitsMCParticles.getName();
129  m_relDigitsTrueHitsName = relDigitsTrueHits.getName();
130  m_relParticlesTrueHitsName = relMCParticlesTrueHits.getName();
131  m_relTrueHitsSimHitsName = relTrueHitsSimHits.getName();
132  m_storeEnergyDepositsName = storeEnergyDeposits.getName();
133  m_storeNeutronFluxesName = storeNeutronFluxes.getName();
134 
135  m_componentTime *= Unit::us;
136  m_integrationTime *= Unit::us;
137 }
138 
139 void PXDBackgroundModule::beginRun()
140 {
141 }
142 
143 void PXDBackgroundModule::event()
144 {
145  //Register collections
146  const StoreObjPtr<FileMetaData> storeFileMetaData(m_storeFileMetaDataName, DataStore::c_Persistent);
147  const StoreObjPtr<BackgroundMetaData> storeBgMetaData(m_storeBgMetaDataName, DataStore::c_Persistent);
148  const StoreArray<MCParticle> storeMCParticles(m_storeMCParticlesName);
149  const StoreArray<PXDSimHit> storeSimHits(m_storeSimHitsName);
150  const StoreArray<PXDTrueHit> storeTrueHits(m_storeTrueHitsName);
151  const StoreArray<PXDDigit> storeDigits(m_storeDigitsName);
152  const StoreArray<PXDCluster> storeClsuters(m_storeClustersName);
153 
154  // Add two new StoreArrays
155  StoreArray<PXDEnergyDepositionEvent> storeEnergyDeposits(m_storeEnergyDepositsName);
156  StoreArray<PXDNeutronFluxEvent> storeNeutronFluxes(m_storeNeutronFluxesName);
157  StoreArray<PXDOccupancyEvent> storeOccupancyEvents(m_storeOccupancyEventsName);
158 
159  // Relations
160  RelationArray relDigitsMCParticles(storeDigits, storeMCParticles, m_relDigitsMCParticlesName);
161  RelationArray relDigitsTrueHits(storeDigits, storeTrueHits, m_relDigitsTrueHitsName);
162  RelationArray relTrueHitsSimHits(storeTrueHits, storeSimHits, m_relTrueHitsSimHitsName);
163  RelationArray relTrueHitsMCParticles(storeMCParticles, storeTrueHits, m_relParticlesTrueHitsName);
164 
165  // unsigned long numberOfEvents = storeFileMetaData->getNEvents();
166  double currentComponentTime = storeBgMetaData->getRealTime();
167  if (currentComponentTime != m_componentTime)
168  B2FATAL("Mismatch in component times:\n"
169  << "Steering file: " << m_componentTime << "\n"
170  << "Background file: " << currentComponentTime);
171 
172  VxdID currentSensorID(0);
173  double currentSensorThickness(0);
174  double currentSensorMass(0);
175  double currentSensorArea(0);
176 
177  // Exposition and dose
178  if (m_doseReportingLevel > c_reportNone) {
179  B2DEBUG(100, "Expo and dose");
180  currentSensorID.setID(0);
181  for (const PXDSimHit& hit : storeSimHits) {
182  // Update if we have a new sensor
183  VxdID sensorID = hit.getSensorID();
184  if (sensorID != currentSensorID) {
185  currentSensorID = sensorID;
186  currentSensorThickness = getSensorThickness(currentSensorID);
187  currentSensorMass = getSensorMass(currentSensorID);
188  currentSensorArea = getSensorArea(currentSensorID);
189  }
190  double hitEnergy = hit.getElectrons() * Const::ehEnergy;
191  // Dose in Gy/smy, normalize by sensor mass
192  m_sensorData[currentSensorID].m_dose +=
193  (hitEnergy / Unit::J) / (currentSensorMass / 1000) * (c_smy / currentComponentTime);
194  // Exposition in GeV/cm2/s
195  m_sensorData[currentSensorID].m_expo += hitEnergy / currentSensorArea / (currentComponentTime / Unit::s);
196  if (m_doseReportingLevel == c_reportNTuple) {
197  const TVector3 localPos = hit.getPosIn();
198  const TVector3 globalPos = pointToGlobal(currentSensorID, localPos);
199  float globalPosXYZ[3];
200  globalPos.GetXYZ(globalPosXYZ);
201  storeEnergyDeposits.appendNew(
202  sensorID.getLayerNumber(), sensorID.getLadderNumber(), sensorID.getSensorNumber(),
203  hit.getPDGcode(), hit.getGlobalTime(),
204  localPos.X(), localPos.Y(), globalPosXYZ, hitEnergy,
205  (hitEnergy / Unit::J) / (currentSensorMass / 1000) / (currentComponentTime / Unit::s),
206  (hitEnergy / Unit::J) / currentSensorArea / (currentComponentTime / Unit::s)
207  );
208  }
209  }
210  }
211 
212  // Neutron flux
213  if (m_nfluxReportingLevel > c_reportNone) {
214  B2DEBUG(100, "Neutron flux");
215  currentSensorID.setID(0);
216  for (const PXDTrueHit& hit : storeTrueHits) {
217  VxdID sensorID = hit.getSensorID();
218  // Update if we are on a new sensor
219  if (sensorID != currentSensorID) {
220  currentSensorID = sensorID;
221  currentSensorThickness = getSensorThickness(currentSensorID);
222  currentSensorMass = getSensorMass(currentSensorID);
223  currentSensorArea = getSensorArea(currentSensorID);
224  }
225  // J(TrueHit) = abs(step)/thickness * correctionFactor;
226  TVector3 entryPos(hit.getEntryU(), hit.getEntryV(), hit.getEntryW());
227  TVector3 exitPos(hit.getExitU(), hit.getExitV(), hit.getExitW());
228  double stepLength = (exitPos - entryPos).Mag();
229  // Identify what particle we've got. We need type and kinetic energy.
230  // TODO: TrueHit must carry pdg or SimHit must carry energy.
231  // NOTE: MCParticles may get remapped, then SimHits still carry correct pdg.
232  const PXDSimHit* simhit = hit.getRelatedTo<PXDSimHit>();
233  if (!simhit) { //either something is very wrong, or we just don't have the relation. Try to find an appropriate SimHit manually.
234  double minDistance = 1.0e10;
235  for (const PXDSimHit& related : storeSimHits) {
236  double distance = (entryPos - related.getPosIn()).Mag();
237  if (distance < minDistance) {
238  minDistance = distance;
239  simhit = &related;
240  }
241  }
242  }
243  // FIXME: Is there a difference between positrons and electrons wrt. NIEL?
244  // We fill neutronFluxBars with summary NIEL deposit for all kinds of particles by layer and component.
245  // Fluency plots are by component and are deposition histograms for a particular type of particle and compoonent.
246  // Special treatment of corrupt p's in TrueHits:
247  TVector3 hitMomentum(hit.getMomentum());
248  hitMomentum.SetX(std::isfinite(hitMomentum.X()) ? hitMomentum.X() : 0.0);
249  hitMomentum.SetY(std::isfinite(hitMomentum.Y()) ? hitMomentum.Y() : 0.0);
250  hitMomentum.SetZ(std::isfinite(hitMomentum.Z()) ? hitMomentum.Z() : 0.0);
251  int pdg = abs(simhit->getPDGcode());
252  double kineticEnergy(0.0);
253  double nielWeight(0.0);
254  if (pdg == Const::neutron.getPDGCode()) {
255  double m0 = Const::neutronMass;
256  kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
257  nielWeight = m_nielNeutrons->getNielFactor(kineticEnergy / Unit::MeV);
258  }
259  if (pdg == Const::proton.getPDGCode()) {
260  double m0 = Const::protonMass;
261  kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
262  nielWeight = m_nielProtons->getNielFactor(kineticEnergy / Unit::MeV);
263  }
264  if (pdg == Const::pi0.getPDGCode() || pdg == Const::pion.getPDGCode()) {
265  double m0 = Const::pi0Mass;
266  kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
267  nielWeight = m_nielPions->getNielFactor(kineticEnergy / Unit::MeV);
268  }
269  if (pdg == Const::electron.getPDGCode()) {
270  double m0 = Const::electronMass;
271  kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
272  nielWeight = m_nielElectrons->getNielFactor(kineticEnergy / Unit::MeV);
273  }
274  if (pdg == Const::photon.getPDGCode()) {
275  double m0 = 0.0;
276  kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
277  }
278 
279  // Only set weight for supported particles
280  nielWeight = std::isfinite(nielWeight) ? nielWeight : 0.0;
281  m_sensorData[currentSensorID].m_neutronFlux += nielWeight * stepLength / currentSensorThickness / currentSensorArea /
282  currentComponentTime * c_smy;
283 
284  // Store data in a PXDNeutronFluxEvent object
285  if (m_nfluxReportingLevel == c_reportNTuple) {
286  TVector3 localPos(hit.getU(), hit.getV(), hit.getW());
287  const TVector3 globalPos = pointToGlobal(currentSensorID, localPos);
288  float globalPosXYZ[3];
289  globalPos.GetXYZ(globalPosXYZ);
290  TVector3 localMom = hit.getMomentum();
291  const TVector3 globalMom = vectorToGlobal(currentSensorID, localMom);
292  float globalMomXYZ[3];
293  globalMom.GetXYZ(globalMomXYZ);
294  storeNeutronFluxes.appendNew(
295  sensorID.getLayerNumber(), sensorID.getLadderNumber(), sensorID.getSensorNumber(),
296  simhit->getPDGcode(), simhit->getGlobalTime(),
297  hit.getU(), hit.getV(), globalPosXYZ, globalMomXYZ, kineticEnergy,
298  stepLength, nielWeight,
299  stepLength / currentSensorThickness / currentSensorArea / (currentComponentTime / Unit::s),
300  nielWeight * stepLength / currentSensorThickness / currentSensorArea / (currentComponentTime / Unit::s)
301  );
302  }
303  }
304  }
305 
306  // Occupancy
307  if (m_occupancyReportingLevel > c_reportNone) {
308  B2DEBUG(100, "Fired pixels");
309  currentSensorID.setID(0);
310  double currentSensorCut = 0;
311  // Store fired pixels: count number of digits over threshold
312  std::map<VxdID, std::vector<float> > firedPixels;
313  for (const PXDDigit& storeDigit : storeDigits) {
314  // Filter out digits with signals below zero-suppression threshold
315  // ARE THRE SUCH DIGITS?
316  VxdID sensorID = storeDigit.getSensorID();
317  if (sensorID != currentSensorID) {
318  currentSensorID = sensorID;
319  auto info = getInfo(sensorID);
320  currentSensorCut = info.getChargeThreshold();
321  }
322  B2DEBUG(30, "Digit charge: " << storeDigit.getCharge() << " threshold: " << currentSensorCut);
323  if (storeDigit.getCharge() < currentSensorCut) continue;
324  B2DEBUG(30, "Passed.");
325  firedPixels[sensorID].push_back(storeDigit.getCharge());
326  }
327  // Process the map
328  for (auto idAndSet : firedPixels) {
329  VxdID sensorID = idAndSet.first;
330  double sensorArea = getSensorArea(sensorID);
331  int nFired = idAndSet.second.size();
332  double fired = nFired / (currentComponentTime / Unit::s) / sensorArea;
333  m_sensorData[sensorID].m_fired += fired;
334  }
335 
336  B2DEBUG(100, "Occupancy");
337  currentSensorID.setID(0);
338  int nPixels = 0;
339  for (auto cluster : storeClsuters) {
340  VxdID sensorID = cluster.getSensorID();
341  if (currentSensorID != sensorID) {
342  currentSensorID = sensorID;
343  auto info = getInfo(sensorID);
344  nPixels = info.getUCells() * info.getVCells();
345  }
346 
347  double w_acceptance = m_integrationTime / currentComponentTime;
348  double occupancy = 1.0 / nPixels * cluster.getSize();
349  m_sensorData[sensorID].m_occupancy += w_acceptance * occupancy;
350 
351  if (m_occupancyReportingLevel == c_reportNTuple) {
352  storeOccupancyEvents.appendNew(
353  sensorID.getLayerNumber(), sensorID.getLadderNumber(),
354  sensorID.getSensorNumber(),
355  cluster.getU(), cluster.getV(), cluster.getSize(),
356  cluster.getCharge(), occupancy
357  );
358  }
359  }
360  }
361 }
362 
363 void PXDBackgroundModule::endRun()
364 {
365 }
366 
367 
368 void PXDBackgroundModule::terminate()
369 {
370  // Write out m_data
371  ofstream outfile;
372  string outfileName(m_outputDirectoryName + m_componentName + "_summary.txt");
373  outfile.open(outfileName.c_str(), ios::out | ios::trunc);
374  outfile << "component_name\t"
375  << "component_time\t"
376  << "layer\t"
377  << "ladder\t"
378  << "sensor\t"
379  << "dose\t"
380  << "expo\t"
381  << "neutronFlux\t"
382  << "fired\t"
383  << "occupancy"
384  << endl;
385  double componentTime = m_componentTime / Unit::us;
386  for (auto vxdSensor : m_sensorData) {
387  outfile << m_componentName.c_str() << "\t"
388  << componentTime << "\t"
389  << vxdSensor.first.getLayerNumber() << "\t"
390  << vxdSensor.first.getLadderNumber() << "\t"
391  << vxdSensor.first.getSensorNumber() << "\t"
392  << vxdSensor.second.m_dose << "\t"
393  << vxdSensor.second.m_expo << "\t"
394  << vxdSensor.second.m_neutronFlux << "\t"
395  << vxdSensor.second.m_fired << "\t"
396  << vxdSensor.second.m_occupancy
397  << endl;
398  }
399  outfile << endl;
400 }
Base class for Modules.
Definition: Module.h:72
The PXD digit class.
Definition: PXDDigit.h:27
Class PXDSimHit - Geant4 simulated hit for the PXD.
Definition: PXDSimHit.h:24
Class PXDTrueHit - Records of tracks that either enter or leave the sensitive volume.
Definition: PXDTrueHit.h:31
Specific implementation of SensorInfo for PXD Sensors which provides additional pixel specific inform...
Definition: SensorInfo.h:23
Low-level class to create/modify relations between StoreArrays.
Definition: RelationArray.h:62
const std::string & getName() const
Return name under which the object is saved in the DataStore.
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:246
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:95
float getGlobalTime() const override
Return the time of the electron deposition.
Definition: VXDSimHit.h:78
int getPDGcode() const
Return the PDG code of the particle causing the electron deposition.
Definition: VXDSimHit.h:68
Class to uniquely identify a any structure of the PXD and SVD.
Definition: VxdID.h:33
baseType getSensorNumber() const
Get the sensor id.
Definition: VxdID.h:100
void setID(baseType id)
Set the unique id.
Definition: VxdID.h:105
baseType getLadderNumber() const
Get the ladder id.
Definition: VxdID.h:98
baseType getLayerNumber() const
Get the layer id.
Definition: VxdID.h:96
TNiel - the class providing values for NIEL factors.
Definition: niel_fun.h:17
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Namespace to encapsulate code needed for simulation and reconstrucion of the PXD.
Abstract base class for different kinds of events.