Belle II Software development
eclMuMuECollectorModule.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
9/* Own header. */
10#include <ecl/modules/eclMuMuECollector/eclMuMuECollectorModule.h>
11
12/* ECL headers. */
13#include <ecl/dataobjects/ECLDigit.h>
14#include <ecl/dataobjects/ECLElementNumbers.h>
15#include <ecl/dbobjects/ECLCrystalCalib.h>
16#include <ecl/geometry/ECLNeighbours.h>
17
18/* Basf2 headers. */
19#include <framework/dataobjects/EventMetaData.h>
20#include <framework/gearbox/Const.h>
21#include <mdst/dataobjects/ECLCluster.h>
22#include <mdst/dataobjects/Track.h>
23#include <mdst/dataobjects/TRGSummary.h>
24#include <tracking/dataobjects/ExtHit.h>
25
26/* ROOT headers. */
27#include <TH2F.h>
28#include <TMath.h>
29
30using namespace std;
31using namespace Belle2;
32using namespace ECL;
33
34
35//-----------------------------------------------------------------
36// Register the Module
37//-----------------------------------------------------------------
38REG_MODULE(eclMuMuECollector);
39
40//-----------------------------------------------------------------
41// Implementation
42//-----------------------------------------------------------------
43
44//-----------------------------------------------------------------------------------------------------
45
47 m_ElectronicsCalib("ECLCrystalElectronics"), m_MuMuECalib("ECLCrystalEnergyMuMu"), m_CrystalEnergy("ECLCrystalEnergy")
48{
49 // Set module properties
50 setDescription("Calibration Collector Module for ECL single crystal energy calibration using muons");
52 addParam("minPairMass", m_minPairMass, "minimum invariant mass of the muon pair (GeV/c^2)", 9.0);
53 addParam("minTrackLength", m_minTrackLength, "minimum extrapolated track length in the crystal (cm)", 30.);
54 addParam("MaxNeighbourE", m_MaxNeighbourE, "maximum energy allowed in a neighbouring crystal (GeV)", 0.010);
55 addParam("thetaLabMinDeg", m_thetaLabMinDeg, "minimum muon theta in lab (degrees)", 17.);
56 addParam("thetaLabMaxDeg", m_thetaLabMaxDeg, "maximum muon theta in lab (degrees)", 150.);
57 addParam("measureTrueEnergy", m_measureTrueEnergy, "use MC events to obtain expected energies", false);
58 addParam("requireL1", m_requireL1, "only use events that have a level 1 trigger", true);
59}
60
64{
65
69 B2INFO("eclMuMuECollector: Experiment = " << m_evtMetaData->getExperiment() << " run = " << m_evtMetaData->getRun());
70
73 auto TrkPerCrysID = new TH1F("TrkPerCrysID", "track extrapolations per crystalID;crystal ID", ECLElementNumbers::c_NCrystals, 0,
75 registerObject<TH1F>("TrkPerCrysID", TrkPerCrysID);
76
77 auto EnVsCrysID = new TH2F("EnVsCrysID", "Normalized energy for each crystal;crystal ID;E/Expected", ECLElementNumbers::c_NCrystals,
78 0, ECLElementNumbers::c_NCrystals, 240, 0.1, 2.5);
79 registerObject<TH2F>("EnVsCrysID", EnVsCrysID);
80
81 auto ExpEvsCrys = new TH1F("ExpEvsCrys", "Sum expected energy vs crystal ID;crystal ID;Energy (GeV)",
83 registerObject<TH1F>("ExpEvsCrys", ExpEvsCrys);
84
85 auto ElecCalibvsCrys = new TH1F("ElecCalibvsCrys", "Sum electronics calib const vs crystal ID;crystal ID;calibration constant",
87 registerObject<TH1F>("ElecCalibvsCrys", ElecCalibvsCrys);
88
89 auto InitialCalibvsCrys = new TH1F("InitialCalibvsCrys",
90 "Sum initial muon pair calib const vs crystal ID;crystal ID;calibration constant", ECLElementNumbers::c_NCrystals, 0,
92 registerObject<TH1F>("InitialCalibvsCrys", InitialCalibvsCrys);
93
94 auto CalibEntriesvsCrys = new TH1F("CalibEntriesvsCrys", "Entries in calib vs crys histograms;crystal ID;Entries per crystal",
97 registerObject<TH1F>("CalibEntriesvsCrys", CalibEntriesvsCrys);
98
100 auto RawDigitAmpvsCrys = new TH2F("RawDigitAmpvsCrys", "Digit Amplitude vs crystal ID;crystal ID;Amplitude",
102 25000);
103 registerObject<TH2F>("RawDigitAmpvsCrys", RawDigitAmpvsCrys);
104
105 auto RawDigitTimevsCrys = new TH2F("RawDigitTimevsCrys", "Digit Time vs crystal ID;crystal ID;Time", ECLElementNumbers::c_NCrystals,
106 0, ECLElementNumbers::c_NCrystals, 200, -2000,
107 2000);
108 registerObject<TH2F>("RawDigitTimevsCrys", RawDigitTimevsCrys);
109
110 //..Diagnose possible cable swaps
111 auto hitCrysVsExtrapolatedCrys = new TH2F("hitCrysVsExtrapolatedCrys",
112 "Crystals with high energy vs extrapolated crystals with no energy;extrapolated crystalID;High crystalID",
115 registerObject<TH2F>("hitCrysVsExtrapolatedCrys", hitCrysVsExtrapolatedCrys);
116
117
118 //------------------------------------------------------------------------
120 myNeighbours4 = new ECLNeighbours("NC", 1);
121 myNeighbours8 = new ECLNeighbours("N", 1);
122
123
124 //------------------------------------------------------------------------
126 B2INFO("Input parameters to eclMuMuECollector:");
127 B2INFO("minPairMass: " << m_minPairMass);
128 B2INFO("minTrackLength: " << m_minTrackLength);
129 B2INFO("MaxNeighbourE: " << m_MaxNeighbourE);
130 B2INFO("thetaLabMinDeg: " << m_thetaLabMinDeg);
131 B2INFO("thetaLabMaxDeg: " << m_thetaLabMaxDeg);
132 if (m_thetaLabMinDeg < 1.) {
133 cotThetaLabMax = 9999.;
134 } else if (m_thetaLabMinDeg > 179.) {
135 cotThetaLabMax = -9999.;
136 } else {
137 double thetaLabMin = m_thetaLabMinDeg * TMath::DegToRad();
138 cotThetaLabMax = 1. / tan(thetaLabMin);
139 }
140 if (m_thetaLabMaxDeg < 1.) {
141 cotThetaLabMin = 9999.;
142 } else if (m_thetaLabMaxDeg > 179.) {
143 cotThetaLabMin = -9999.;
144 } else {
145 double thetaLabMax = m_thetaLabMaxDeg * TMath::DegToRad();
146 cotThetaLabMin = 1. / tan(thetaLabMax);
147 }
148 B2INFO("cotThetaLabMin: " << cotThetaLabMin);
149 B2INFO("cotThetaLabMax: " << cotThetaLabMax);
150 B2INFO("measureTrueEnergy: " << m_measureTrueEnergy);
151 B2INFO("requireL1: " << m_requireL1);
152
155
158 if (m_ECLExpMuMuE.hasChanged()) {ExpMuMuE = m_ECLExpMuMuE->getCalibVector();}
159 if (m_ElectronicsCalib.hasChanged()) {ElectronicsCalib = m_ElectronicsCalib->getCalibVector();}
160 if (m_MuMuECalib.hasChanged()) {MuMuECalib = m_MuMuECalib->getCalibVector();}
161 if (m_CrystalEnergy.hasChanged()) {CrystalEnergy = m_CrystalEnergy->getCalibVector();}
162
164 for (int ic = 1; ic < 9000; ic += 1000) {
165 B2INFO("DB constants for cellID=" << ic << ": ExpMuMuE = " << ExpMuMuE[ic - 1] << " ElectronicsCalib = " << ElectronicsCalib[ic - 1]
166 << " MuMuECalib = " << MuMuECalib[ic - 1]);
167 }
168
170 for (int crysID = 0; crysID < ECLElementNumbers::c_NCrystals; crysID++) {
171 if (ElectronicsCalib[crysID] <= 0) {B2FATAL("eclMuMuECollector: ElectronicsCalib = " << ElectronicsCalib[crysID] << " for crysID = " << crysID);}
172 if (ExpMuMuE[crysID] == 0) {B2FATAL("eclMuMuECollector: ExpMuMuE = 0 for crysID = " << crysID);}
173 if (MuMuECalib[crysID] == 0) {B2FATAL("eclMuMuECollector: MuMuECalib = 0 for crysID = " << crysID);}
174 }
175
178 m_trackArray.isRequired();
179 m_eclDigitArray.isRequired();
180 if (m_measureTrueEnergy) {m_eclClusterArray.isRequired();}
181
182}
183
184
188{
189
191 if (iEvent == 0) {
192 for (int crysID = 0; crysID < ECLElementNumbers::c_NCrystals; crysID++) {
193 getObjectPtr<TH1F>("ExpEvsCrys")->Fill(crysID + 0.001, ExpMuMuE[crysID]);
194 getObjectPtr<TH1F>("ElecCalibvsCrys")->Fill(crysID + 0.001, ElectronicsCalib[crysID]);
195 getObjectPtr<TH1F>("InitialCalibvsCrys")->Fill(crysID + 0.001, MuMuECalib[crysID]);
196 getObjectPtr<TH1F>("CalibEntriesvsCrys")->Fill(crysID + 0.001);
197 }
198 }
199
200 if (iEvent % 10000 == 0) {B2INFO("eclMuMuECollector: iEvent = " << iEvent);}
201 iEvent++;
202
205 bool newConst = false;
206 if (m_ECLExpMuMuE.hasChanged()) {
207 newConst = true;
208 B2INFO("ECLExpMuMuE has changed, exp = " << m_evtMetaData->getExperiment() << " run = " << m_evtMetaData->getRun());
209 ExpMuMuE = m_ECLExpMuMuE->getCalibVector();
210 }
211 if (m_ElectronicsCalib.hasChanged()) {
212 newConst = true;
213 B2INFO("ECLCrystalElectronics has changed, exp = " << m_evtMetaData->getExperiment() << " run = " << m_evtMetaData->getRun());
214 ElectronicsCalib = m_ElectronicsCalib->getCalibVector();
215 }
216 if (m_MuMuECalib.hasChanged()) {
217 newConst = true;
218 B2INFO("ECLCrystalEnergyMuMu has changed, exp = " << m_evtMetaData->getExperiment() << " run = " << m_evtMetaData->getRun());
219 MuMuECalib = m_MuMuECalib->getCalibVector();
220 }
221 if (m_CrystalEnergy.hasChanged()) {
222 newConst = true;
223 B2INFO("ECLCrystalEnergy has changed, exp = " << m_evtMetaData->getExperiment() << " run = " << m_evtMetaData->getRun());
224 CrystalEnergy = m_CrystalEnergy->getCalibVector();
225 }
226
227 if (newConst) {
228 for (int ic = 1; ic < 9000; ic += 1000) {
229 B2INFO("DB constants for cellID=" << ic << ": ExpMuMuE = " << ExpMuMuE[ic - 1] << " ElectronicsCalib = " <<
230 ElectronicsCalib[ic - 1]
231 << " MuMuECalib = " << MuMuECalib[ic - 1]);
232 }
233
235 for (int crysID = 0; crysID < ECLElementNumbers::c_NCrystals; crysID++) {
236 if (ElectronicsCalib[crysID] <= 0) {B2FATAL("eclMuMuECollector: ElectronicsCalib = " << ElectronicsCalib[crysID] << " for crysID = " << crysID);}
237 if (ExpMuMuE[crysID] == 0) {B2FATAL("eclMuMuECollector: ExpMuMuE = 0 for crysID = " << crysID);}
238 if (MuMuECalib[crysID] == 0) {B2FATAL("eclMuMuECollector: MuMuECalib = 0 for crysID = " << crysID);}
239 }
240 }
241
242
243
246 if (m_requireL1) {
247 unsigned int L1TriggerResults = m_TRGResults->getTRGSummary(0);
248 if (L1TriggerResults == 0) {return;}
249 }
250
251 //------------------------------------------------------------------------
253 int nTrack = m_trackArray.getEntries();
254 if (nTrack < 2) {return;}
255
257 double maxpt[2] = {0., 0.};
258 int iTrack[2] = { -1, -1};
259 for (int it = 0; it < nTrack; it++) {
260 const TrackFitResult* temptrackFit = m_trackArray[it]->getTrackFitResult(Const::ChargedStable(211));
261 if (not temptrackFit) {continue;}
262 int imu = 0;
263 if (temptrackFit->getChargeSign() == 1) {imu = 1; }
264
265 double temppt = temptrackFit->getTransverseMomentum();
266 double cotThetaLab = temptrackFit->getCotTheta();
267 if (temppt > maxpt[imu] && cotThetaLab > cotThetaLabMin && cotThetaLab < cotThetaLabMax) {
268 maxpt[imu] = temppt;
269 iTrack[imu] = it;
270 }
271 }
272
274 if (iTrack[0] == -1 || iTrack[1] == -1) { return; }
275
277 ROOT::Math::PxPyPzEVector mu0 = m_trackArray[iTrack[0]]->getTrackFitResult(Const::ChargedStable(211))->get4Momentum();
278 ROOT::Math::PxPyPzEVector mu1 = m_trackArray[iTrack[1]]->getTrackFitResult(Const::ChargedStable(211))->get4Momentum();
279 if ((mu0 + mu1).M() < m_minPairMass) { return; }
280
281 //------------------------------------------------------------------------
283 int extCrysID[2] = { -1, -1};
284 Const::EDetector eclID = Const::EDetector::ECL;
285 for (int imu = 0; imu < 2; imu++) {
286 ROOT::Math::XYZVector temppos[2] = {};
287 int IDEnter = -99;
288 for (auto& extHit : m_trackArray[iTrack[imu]]->getRelationsTo<ExtHit>()) {
289 int pdgCode = extHit.getPdgCode();
290 Const::EDetector detectorID = extHit.getDetectorID(); // subsystem ID
291 int temp0 = extHit.getCopyID(); // ID within that subsystem; for ecl it is crystal ID
292
294 if (detectorID == eclID && TMath::Abs(pdgCode) == Const::muon.getPDGCode() && extHit.getStatus() == EXT_ENTER) {
295 IDEnter = temp0;
296 temppos[0] = extHit.getPosition();
297 }
298
300 if (detectorID == eclID && TMath::Abs(pdgCode) == Const::muon.getPDGCode() && extHit.getStatus() == EXT_EXIT && temp0 == IDEnter) {
301 temppos[1] = extHit.getPosition();
302
304 double trackLength = (temppos[1] - temppos[0]).R();
305 if (trackLength > m_minTrackLength) {extCrysID[imu] = temp0;}
306 break;
307 }
308 }
309 }
310
312 if (extCrysID[0] == -1 && extCrysID[1] == -1) { return; }
313
314 //------------------------------------------------------------------------
316 std::fill(EperCrys.begin(), EperCrys.end(), 0); // clear array
317
318 //..Record crystals with high energies to diagnose cable swaps
319 const double highEnergyThresh = 0.18; // GeV
320 std::vector<int> highECrys; // crystalIDs of crystals with high energy
321
322 //..For data, use muon pair calibration; for expected energies, use ECLCrystalEnergy
323 for (auto& eclDigit : m_eclDigitArray) {
324 int crysID = eclDigit.getCellId() - 1;
325 getObjectPtr<TH2F>("RawDigitAmpvsCrys")->Fill(crysID + 0.001, eclDigit.getAmp());
326
328 float calib = abs(MuMuECalib[crysID]);
329 if (m_measureTrueEnergy) {calib = CrystalEnergy[crysID];}
330 EperCrys[crysID] = eclDigit.getAmp() * calib * ElectronicsCalib[crysID];
331 if (EperCrys[crysID] > highEnergyThresh) {highECrys.push_back(crysID);}
332 if (EperCrys[crysID] > 0.01) {
333 getObjectPtr<TH2F>("RawDigitTimevsCrys")->Fill(crysID + 0.001, eclDigit.getTimeFit());
334 }
335 }
336
337 //------------------------------------------------------------------------
338 //..For expected energies, get the max energies crystals from the cluster. This is
339 // safer than converting the ECLDigit, since it does not require that the ECLCrystalEnergy
340 // payload used now is the same as was used when the event was generated.
342 for (int ic = 0; ic < m_eclClusterArray.getEntries(); ic++) {
344 int crysID = m_eclClusterArray[ic]->getMaxECellId() - 1;
345 float undoCorrection = m_eclClusterArray[ic]->getEnergyRaw() / m_eclClusterArray[ic]->getEnergy(
347 EperCrys[crysID] = undoCorrection * m_eclClusterArray[ic]->getEnergyHighestCrystal();
348 }
349 }
350 }
351
352 //------------------------------------------------------------------------
355 for (int imu = 0; imu < 2; imu++) {
356 int crysID = extCrysID[imu];
357 int cellID = crysID + 1;
358 if (crysID > -1) {
359
360 getObjectPtr<TH1F>("TrkPerCrysID")->Fill(crysID + 0.001);
361
362 bool noNeighbourSignal = true;
363 bool highNeighourSignal = false;
364 if (cellID >= firstcellIDN4 && crysID <= lastcellIDN4) {
365 for (const auto& tempCellID : myNeighbours4->getNeighbours(cellID)) {
366 int tempCrysID = tempCellID - 1;
367 if (tempCellID != cellID && EperCrys[tempCrysID] > m_MaxNeighbourE) {
368 noNeighbourSignal = false;
369 if (EperCrys[tempCrysID] > highEnergyThresh) {highNeighourSignal = true;}
370 }
371 }
372 } else {
373 for (const auto& tempCellID : myNeighbours8->getNeighbours(cellID)) {
374 int tempCrysID = tempCellID - 1;
375 if (tempCellID != cellID && EperCrys[tempCrysID] > m_MaxNeighbourE) {
376 noNeighbourSignal = false;
377 if (EperCrys[tempCrysID] > highEnergyThresh) {highNeighourSignal = true;}
378 }
379 }
380 }
381
383 if (noNeighbourSignal) {
384
386 getObjectPtr<TH2F>("EnVsCrysID")->Fill(crysID + 0.001, EperCrys[crysID] / abs(ExpMuMuE[crysID]));
387 getObjectPtr<TH1F>("ExpEvsCrys")->Fill(crysID + 0.001, ExpMuMuE[crysID]);
388 getObjectPtr<TH1F>("ElecCalibvsCrys")->Fill(crysID + 0.001, ElectronicsCalib[crysID]);
389 getObjectPtr<TH1F>("InitialCalibvsCrys")->Fill(crysID + 0.001, MuMuECalib[crysID]);
390 getObjectPtr<TH1F>("CalibEntriesvsCrys")->Fill(crysID + 0.001);
391 }
392
393 //..Possible cable swap
394 if (highNeighourSignal or (noNeighbourSignal and EperCrys[crysID] < m_MaxNeighbourE)) {
395 for (auto& id : highECrys) {
396 getObjectPtr<TH2F>("hitCrysVsExtrapolatedCrys")->Fill(crysID + 0.0001, id + 0.0001);
397 }
398 }
399 }
400 }
401}
double R
typedef autogenerated by FFTW
Calibration collector module base class.
Provides a type-safe way to pass members of the chargedStableSet set.
Definition: Const.h:589
static const ChargedStable muon
muon particle
Definition: Const.h:660
EDetector
Enum for identifying the detector components (detector and subdetector).
Definition: Const.h:42
@ c_nPhotons
CR is split into n photons (N1)
Class to get the neighbours for a given cell id.
Definition: ECLNeighbours.h:25
const std::vector< short int > & getNeighbours(short int cid) const
Return the neighbours for a given cell ID.
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
Values of the result of a track fit with a given particle hypothesis.
short getChargeSign() const
Return track charge (1 or -1).
double getCotTheta() const
Getter for tanLambda with CDF naming convention.
double getTransverseMomentum() const
Getter for the absolute value of the transverse momentum at the perigee.
StoreArray< ECLDigit > m_eclDigitArray
Required input array of eclDigits.
double m_MaxNeighbourE
maximum signal allowed in a neighbouring crystal (0.010 GeV)
bool m_requireL1
require events to satisfy a level 1 trigger (true)
DBObjPtr< ECLCrystalCalib > m_ECLExpMuMuE
Expected energies from database.
double m_minTrackLength
minimum extrapolated track length in the crystal (30 cm)
ECL::ECLNeighbours * myNeighbours8
class to return 8 nearest neighbours to crystal
StoreArray< ECLCluster > m_eclClusterArray
Required input array of ECLClusters.
StoreObjPtr< TRGSummary > m_TRGResults
DataStore TRGSummary.
std::vector< float > ExpMuMuE
vector obtained from DB object
std::vector< float > EperCrys
ECL digit energy for each crystal.
double cotThetaLabMin
Some other useful quantities.
void collect() override
Select events and crystals and accumulate histograms.
double m_minPairMass
Parameters to control the job.
StoreObjPtr< EventMetaData > m_evtMetaData
DataStore EventMetaData.
std::vector< float > ElectronicsCalib
vector obtained from DB object
int firstcellIDN4
Neighbours of each ECL crystal.
DBObjPtr< ECLCrystalCalib > m_CrystalEnergy
Existing single single calibration from DB is used to find expected E.
int lastcellIDN4
last cellID where we only need 4 neighbours
void prepare() override
Define histograms and read payloads from DB.
ECL::ECLNeighbours * myNeighbours4
class to return 4 nearest neighbours to crystal
double m_thetaLabMaxDeg
maximum muon theta in lab (150 degrees)
double m_thetaLabMinDeg
minimum muon theta in lab (17 degrees)
std::vector< float > CrystalEnergy
vector obtained from DB object
std::vector< float > MuMuECalib
vector obtained from DB object
DBObjPtr< ECLCrystalCalib > m_MuMuECalib
Existing single muon pair calibration from DB; will be updated by CAF.
DBObjPtr< ECLCrystalCalib > m_ElectronicsCalib
Electronics calibration from database.
eclMuMuECollectorModule()
Constructor: Sets the description, the properties and the parameters of the module.
bool m_measureTrueEnergy
use eclCalDigit to determine MC deposited energy (false)
double cotThetaLabMax
m_thetaLabMaxDeg converted to cotangent
StoreArray< Track > m_trackArray
Required arrays.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
const int c_NCrystals
Number of crystals.
Abstract base class for different kinds of events.
STL namespace.