8 #include <svd/modules/svdBackground/SVDBackgroundModule.h>
10 #include <framework/datastore/DataStore.h>
11 #include <framework/datastore/StoreObjPtr.h>
12 #include <framework/datastore/StoreArray.h>
13 #include <framework/datastore/RelationArray.h>
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 <svd/dataobjects/SVDSimHit.h>
20 #include <svd/dataobjects/SVDTrueHit.h>
21 #include <svd/dataobjects/SVDShaperDigit.h>
22 #include <svd/dataobjects/SVDCluster.h>
23 #include <svd/dataobjects/SVDEnergyDepositionEvent.h>
24 #include <svd/dataobjects/SVDNeutronFluxEvent.h>
25 #include <svd/dataobjects/SVDOccupancyEvent.h>
30 #include <boost/format.hpp>
42 double eToADU(
double charge)
44 double minADC = -96000;
45 double maxADC = 288000;
46 double unitADC = (maxADC - minADC) / 1024.0;
47 return round(std::min(maxADC, std::max(minADC, charge)) / unitADC);
61 Module(), m_outputDirectoryName(""),
62 m_doseReportingLevel(c_reportNTuple),
63 m_nfluxReportingLevel(c_reportNTuple),
64 m_occupancyReportingLevel(c_reportNTuple),
65 m_componentName(""), m_componentTime(0),
66 m_triggerWidth(5), m_acceptanceWidth(2.5),
67 m_nielNeutrons(new
TNiel(c_niel_neutronFile)),
68 m_nielProtons(new
TNiel(c_niel_protonFile)),
69 m_nielPions(new
TNiel(c_niel_pionFile)),
70 m_nielElectrons(new
TNiel(c_niel_electronFile))
73 setDescription(
"SVD background module");
74 setPropertyFlags(c_ParallelProcessingCertified);
76 addParam(
"componentName", m_componentName,
"Background component name to process", m_componentName);
77 addParam(
"componentTime", m_componentTime,
"Background component time", m_componentTime);
78 addParam(
"triggerWidth", m_triggerWidth,
"RMS of trigger time estimate in ns", m_triggerWidth);
79 addParam(
"acceptanceWidth", m_acceptanceWidth,
80 "A hit is accepted if arrived within +/- accpetanceWidth * RMS(hit time - trigger time) of trigger; in ns", m_acceptanceWidth);
81 addParam(
"doseReportingLevel", m_doseReportingLevel,
"0 - no data, 1 - summary only, 2 - summary + ntuple", m_doseReportingLevel);
82 addParam(
"nfluxReportingLevel", m_nfluxReportingLevel,
"0 - no data, 1 - summary only, 2 - summary + ntuple",
83 m_nfluxReportingLevel);
84 addParam(
"occupancyReportingLevel", m_occupancyReportingLevel,
"0 - no data, 1 - summary only, 2 - summary + ntuple",
85 m_occupancyReportingLevel);
86 addParam(
"outputDirectory", m_outputDirectoryName,
"Name of output directory", m_outputDirectoryName);
89 const TVector3& SVDBackgroundModule::pointToGlobal(
VxdID sensorID,
const TVector3& local)
91 static TVector3 result(0, 0, 0);
94 result = info.pointToGlobal(local);
98 const TVector3& SVDBackgroundModule::vectorToGlobal(
VxdID sensorID,
const TVector3& local)
100 static TVector3 result(0, 0, 0);
103 result = info.vectorToGlobal(local);
107 SVDBackgroundModule::~SVDBackgroundModule()
111 void SVDBackgroundModule::initialize()
122 RelationArray relDigitsMCParticles(storeDigits, storeMCParticles);
124 RelationArray relMCParticlesTrueHits(storeMCParticles, storeTrueHits);
125 RelationArray relTrueHitsSimHits(storeTrueHits, storeSimHits);
136 m_storeFileMetaDataName = storeFileMetaData.
getName();
137 m_storeBgMetaDataName = storeBgMetaData.
getName();
138 m_storeMCParticlesName = storeMCParticles.
getName();
139 m_storeSimHitsName = storeSimHits.
getName();
140 m_storeTrueHitsName = storeTrueHits.
getName();
141 m_storeDigitsName = storeDigits.
getName();
142 m_relDigitsMCParticlesName = relDigitsMCParticles.
getName();
143 m_relDigitsTrueHitsName = relDigitsTrueHits.
getName();
144 m_relParticlesTrueHitsName = relMCParticlesTrueHits.
getName();
145 m_relTrueHitsSimHitsName = relTrueHitsSimHits.
getName();
146 m_storeEnergyDepositsName = storeEnergyDeposits.
getName();
147 m_storeNeutronFluxesName = storeNeutronFluxes.
getName();
149 m_componentTime *= Unit::us;
150 m_acceptanceWidth *= Unit::ns;
151 m_triggerWidth *= Unit::ns;
154 void SVDBackgroundModule::beginRun()
158 void SVDBackgroundModule::event()
175 RelationArray relDigitsMCParticles(storeDigits, storeMCParticles, m_relDigitsMCParticlesName);
176 RelationArray relDigitsTrueHits(storeDigits, storeTrueHits, m_relDigitsTrueHitsName);
177 RelationArray relTrueHitsSimHits(storeTrueHits, storeSimHits, m_relTrueHitsSimHitsName);
178 RelationArray relTrueHitsMCParticles(storeMCParticles, storeTrueHits, m_relParticlesTrueHitsName);
181 double currentComponentTime = storeBgMetaData->getRealTime();
182 if (currentComponentTime != m_componentTime)
183 B2FATAL(
"Mismatch in component times:\n"
184 <<
"Steering file: " << m_componentTime <<
"\n"
185 <<
"Background file: " << currentComponentTime);
187 VxdID currentSensorID(0);
188 double currentSensorThickness(0);
189 double currentSensorMass(0);
190 double currentSensorArea(0);
193 if (m_doseReportingLevel > c_reportNone) {
194 B2DEBUG(100,
"Expo and dose");
195 currentSensorID.
setID(0);
196 for (
const SVDSimHit& hit : storeSimHits) {
198 VxdID sensorID = hit.getSensorID();
199 if (sensorID != currentSensorID) {
200 currentSensorID = sensorID;
201 currentSensorThickness = getSensorThickness(currentSensorID);
202 currentSensorMass = getSensorMass(currentSensorID);
203 currentSensorArea = getSensorArea(currentSensorID);
205 double hitEnergy = hit.getElectrons() * Const::ehEnergy;
207 m_sensorData[currentSensorID].m_dose +=
208 (hitEnergy / Unit::J) / (currentSensorMass / 1000) * (c_smy / currentComponentTime);
210 m_sensorData[currentSensorID].m_expo += hitEnergy / currentSensorArea / (currentComponentTime / Unit::s);
211 if (m_doseReportingLevel == c_reportNTuple) {
212 const TVector3 localPos = hit.getPosIn();
213 const TVector3 globalPos = pointToGlobal(currentSensorID, localPos);
214 float globalPosXYZ[3];
215 globalPos.GetXYZ(globalPosXYZ);
218 hit.getPDGcode(), hit.getGlobalTime(),
219 localPos.X(), localPos.Y(), globalPosXYZ, hitEnergy,
220 (hitEnergy / Unit::J) / (currentSensorMass / 1000) / (currentComponentTime / Unit::s),
221 (hitEnergy / Unit::J) / currentSensorArea / (currentComponentTime / Unit::s)
228 if (m_nfluxReportingLevel > c_reportNone) {
229 B2DEBUG(100,
"Neutron flux");
230 currentSensorID.
setID(0);
232 VxdID sensorID = hit.getSensorID();
234 if (sensorID != currentSensorID) {
235 currentSensorID = sensorID;
236 currentSensorThickness = getSensorThickness(currentSensorID);
237 currentSensorMass = getSensorMass(currentSensorID);
238 currentSensorArea = getSensorArea(currentSensorID);
241 TVector3 entryPos(hit.getEntryU(), hit.getEntryV(), hit.getEntryW());
242 TVector3 exitPos(hit.getExitU(), hit.getExitV(), hit.getExitW());
243 double stepLength = (exitPos - entryPos).Mag();
249 double minDistance = 1.0e10;
250 for (
const SVDSimHit& related : storeSimHits) {
251 double distance = (entryPos - related.getPosIn()).Mag();
252 if (distance < minDistance) {
253 minDistance = distance;
262 TVector3 hitMomentum(hit.getMomentum());
263 hitMomentum.SetX(std::isfinite(hitMomentum.X()) ? hitMomentum.X() : 0.0);
264 hitMomentum.SetY(std::isfinite(hitMomentum.Y()) ? hitMomentum.Y() : 0.0);
265 hitMomentum.SetZ(std::isfinite(hitMomentum.Z()) ? hitMomentum.Z() : 0.0);
267 double kineticEnergy(0.0);
268 double nielWeight(0.0);
269 if (pdg == Const::neutron.getPDGCode()) {
270 double m0 = Const::neutronMass;
271 kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
272 nielWeight = m_nielNeutrons->getNielFactor(kineticEnergy / Unit::MeV);
274 if (pdg == Const::proton.getPDGCode()) {
275 double m0 = Const::protonMass;
276 kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
277 nielWeight = m_nielProtons->getNielFactor(kineticEnergy / Unit::MeV);
279 if (pdg == Const::pi0.getPDGCode() || pdg == Const::pion.getPDGCode()) {
280 double m0 = Const::pi0Mass;
281 kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
282 nielWeight = m_nielPions->getNielFactor(kineticEnergy / Unit::MeV);
284 if (pdg == Const::electron.getPDGCode()) {
285 double m0 = Const::electronMass;
286 kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
287 nielWeight = m_nielElectrons->getNielFactor(kineticEnergy / Unit::MeV);
289 if (pdg == Const::photon.getPDGCode()) {
291 kineticEnergy = sqrt(hitMomentum.Mag2() + m0 * m0) - m0;
295 nielWeight = std::isfinite(nielWeight) ? nielWeight : 0.0;
296 m_sensorData[currentSensorID].m_neutronFlux += nielWeight * stepLength / currentSensorThickness / currentSensorArea /
297 currentComponentTime * c_smy;
300 if (m_nfluxReportingLevel == c_reportNTuple) {
301 TVector3 localPos(hit.getU(), hit.getV(), hit.getW());
302 const TVector3 globalPos = pointToGlobal(currentSensorID, localPos);
303 float globalPosXYZ[3];
304 globalPos.GetXYZ(globalPosXYZ);
305 TVector3 localMom = hit.getMomentum();
306 const TVector3 globalMom = vectorToGlobal(currentSensorID, localMom);
307 float globalMomXYZ[3];
308 globalMom.GetXYZ(globalMomXYZ);
312 hit.getU(), hit.getV(), globalPosXYZ, globalMomXYZ, kineticEnergy,
313 stepLength, nielWeight,
314 stepLength / currentSensorThickness / currentSensorArea / (currentComponentTime / Unit::s),
315 nielWeight * stepLength / currentSensorThickness / currentSensorArea / (currentComponentTime / Unit::s)
322 if (m_occupancyReportingLevel > c_reportNone) {
323 B2DEBUG(100,
"Fired strips");
324 currentSensorID.
setID(0);
325 double currentSensorUCut = 0;
326 double currentSensorVCut = 0;
328 std::map<VxdID, std::multiset<unsigned short> > firedStrips;
332 VxdID sensorID = digit.getSensorID();
333 if (sensorID != currentSensorID) {
334 currentSensorID = sensorID;
335 auto info = getInfo(sensorID);
336 currentSensorUCut = eToADU(3.0 * info.getElectronicNoiseU());
337 currentSensorVCut = eToADU(3.0 * info.getElectronicNoiseV());
339 B2DEBUG(30,
"MaxCharge: " << digit.getMaxADCCounts() <<
" threshold: " << (digit.isUStrip() ? currentSensorUCut :
341 if (digit.getMaxADCCounts() < (digit.isUStrip() ? currentSensorUCut : currentSensorVCut))
continue;
342 B2DEBUG(30,
"Passed.");
344 VxdID writeID(sensorID);
345 if (digit.isUStrip())
349 firedStrips[writeID].insert(digit.getCellID());
352 for (
auto idAndSet : firedStrips) {
353 bool isUStrip = (idAndSet.first.getSegmentNumber() == 0);
354 VxdID sensorID = idAndSet.first;
356 double sensorArea = getSensorArea(sensorID);
357 int nFired_APV = idAndSet.second.size();
359 for (
auto it = idAndSet.second.begin();
360 it != idAndSet.second.end();
361 it = idAndSet.second.upper_bound(*it)) nFired++;
362 double fired = nFired / (currentComponentTime / Unit::s) / sensorArea;
363 double fired_t = nFired_APV * c_APVCycleTime / (currentComponentTime / Unit::s) / sensorArea;
365 m_sensorData[sensorID].m_firedU += fired;
366 m_sensorData[sensorID].m_firedU_t += fired_t;
368 m_sensorData[sensorID].m_firedV += fired;
369 m_sensorData[sensorID].m_firedV_t += fired_t;
388 B2DEBUG(100,
"Occupancy");
389 currentSensorID.
setID(0);
390 double currentNoiseU = 0;
391 double currentNoiseV = 0;
394 for (
auto cluster : storeClsuters) {
395 VxdID sensorID = cluster.getSensorID();
396 if (currentSensorID != sensorID) {
397 currentSensorID = sensorID;
398 auto info = getInfo(sensorID);
399 currentNoiseU = eToADU(info.getElectronicNoiseU());
400 currentNoiseV = eToADU(info.getElectronicNoiseV());
401 nStripsU = info.getUCells();
402 nStripsV = info.getVCells();
404 bool isU = cluster.isUCluster();
405 double snr = (isU) ? cluster.getCharge() / currentNoiseU : cluster.getCharge() / currentNoiseV;
406 int nStrips = (isU) ? nStripsU : nStripsV;
407 double tau_error = 45 / snr * Unit::ns;
408 tau_error = sqrt(m_triggerWidth * m_triggerWidth + tau_error * tau_error);
409 double tau_acceptance = 2 * m_acceptanceWidth * tau_error;
410 double w_acceptance = tau_acceptance / currentComponentTime;
411 double w_acceptance_APV = c_APVCycleTime / currentComponentTime;
412 double occupancy = 1.0 / nStrips * cluster.getSize();
414 m_sensorData[sensorID].m_occupancyU += w_acceptance * occupancy;
415 m_sensorData[sensorID].m_occupancyU_APV += w_acceptance_APV * occupancy;
417 m_sensorData[sensorID].m_occupancyV += w_acceptance * occupancy;
418 m_sensorData[sensorID].m_occupancyV_APV += w_acceptance_APV * occupancy;
420 if (m_occupancyReportingLevel == c_reportNTuple) {
424 cluster.isUCluster(), cluster.getPosition(), cluster.getSize(),
425 cluster.getCharge(), snr, w_acceptance, w_acceptance * occupancy,
426 w_acceptance_APV * occupancy
433 void SVDBackgroundModule::endRun()
438 void SVDBackgroundModule::terminate()
442 string outfileName(m_outputDirectoryName + m_componentName +
"_summary.txt");
443 outfile.open(outfileName.c_str(), ios::out | ios::trunc);
444 outfile <<
"component_name\t"
445 <<
"component_time\t"
458 <<
"occupancy_u_APV\t"
461 double componentTime = m_componentTime / Unit::us;
462 for (
auto vxdSensor : m_sensorData) {
463 outfile << m_componentName.c_str() <<
"\t"
464 << componentTime <<
"\t"
465 << vxdSensor.first.getLayerNumber() <<
"\t"
466 << vxdSensor.first.getLadderNumber() <<
"\t"
467 << vxdSensor.first.getSensorNumber() <<
"\t"
468 << vxdSensor.second.m_dose <<
"\t"
469 << vxdSensor.second.m_expo <<
"\t"
470 << vxdSensor.second.m_neutronFlux <<
"\t"
471 << vxdSensor.second.m_firedU <<
"\t"
472 << vxdSensor.second.m_firedV <<
"\t"
473 << vxdSensor.second.m_firedU_t <<
"\t"
474 << vxdSensor.second.m_firedV_t <<
"\t"
475 << vxdSensor.second.m_occupancyU <<
"\t"
476 << vxdSensor.second.m_occupancyV <<
"\t"
477 << vxdSensor.second.m_occupancyU_APV <<
"\t"
478 << vxdSensor.second.m_occupancyV_APV
Low-level class to create/modify relations between StoreArrays.
The SVD ShaperDigit class.
Class SVDSimHit - Geant4 simulated hit for the SVD.
Class SVDTrueHit - Records of tracks that either enter or leave the sensitive volume.
Specific implementation of SensorInfo for SVD Sensors which provides additional sensor specific infor...
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.
Type-safe access to single objects in the data store.
float getGlobalTime() const override
Return the time of the electron deposition.
int getPDGcode() const
Return the PDG code of the particle causing the electron deposition.
Class to uniquely identify a any structure of the PXD and SVD.
void setSegmentNumber(baseType segment)
Set the sensor segment.
baseType getSensorNumber() const
Get the sensor id.
void setID(baseType id)
Set the unique id.
baseType getLadderNumber() const
Get the ladder id.
baseType getLayerNumber() const
Get the layer id.
TNiel - the class providing values for NIEL factors.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Namespace to encapsulate code needed for simulation and reconstrucion of the SVD.
Abstract base class for different kinds of events.