Belle II Software release-09-00-07
FilterCalculator Class Reference

Implementation of a calculator used in the SoftwareTriggerModule to fill a SoftwareTriggerObject for doing HLT cuts. More...

#include <FilterCalculator.h>

Inheritance diagram for FilterCalculator:
Collaboration diagram for FilterCalculator:

Public Member Functions

 FilterCalculator ()
 Set the default names for the store object particle lists.
 
void requireStoreArrays () override
 Require the particle list. We do not need more here.
 
void doCalculation (SoftwareTriggerObject &calculationResult) override
 Actually write out the variables into the map.
 
void writeDebugOutput (const std::unique_ptr< TTree > &debugOutputTTree)
 Function to write out debug output into the given TTree.
 
void addDebugOutput (const StoreObjPtr< SoftwareTriggerVariables > &storeObject, const std::string &prefix)
 Function to write out debug output into the given StoreObject.
 
const SoftwareTriggerObject & fillInCalculations ()
 Main function of this class: calculate the needed variables using the overwritten doCalculation function and write out the values into the results object (with their names).
 

Private Attributes

StoreArray< Trackm_tracks
 Store Array of the tracks to be used.
 
StoreArray< ECLClusterm_eclClusters
 Store Array of the ecl clusters to be used.
 
StoreObjPtr< TRGSummarym_l1Trigger
 Store Object with the trigger result.
 
StoreArray< CDCTriggerUnpacker::NNBitStreamm_bitsNN
 Store Object with the trigger NN bits.
 
double m_looseTrkZ0 = 10 * Unit::cm
 which Z0 defines a loose track
 
double m_tightTrkZ0 = 2 * Unit::cm
 which Z0 defines a tight track
 
double m_E2min = 0.2
 which CMS energy defines nElow
 
double m_E0min = 0.3
 which CMS energy defines nEmedium
 
double m_Ehigh = 2
 which CMS energy defines nEhigh
 
double m_EminLab = 0.18
 which lab energy defines nE180Lab
 
double m_EminLab4Cluster = 0.3
 which lab energy defines nE300Lab
 
double m_EminLab3Cluster = 0.5
 which lab energy defines nE500Lab
 
double m_EsinglePhoton = 1
 which CMS energy defines nEsingleClust
 
double m_reducedEsinglePhoton = 0.5
 which CMS energy defines nReducedEsingle clusters
 
double m_cosmicMinPt = 0.5 * Unit::GeV
 which LAB pt defines a cosmic
 
double m_cosmicMaxClusterEnergy = 1.0 * Unit::GeV
 which LAB cluster energy vetoes a cosmic candidate
 
double m_goodMagneticRegionZ0 = 57.
 maximum z0 for well understood magnetic field (cm)
 
double m_goodMagneticRegionD0 = 26.5
 minimum d0 for well understood magnetic field, if z0 is large (cm)
 
DBObjPtr< HLTPrefilterParametersm_hltPrefilterParameters
 Objects relevant to HLTPrefilter monitoring HLTprefilterParameters Database OjbPtr.
 
HLTPrefilter::TimingCutState m_timingPrefilter
 Helper instance for timing based prefilter.
 
HLTPrefilter::CDCECLCutState m_cdceclPrefilter
 Helper instance for CDC-ECL occupancy based prefilter.
 
SoftwareTriggerObject m_calculationResult
 Internal storage of the result of the calculation.
 
bool m_debugPrepared = false
 Flag to not add the branches twice to the TTree.
 

Detailed Description

Implementation of a calculator used in the SoftwareTriggerModule to fill a SoftwareTriggerObject for doing HLT cuts.

This calculator exports variables needed for the trigger HLT part of the path ( = filtering out events)

This class implements the two main functions requireStoreArrays and doCalculation of the SoftwareTriggerCalculation class.

Definition at line 34 of file FilterCalculator.h.

Constructor & Destructor Documentation

◆ FilterCalculator()

Set the default names for the store object particle lists.

Definition at line 72 of file FilterCalculator.cc.

72 : m_bitsNN("CDCTriggerNNBits")
73{
74
75}
StoreArray< CDCTriggerUnpacker::NNBitStream > m_bitsNN
Store Object with the trigger NN bits.

Member Function Documentation

◆ addDebugOutput()

void addDebugOutput ( const StoreObjPtr< SoftwareTriggerVariables > &  storeObject,
const std::string &  prefix 
)
inherited

Function to write out debug output into the given StoreObject.

Needs an already prefilled calculationResult for this (probably using the fillInCalculations function). All added variables are prefixed with the given prefix string.

Definition at line 34 of file SoftwareTriggerCalculation.cc.

35 {
36 for (auto& identifierWithValue : m_calculationResult) {
37 const std::string& identifier = identifierWithValue.first;
38 const double value = identifierWithValue.second;
39
40 storeObject->append(prefix + "_" + identifier, value);
41 }
42 }
SoftwareTriggerObject m_calculationResult
Internal storage of the result of the calculation.

◆ doCalculation()

void doCalculation ( SoftwareTriggerObject &  calculationResult)
overridevirtual

Actually write out the variables into the map.

< number of loose tracks

< number of tight tracks

< Bhabha, 2 tracks with accompanying ECL info

< number of clusters with E*>m_Emedium (higher threshold)

< number of clusters with E*>m_Elow (lower threshold)

< number of clusters with E*>m_Ehigh (2 GeV)

< number of clusters with Elab > m_EminLab outside of high background endcap region

< number of clusters with Elab > m_EminLab4Cluster outside of high background endcap region

< number of clusters with Elab > m_EminLab3Cluster outside of high background endcap region

< number of clusters with Ecms > m_Ehigh outside of high background endcap region

< number of clusters with E*>4 GeV

< neutral clusters with Elab>250 MeV (anywhere)

< Neutral, zmva>0.5, in [17,150], max 2 energy clusters

< dphi* between 2 max E clusters

< net charge of loose tracks

< maximum p* of loose tracks (GeV/c)

< maximum pLab of loose tracks (GeV/c)

< Bhabha, 2 tracks, at least 1 of which has ECL info

< number of clusters with E*>m_Ehigh, low angles

< clusters with E*> m_EsinglePhoton (1 GeV)

< neutral clusters with E*> 1 GeV in [45,115]

< neutral clusters with E*> 1 GeV in [32,130]

< neutral clusters with E*> 1 GeV, not barrel or low

< charged clusters with E*> 1 GeV in [45,115]

< charged clusters with E*> 1 GeV in [32,130]

< charged clusters with E*> 0.5 GeV in [44,98]

< clusters with E>m_Emedium and |t|/dt99 < 10

< charged clusters with E*>2 GeV

< neutral clusters with E*>0.45 GeV in [17,150]

< neutral clusters with E*>0.45 GeV in [30,130]

< track + ECL info consistent with Bhabha

< Bhabha, 2 ECL clusters, one of which is charged

< Bhabha, 1 track has high energy cluster, other is electron

< Bhabha, 2 ECL clusters

< gg veto, 2 ECL clusters

< number of looseB tracks

< number of tightB tracks

< maximum pcms of looseB tracks (GeV/c)

< net charge of looseB tracks

< muon pair veto counting looseB tracks

< Bhabha veto hard Brem one leg

< single tag pi0 or eta

< single tag higher mass

< radiative Bhabha, recoil p in detector

< number of "C" tracks

< maximum pcms of "C" tracks GeV/c

< pCms of maximum p negatively charged track GeV/c

< clusterE lab of max p negatively charged track GeV

< pCms of maximum p positively charged track GeV/c

< clusterE lab of max p positively charged track GeV

< delta phi cms of max p positive and negative tracks deg

< Events in the injection strip

< Events with high CDC-ECL occupancy

Implements SoftwareTriggerCalculation.

Definition at line 85 of file FilterCalculator.cc.

86{
87 calculationResult["nTrkLoose"] = 0;
88 calculationResult["nTrkTight"] = 0;
89 calculationResult["ee2leg"] = 0;
90 calculationResult["nEmedium"] = 0;
91 calculationResult["nElow"] = 0;
92 calculationResult["nEhigh"] = 0;
93 calculationResult["nE180Lab"] = 0;
94 calculationResult["nE300Lab"] = 0;
95 calculationResult["nE500Lab"] = 0;
96 calculationResult["nE2000CMS"] = 0;
97 calculationResult["nE4000CMS"] = 0;
98 calculationResult["nE250Lab"] = 0;
99 calculationResult["nMaxEPhotonAcc"] = 0;
100 calculationResult["dphiCmsClust"] = NAN;
102 calculationResult["netChargeLoose"] = 0;
103 calculationResult["maximumPCMS"] = NAN;
104 calculationResult["maximumPLab"] = NAN;
105 calculationResult["eexx"] = 0;
106 calculationResult["ee1leg1trk"] = 0;
107 calculationResult["nEhighLowAng"] = 0;
108 calculationResult["nEsingleClust"] = 0;
109 calculationResult["nEsinglePhotonBarrel"] = 0;
110 calculationResult["nEsinglePhotonExtendedBarrel"] = 0;
111 calculationResult["nEsinglePhotonEndcap"] = 0;
112 calculationResult["nEsingleElectronBarrel"] = 0;
113 calculationResult["nEsingleElectronExtendedBarrel"] = 0;
114 calculationResult["nReducedEsinglePhotonReducedBarrel"] = 0;
115 calculationResult["nVetoClust"] = 0;
116 calculationResult["chrgClust2GeV"] = 0;
117 calculationResult["neutClust045GeVAcc"] = 0;
118 calculationResult["neutClust045GeVBarrel"] = 0;
119 calculationResult["singleTagLowMass"] = 0;
120 calculationResult["singleTagHighMass"] = 0;
121 calculationResult["n2GeVNeutBarrel"] = 0;
122 calculationResult["n2GeVNeutEndcap"] = 0;
123 calculationResult["n2GeVChrg"] = 0;
124 calculationResult["n2GeVPhotonBarrel"] = 0;
125 calculationResult["n2GeVPhotonEndcap"] = 0;
126 calculationResult["ee1leg"] = 0;
127 calculationResult["ee1leg1clst"] = 0;
128 calculationResult["ee1leg1e"] = 0;
129 calculationResult["ee2clst"] = 0;
130 calculationResult["gg2clst"] = 0;
131 calculationResult["eeee"] = 0;
132 calculationResult["eemm"] = 0;
133 calculationResult["eexxSelect"] = 0;
134 calculationResult["radBhabha"] = 0;
135 calculationResult["eeBrem"] = 0;
136 calculationResult["isrRadBhabha"] = 0;
137 calculationResult["muonPairECL"] = 0;
138 calculationResult["ggHighPt"] = 0;
139 calculationResult["selectee1leg1trk"] = 0;
140 calculationResult["selectee1leg1clst"] = 0;
141 calculationResult["selectee"] = 0;
142 calculationResult["ggBarrelVL"] = 0;
143 calculationResult["ggBarrelLoose"] = 0;
144 calculationResult["ggBarrelTight"] = 0;
145 calculationResult["ggEndcapVL"] = 0;
146 calculationResult["ggEndcapLoose"] = 0;
147 calculationResult["ggEndcapTight"] = 0;
148 calculationResult["muonPairV"] = 0;
149 calculationResult["selectmumu"] = 0;
150 calculationResult["singleMuon"] = 0;
151 calculationResult["cosmic"] = 0;
152 calculationResult["displacedVertex"] = 0;
153 calculationResult["eeFlat0"] = 0;
154 calculationResult["eeFlat1"] = 0;
155 calculationResult["eeFlat2"] = 0;
156 calculationResult["eeFlat3"] = 0;
157 calculationResult["eeFlat4"] = 0;
158 calculationResult["eeFlat5"] = 0;
159 calculationResult["eeFlat6"] = 0;
160 calculationResult["eeFlat7"] = 0;
161 calculationResult["eeFlat8"] = 0;
162 calculationResult["eeOneClust"] = 0;
163
164 //..New track definitions
165 calculationResult["nTrkLooseB"] = 0;
166 calculationResult["nTrkTightB"] = 0;
167 calculationResult["maximumPCMSB"] = NAN;
168 calculationResult["netChargeLooseB"] = 0;
169 calculationResult["muonPairVB"] = 0;
170 calculationResult["eeBremB"] = 0;
171 calculationResult["singleTagLowMassB"] = 0;
172 calculationResult["singleTagHighMassB"] = 0;
173 calculationResult["radBhabhaB"] = 0;
175 //..Tracks without a requirement on nCDCHits
176 calculationResult["nTrackC"] = 0;
177 calculationResult["maximumPCMSC"] = NAN;
178 calculationResult["pCmsNegC"] = NAN;
179 calculationResult["clusterENegC"] = NAN;
180 calculationResult["pCmsPosC"] = NAN;
181 calculationResult["clusterEPosC"] = NAN;
182 calculationResult["dPhiCmsC"] = NAN;
184 //..Filters lines for HLTPrefilter decision
185 calculationResult["Prefilter_InjectionStrip"] = 0;
186 calculationResult["Prefilter_CDCECLthreshold"] = 0;
188 // Passed on L1 information
189 if (m_l1Trigger.isValid()) {
190 calculationResult["l1_trigger_random"] = m_l1Trigger->getTimType() == TRGSummary::ETimingType::TTYP_RAND;
191 calculationResult["l1_trigger_delayed_bhabha"] = m_l1Trigger->getTimType() == TRGSummary::ETimingType::TTYP_DPHY;
192 calculationResult["l1_trigger_poisson"] = m_l1Trigger->getTimType() == TRGSummary::ETimingType::TTYP_POIS;
193 bool bha3d;
194 try {
195 bha3d = m_l1Trigger->testPsnm("bha3d");
196 } catch (const std::exception&) {
197 bha3d = false;
198 }
199 bool bhapurPsnm;
200 try {
201 bhapurPsnm = m_l1Trigger->testPsnm("bhapur");
202 } catch (const std::exception&) {
203 bhapurPsnm = false;
204 }
205 bool bhapurFtdl;
206 try {
207 bhapurFtdl = m_l1Trigger->testFtdl("bhapur");
208 } catch (const std::exception&) {
209 bhapurFtdl = false;
210 }
211 bool lml1;
212 try {
213 lml1 = m_l1Trigger->testPsnm("lml1");
214 } catch (const std::exception&) {
215 lml1 = false;
216 }
217 bool l1_bit_f;
218 try {
219 l1_bit_f = m_l1Trigger->testPsnm("fpre");
220 } catch (const std::exception&) {
221 l1_bit_f = false;
222 }
223 calculationResult["bha3d"] = bha3d;
224 calculationResult["bhapur"] = bhapurPsnm;
225 calculationResult["bhapur_lml1"] = lml1 and bhapurFtdl;
226 calculationResult["l1_bit_f"] = l1_bit_f;
227 } else {
228 calculationResult["l1_trigger_random"] = 1; // save every event if no L1 trigger info
229 calculationResult["l1_trigger_delayed_bhabha"] = 0;
230 calculationResult["l1_trigger_poisson"] = 0;
231 calculationResult["bha3d"] = 0;
232 calculationResult["bhapur"] = 0;
233 calculationResult["bhapur_lml1"] = 0;
234 calculationResult["l1_bit_f"] = 0;
235 }
236
237 // Every 256th event has CDC NN trigger information
238 calculationResult["l1_trg_NN_info"] = 0;
239 if (m_bitsNN.isValid() and m_bitsNN.getEntries() > 0) {calculationResult["l1_trg_NN_info"] = 1;}
240
241 calculationResult["true"] = 1;
242 calculationResult["false"] = 0;
243
244 // Some utilities
245 // TODO: into constructor
246 ClusterUtils cUtil;
247 const ROOT::Math::XYZVector clustervertex = cUtil.GetIPPosition();
248 PCmsLabTransform boostrotate;
249 ROOT::Math::PxPyPzEVector p4ofCOM;
250 p4ofCOM.SetPxPyPzE(0, 0, 0, boostrotate.getCMSEnergy());
251
252 // Pointers to the two tracks with the maximum pt
253 std::map<short, std::optional<MaximumPtTrack>> maximumPtTracks = {
254 { -1, {}}, {1, {}}
255 };
256
257 // Pointer to the two tracks with maximum pt without a cut applied on z0 (used for cosmic trigger)
258 std::map<short, std::optional<MaximumPtTrack>> maximumPtTracksWithoutZCut = {
259 { -1, {}}, {1, {}}
260 };
261
262 //..Pointers to the two "C" tracks with maximum pCms (no requirement on nCDC hits)
263 std::map<short, std::optional<MaximumPtTrack>> maximumPCmsTracksC = {
264 { -1, {}}, {1, {}}
265 };
266
267 //------------------------------------------------------------
268 // --- Track variables -- //
269 for (const Track& track : m_tracks) {
270 const TrackFitResult* trackFitResult = track.getTrackFitResultWithClosestMass(Const::pion);
271 if (not trackFitResult) {
272 // Huh? No track fit result for pion? Ok, lets skip this track
273 continue;
274 }
275
276 //..Track properties. Only use tracks with defined charge.
277 const short charge = trackFitResult->getChargeSign();
278 if (charge == 0) {continue;}
279 const double z0 = trackFitResult->getZ0();
280 const ROOT::Math::PxPyPzEVector& momentumLab = trackFitResult->get4Momentum();
281 const ROOT::Math::PxPyPzEVector momentumCMS = boostrotate.rotateLabToCms() * momentumLab;
282 const double pCMS = momentumCMS.P();
283 const double pLab = momentumLab.P();
284 const double trackTime = track.getTrackTime();
285 const double pT = trackFitResult->getTransverseMomentum();
286 const int nCDCHits = (int)(0.5 + trackFitResult->getHitPatternCDC().getNHits());
287 double clusterELab = 0.;
288 for (auto& cluster : track.getRelationsTo<ECLCluster>()) {
289 if (cluster.hasHypothesis(Belle2::ECLCluster::EHypothesisBit::c_nPhotons)) {
290 clusterELab += cluster.getEnergy(Belle2::ECLCluster::EHypothesisBit::c_nPhotons);
291 }
292 }
293 const double clusterECMS = clusterELab * pCMS / pLab;
294
295
296 //------------------------------------------------------------
297 //..New list without a requirement on number of CDC hits
298 bool goodTrackCTime = true;
299 if (std::abs(trackTime) > 15.) {goodTrackCTime = false;}
300 if (std::abs(z0) < 1. and goodTrackCTime and pCMS > 0.2) {
301 calculationResult["nTrackC"] += 1;
302 if (std::isnan(calculationResult["maximumPCMSC"]) or pCMS > calculationResult["maximumPCMSC"]) {
303 calculationResult["maximumPCMSC"] = pCMS;
304 }
305 }
306
307 //..Store maximum pCms positive and negative tracks
308 const auto& currentMaximumC = maximumPCmsTracksC.at(charge);
309 if (not currentMaximumC or pCMS > currentMaximumC->pCMS) {
310 MaximumPtTrack newMaximum;
311 newMaximum.pT = pT;
312 newMaximum.track = &track;
313 newMaximum.pCMS = pCMS;
314 newMaximum.pLab = pLab;
315 newMaximum.p4CMS = momentumCMS;
316 newMaximum.p4Lab = momentumLab;
317 newMaximum.clusterEnergySumLab = clusterELab;
318 newMaximum.clusterEnergySumCMS = clusterECMS;
319 maximumPCmsTracksC[charge] = newMaximum;
320 }
321
322 //------------------------------------------------------------
323 //..Now original loose and tight tracks, which require nCDC hits
324 // TODO: Temporary cut on number of CDC hits
325 if (nCDCHits == 0) {
326 continue;
327 }
328
329 // Count tight tracks
330 if (std::abs(z0) < m_tightTrkZ0) {
331 calculationResult["nTrkTight"] += 1;
332 }
333
334
335 //------------------------------------------------------------
336 // Find the maximum pt negative [0] and positive [1] tracks without z0 cut
337 const auto& currentMaximum = maximumPtTracksWithoutZCut.at(charge);
338 if (not currentMaximum or pT > currentMaximum->pT) {
339 MaximumPtTrack newMaximum;
340 newMaximum.pT = pT;
341 newMaximum.track = &track;
342 newMaximum.pCMS = pCMS;
343 newMaximum.pLab = pLab;
344 newMaximum.p4CMS = momentumCMS;
345 newMaximum.p4Lab = momentumLab;
346 newMaximum.clusterEnergySumLab = clusterELab;
347 newMaximum.clusterEnergySumCMS = clusterECMS;
348 maximumPtTracksWithoutZCut[charge] = newMaximum;
349 }
350
351
352 //------------------------------------------------------------
353 // Loose tracks
354 if (std::abs(z0) < m_looseTrkZ0) {
355 calculationResult["nTrkLoose"] += 1;
356 calculationResult["netChargeLoose"] += charge;
357
358 if (std::isnan(calculationResult["maximumPCMS"]) or pCMS > calculationResult["maximumPCMS"]) {
359 calculationResult["maximumPCMS"] = pCMS;
360 }
361
362 if (std::isnan(calculationResult["maximumPLab"]) or pLab > calculationResult["maximumPLab"]) {
363 calculationResult["maximumPLab"] = pLab;
364 }
365
366 // Find the maximum pt negative [0] and positive [1] tracks
367 const double pTLoose = trackFitResult->getTransverseMomentum();
368 const auto& currentMaximumLoose = maximumPtTracks.at(charge);
369 if (not currentMaximumLoose or pTLoose > currentMaximumLoose->pT) {
370 MaximumPtTrack newMaximum;
371 newMaximum.pT = pTLoose;
372 newMaximum.track = &track;
373 newMaximum.pCMS = pCMS;
374 newMaximum.pLab = momentumLab.P();
375 newMaximum.p4CMS = momentumCMS;
376 newMaximum.p4Lab = momentumLab;
377 newMaximum.clusterEnergySumLab = clusterELab;
378 newMaximum.clusterEnergySumCMS = clusterECMS;
379 maximumPtTracks[charge] = newMaximum;
380 }
381 }
382
383 //..New tighter track definitions.
384 // We can use pCMS and charge from above, because every looseB track is also a loose track
385 if (nCDCHits >= 5) {
386 if (std::abs(z0) < 1.) {calculationResult["nTrkTightB"] += 1;}
387 if (std::abs(z0) < 5.) {
388 calculationResult["nTrkLooseB"] += 1;
389 calculationResult["netChargeLooseB"] += charge;
390 if (std::isnan(calculationResult["maximumPCMSB"]) or pCMS > calculationResult["maximumPCMSB"]) {
391 calculationResult["maximumPCMSB"] = pCMS;
392
393 }
394 }
395 }
396
397 } // End loop over tracks
398
399
400 //------------------------------------------------------------
401 //..Store the "C" track calculations
402 for (short charge : { -1, 1}) {
403 auto& maximumPcmsTrackC = maximumPCmsTracksC.at(charge);
404 if (not maximumPcmsTrackC) {
405 continue;
406 }
407
408 //..Calculations for each track
409 if (charge == -1) {
410 calculationResult["pCmsNegC"] = maximumPcmsTrackC->pCMS;
411 calculationResult["clusterENegC"] = maximumPcmsTrackC->clusterEnergySumLab;
412 } else {
413 calculationResult["pCmsPosC"] = maximumPcmsTrackC->pCMS;
414 calculationResult["clusterEPosC"] = maximumPcmsTrackC->clusterEnergySumLab;
415 }
416 }
417
418 //..Calculations using both highest-pCms C tracks
419 if (maximumPCmsTracksC.at(-1) and maximumPCmsTracksC.at(1)) {
420 const MaximumPtTrack& negativeTrack = *maximumPCmsTracksC.at(-1);
421 const MaximumPtTrack& positiveTrack = *maximumPCmsTracksC.at(1);
422
423 calculationResult["dPhiCmsC"] = std::abs(ROOT::Math::VectorUtil::DeltaPhi(negativeTrack.p4CMS,
424 positiveTrack.p4CMS)) * TMath::RadToDeg();
425 }
426
427
428 //------------------------------------------------------------
429 // -- Cluster variables -- //
430 std::vector<SelectedECLCluster> selectedClusters;
431 for (const ECLCluster& cluster : m_eclClusters) {
432 // Use the photon hypothesis, and only look at clusters with times < 200 ns
433 const double time = cluster.getTime();
434 if (not cluster.hasHypothesis(Belle2::ECLCluster::EHypothesisBit::c_nPhotons) or
435 std::abs(time) > 200 or
436 cluster.getEnergy(Belle2::ECLCluster::EHypothesisBit::c_nPhotons) < 0.1) {
437 continue;
438 }
439
440 const double dt99 = cluster.getDeltaTime99();
441
442 // Store cluster information
443 SelectedECLCluster selectedCluster;
444 selectedCluster.p4Lab = cUtil.Get4MomentumFromCluster(&cluster, clustervertex,
446 selectedCluster.p4CMS = boostrotate.rotateLabToCms() * selectedCluster.p4Lab; // was clustp4COM
447 selectedCluster.cluster = &cluster;
448 selectedCluster.clusterTime = time / dt99; // was clustT
449 selectedCluster.energyLab = selectedCluster.p4Lab.E();
450 selectedCluster.energyCMS = selectedCluster.p4CMS.E(); // was first of ECOMPair
451 selectedCluster.isTrack = cluster.isTrack(); // was tempCharge
452
453 selectedClusters.push_back(selectedCluster);
454
455 if (selectedCluster.energyCMS > m_E2min) {
456 calculationResult["nElow"] += 1;
457 }
458 if (selectedCluster.energyCMS > m_E0min) {
459 calculationResult["nEmedium"] += 1;
460 }
461 if (selectedCluster.energyCMS > m_Ehigh) {
462 calculationResult["nEhigh"] += 1;
463 }
464
465 if (selectedCluster.energyCMS > m_E0min and std::abs(selectedCluster.clusterTime) < 10) {
466 calculationResult["nVetoClust"] += 1;
467 }
468
469
470 //..Single cluster trigger objects use charge, Zernike moment, and thetaLab
471 const double thetaLab = selectedCluster.p4Lab.Theta() * TMath::RadToDeg();
472 const double zmva = cluster.getZernikeMVA();
473 const bool photon = zmva > 0.5 and not selectedCluster.isTrack;
474 const bool electron = zmva > 0.5 and selectedCluster.isTrack;
475
476 //..For 1 track radiative Bhabha control sample
477 if (selectedCluster.energyCMS > 2. and selectedCluster.isTrack) {
478 calculationResult["chrgClust2GeV"] += 1;
479 }
480 if (selectedCluster.energyCMS > 0.45 and not selectedCluster.isTrack) {
481 const bool isInAcceptance = 17. < thetaLab and thetaLab < 150.;
482 if (isInAcceptance) {calculationResult["neutClust045GeVAcc"] += 1;}
483 const bool isInBarrel = 30. < thetaLab and thetaLab < 130.;
484 if (isInBarrel) {calculationResult["neutClust045GeVBarrel"] += 1;}
485 }
486
487
488 // improved 3 cluster (4 cluster) trigger
489 const bool notInHighBackgroundEndcapRegion = 18.5 < thetaLab and thetaLab < 139.3;
490 if (selectedCluster.energyLab > m_EminLab and notInHighBackgroundEndcapRegion) {
491 calculationResult["nE180Lab"] += 1;
492 }
493
494 if (selectedCluster.energyLab > m_EminLab4Cluster and notInHighBackgroundEndcapRegion) {
495 calculationResult["nE300Lab"] += 1;
496 }
497
498 if (selectedCluster.energyLab > m_EminLab3Cluster and notInHighBackgroundEndcapRegion) {
499 calculationResult["nE500Lab"] += 1;
500 }
501
502 if (selectedCluster.energyCMS > m_Ehigh and notInHighBackgroundEndcapRegion) {
503 calculationResult["nE2000CMS"] += 1;
504 }
505
506 //..For two-photon fusion ALP trigger
507 if (selectedCluster.energyLab > 0.25) {
508 calculationResult["nE250Lab"] += 1;
509 }
510 if (selectedCluster.energyCMS > 4.) {
511 calculationResult["nE4000CMS"] += 1;
512 }
513
514 //..Single cluster triggers
515 if (selectedCluster.energyCMS > m_EsinglePhoton) {
516 calculationResult["nEsingleClust"] += 1;
517
518 const bool barrelRegion = thetaLab > 45 and thetaLab < 115;
519 const bool extendedBarrelRegion = thetaLab > 30 and thetaLab < 130;
520 const bool endcapRegion = (thetaLab > 22 and thetaLab < 45) or (thetaLab > 115 and thetaLab < 145);
521
522 if (photon and barrelRegion) {
523 calculationResult["nEsinglePhotonBarrel"] += 1;
524 }
525
526 if (photon and extendedBarrelRegion) {
527 calculationResult["nEsinglePhotonExtendedBarrel"] += 1;
528 }
529
530 if (electron and barrelRegion) {
531 calculationResult["nEsingleElectronBarrel"] += 1;
532 }
533
534 if (electron and extendedBarrelRegion) {
535 calculationResult["nEsingleElectronExtendedBarrel"] += 1;
536 }
537
538 if (photon and endcapRegion) {
539 calculationResult["nEsinglePhotonEndcap"] += 1;
540 }
541 }
542
543 if (selectedCluster.energyCMS > m_reducedEsinglePhoton) {
544 const bool reducedBarrelRegion = thetaLab > 44 and thetaLab < 98;
545
546 if (photon and reducedBarrelRegion) {
547 calculationResult["nReducedEsinglePhotonReducedBarrel"] += 1;
548 }
549 }
550
551 if (selectedCluster.energyCMS > m_Ehigh) {
552 // TODO: different definition!
553 const bool barrelRegion = thetaLab > 32 and thetaLab < 130;
554 const bool endcapRegion = (thetaLab > 22 and thetaLab < 32) or (thetaLab > 130 and thetaLab < 145);
555 const bool lowAngleRegion = thetaLab < 22 or thetaLab > 145;
556
557 if (not selectedCluster.isTrack and barrelRegion) {
558 calculationResult["n2GeVNeutBarrel"] += 1;
559 }
560 if (not selectedCluster.isTrack and endcapRegion) {
561 calculationResult["n2GeVNeutEndcap"] += 1;
562 }
563 if (selectedCluster.isTrack and not lowAngleRegion) {
564 calculationResult["n2GeVChrg"] += 1;
565 }
566 if (lowAngleRegion) {
567 calculationResult["nEhighLowAng"] += 1;
568 }
569 if (photon and barrelRegion) {
570 calculationResult["n2GeVPhotonBarrel"] += 1;
571 }
572 if (photon and endcapRegion) {
573 calculationResult["n2GeVPhotonEndcap"] += 1;
574 }
575 }
576 } // end of loop over clusters
577
578 //..Order clusters by CMS energy
579 std::sort(selectedClusters.begin(), selectedClusters.end(), [](const auto & lhs, const auto & rhs) {
580 return lhs.energyCMS > rhs.energyCMS;
581 });
582
583 // -- Bhabha and two-photon lepton preparation -- //
584 for (short charge : { -1, 1}) {
585 auto& maximumPtTrack = maximumPtTracks.at(charge);
586 if (not maximumPtTrack) {
587 continue;
588 }
589
590 // Single leg Bhabha selections
591 if (maximumPtTrack->clusterEnergySumCMS > 4.5) {
592 calculationResult["ee1leg"] = 1;
593 }
594
595 // single muon
596 if (maximumPtTrack->pCMS > 3 and maximumPtTrack->clusterEnergySumLab > 0. and maximumPtTrack->clusterEnergySumLab < 1.) {
597 calculationResult["singleMuon"] = 1;
598 }
599 }
600
601 // Bhabha selections using two tracks
602 if (maximumPtTracks.at(-1) and maximumPtTracks.at(1)) {
603 const MaximumPtTrack& negativeTrack = *maximumPtTracks.at(-1);
604 const MaximumPtTrack& positiveTrack = *maximumPtTracks.at(1);
605
606 double dphi = std::abs(negativeTrack.p4CMS.Phi() - positiveTrack.p4CMS.Phi()) * TMath::RadToDeg();
607 if (dphi > 180) {
608 dphi = 360 - dphi;
609 }
610
611 const double negativeClusterSum = negativeTrack.clusterEnergySumCMS;
612 const double negativeClusterSumLab = negativeTrack.clusterEnergySumLab;
613 const double negativeP = negativeTrack.pCMS;
614 const double positiveClusterSum = positiveTrack.clusterEnergySumCMS;
615 const double positiveClusterSumLab = positiveTrack.clusterEnergySumLab;
616 const double positiveP = positiveTrack.pCMS;
617
618 // two leg Bhabha
619 const double thetaSum = (negativeTrack.p4CMS.Theta() + positiveTrack.p4CMS.Theta()) * TMath::RadToDeg();
620 const double dthetaSum = std::abs(thetaSum - 180);
621 const auto back2back = dphi > 175 and dthetaSum < 15;
622 if (back2back and negativeClusterSum > 3 and positiveClusterSum > 3 and
623 (negativeClusterSum > 4.5 or positiveClusterSum > 4.5)) {
624 calculationResult["ee2leg"] = 1;
625 }
626
627 // one leg one track Bhabha
628 if (back2back and ((negativeClusterSum > 4.5 and positiveP > 3) or (positiveClusterSum > 4.5 and negativeP > 3))) {
629 calculationResult["ee1leg1trk"] = 1;
630 }
631
632
633 // one leg plus one electron
634 if ((negativeClusterSum > 4.5 and positiveClusterSum > 0.8 * positiveP) or
635 (positiveClusterSum > 4.5 and negativeClusterSum > 0.8 * negativeP)) {
636 calculationResult["ee1leg1e"] = 1;
637 }
638
639 // eeee, eemm, mu mu, and radiative Bhabha selection
640 const ROOT::Math::PxPyPzEVector p4Miss = p4ofCOM - negativeTrack.p4CMS - positiveTrack.p4CMS;
641 const double pmissTheta = p4Miss.Theta() * TMath::RadToDeg();
642 const double pmissp = p4Miss.P();
643 const double relMissAngle0 = B2Vector3D(negativeTrack.p4CMS.Vect()).Angle(B2Vector3D(p4Miss.Vect())) * TMath::RadToDeg();
644 const double relMissAngle1 = B2Vector3D(positiveTrack.p4CMS.Vect()).Angle(B2Vector3D(p4Miss.Vect())) * TMath::RadToDeg();
645
646 const bool electronEP = positiveClusterSum > 0.8 * positiveP or negativeClusterSum > 0.8 * negativeP;
647 const bool notMuonPair = negativeClusterSumLab > 1 or positiveClusterSumLab > 1;
648 const double highp = std::max(negativeP, positiveP);
649 const double lowp = std::min(negativeP, positiveP);
650 const bool lowEdep = negativeClusterSumLab < 0.5 and positiveClusterSumLab < 0.5;
651
652 //..Select two photon fusion lepton pairs
653 if (calculationResult["maximumPCMS"] < 2 and dphi > 160 and (pmissTheta < 25. or pmissTheta > 155.)) {
654 calculationResult["eexxSelect"] = 1;
655 if (electronEP) {
656 calculationResult["eeee"] = 1;
657 } else {
658 calculationResult["eemm"] = 1;
659 }
660 }
661
662 //..Veto two photon fusion lepton pairs
663 if ((pmissTheta < 20. or pmissTheta > 160.) and
664 ((calculationResult["maximumPCMS"] < 1.2 and dphi > 150.) or
665 (calculationResult["maximumPCMS"] < 2. and 175. < dphi))) {
666 calculationResult["eexx"] = 1;
667 }
668
669 //..Radiative Bhabhas with missing momentum in acceptance
670 if (negativeP > 1. and pmissTheta > 10. and pmissTheta < 170. and positiveP > 1. and dphi < 170. and pmissp > 1. and electronEP) {
671 if (calculationResult["nTrkLoose"] == 2 and calculationResult["nTrkTight"] >= 1) {
672 calculationResult["radBhabha"] = 1;
673 }
674 if (calculationResult["nTrkLooseB"] == 2 and calculationResult["nTrkTightB"] >= 1) {
675 calculationResult["radBhabhaB"] = 1;
676 }
677 }
678
679 //..ISR radiative Bhabha with missing momentum down beam pipe
680 if (negativeP > 2. and positiveP > 2. and 2 == calculationResult["nTrkLoose"] and
681 calculationResult["nTrkTight"] >= 1 and dphi > 175. and
682 (pmissTheta < 5. or pmissTheta > 175.) and electronEP) {
683 calculationResult["isrRadBhabha"] = 1;
684 }
685
686 //..Veto Bhabha with hard brem
687 if (highp > 4.5 and notMuonPair and pmissp > 1. and (relMissAngle0 < 5. or relMissAngle1 < 5.)) {
688 if (calculationResult["nTrkLoose"] == 2) { calculationResult["eeBrem"] = 1;}
689 if (calculationResult["nTrkLooseB"] >= 1) { calculationResult["eeBremB"] = 1;}
690 }
691
692 //..Veto e+e- -> mu+mu-
693 if (highp > 4.5 and lowEdep and thetaSum > 175. and thetaSum < 185. and dphi > 175.) {
694 if (calculationResult["nTrkLoose"] == 2) {calculationResult["muonPairV"] = 1;}
695 if (calculationResult["nTrkLooseB"] == 2) {calculationResult["muonPairVB"] = 1;}
696 }
697
698 //..Select e+e- -> mu+mu-
699 if (highp > 3. and lowp > 2.5 and dphi > 165. and
700 ((negativeClusterSumLab > 0. and negativeClusterSumLab < 1.) or
701 (positiveClusterSumLab > 0. and positiveClusterSumLab < 1.))) {
702 calculationResult["selectmumu"] = 1;
703 }
704 }
705
706 //..Filters and vetoes using two maximum-energy clusters
707 if (selectedClusters.size() >= 2) {
708 const SelectedECLCluster& firstCluster = selectedClusters[0];
709 const SelectedECLCluster& secondCluster = selectedClusters[1];
710
711 double dphi = std::abs(firstCluster.p4CMS.Phi() - secondCluster.p4CMS.Phi()) * TMath::RadToDeg();
712 if (dphi > 180) {
713 dphi = 360 - dphi;
714 }
715 double thetaSum = (firstCluster.p4CMS.Theta() + secondCluster.p4CMS.Theta()) * TMath::RadToDeg();
716 double dthetaSum = std::abs(thetaSum - 180);
717
718 //..Quantities for two-photon fusion triggers
719 calculationResult["dphiCmsClust"] = dphi;
720 for (int ic = 0; ic < 2; ic++) {
721 const double thetaLab = selectedClusters[ic].p4Lab.Theta() * TMath::RadToDeg();
722 const bool isInAcceptance = 17. < thetaLab and thetaLab < 150.;
723 const ECLCluster* cluster = selectedClusters[ic].cluster;
724 const double zmva = cluster->getZernikeMVA();
725 const bool photon = zmva > 0.5 and not selectedClusters[ic].isTrack;
726 if (isInAcceptance and photon) {calculationResult["nMaxEPhotonAcc"] += 1;}
727 }
728
729 const double firstEnergy = firstCluster.p4CMS.E();
730 const double secondEnergy = secondCluster.p4CMS.E();
731
732 const bool highEnergetic = firstEnergy > 3 and secondEnergy > 3 and (firstEnergy > 4.5 or secondEnergy > 4.5);
733
734 if (dphi > 160 and dphi < 178 and dthetaSum < 15 and highEnergetic) {
735 calculationResult["ee2clst"] = 1;
736 }
737
738 if (dphi > 178 and dthetaSum < 15 and highEnergetic) {
739 calculationResult["gg2clst"] = 1;
740 }
741
742 if ((calculationResult["ee2clst"] == 1 or calculationResult["gg2clst"] == 1) and
743 calculationResult["ee1leg"] == 1) {
744 calculationResult["ee1leg1clst"] = 1;
745 }
746
747 const double Elab0 = firstCluster.p4Lab.E();
748 const double Elab1 = secondCluster.p4Lab.E();
749
750 // gg and mumu accept triggers using ECL
751 if (firstEnergy > 2 and secondEnergy > 2) {
752 const double thetaLab0 = firstCluster.p4Lab.Theta() * TMath::RadToDeg();
753 const double thetaLab1 = secondCluster.p4Lab.Theta() * TMath::RadToDeg();
754
755 const bool barrel0 = 32. < thetaLab0 and thetaLab0 < 130.;
756 const bool barrel1 = 32. < thetaLab1 and thetaLab1 < 130.;
757 const bool oneClustersAbove4 = firstEnergy > 4 or secondEnergy > 4;
758 const bool oneIsNeutral = not firstCluster.isTrack or not secondCluster.isTrack;
759 const bool bothAreNeutral = not firstCluster.isTrack and not secondCluster.isTrack;
760 const bool oneIsBarrel = barrel0 or barrel1;
761 const bool dphiCutExtraLoose = dphi > 175;
762 const bool dphiCutLoose = dphi > 177;
763 const bool dphiCutTight = dphi > 177.5;
764
765 if (dphiCutExtraLoose and oneIsNeutral and oneIsBarrel) {
766 calculationResult["ggBarrelVL"] = 1;
767 }
768 if (oneClustersAbove4 and dphiCutLoose and oneIsNeutral and oneIsBarrel) {
769 calculationResult["ggBarrelLoose"] = 1;
770 }
771 if (oneClustersAbove4 and dphiCutTight and bothAreNeutral and oneIsBarrel) {
772 calculationResult["ggBarrelTight"] = 1;
773 }
774 if (dphiCutExtraLoose and oneIsNeutral and not oneIsBarrel) {
775 calculationResult["ggEndcapVL"] = 1;
776 }
777 if (oneClustersAbove4 and dphiCutLoose and oneIsNeutral and not oneIsBarrel) {
778 calculationResult["ggEndcapLoose"] = 1;
779 }
780 if (oneClustersAbove4 and dphiCutTight and bothAreNeutral and not oneIsBarrel) {
781 calculationResult["ggEndcapTight"] = 1;
782 }
783 }
784
785 const double minEnergy = std::min(Elab0, Elab1);
786 const double maxEnergy = std::max(Elab0, Elab1);
787 if (dphi > 155 and thetaSum > 165 and thetaSum < 195 and minEnergy > 0.15 and minEnergy < 0.5 and
788 maxEnergy > 0.15 and maxEnergy < 0.5) {
789 calculationResult["muonPairECL"] = 1;
790 }
791
792 //..diPhoton line
793 const double thetaLab0 = firstCluster.p4Lab.Theta() * TMath::RadToDeg();
794 const double thetaLab1 = secondCluster.p4Lab.Theta() * TMath::RadToDeg();
795 const bool inHieRegion0 = thetaLab0 > 26. and thetaLab0 < 130.;
796 const bool inHieRegion1 = thetaLab1 > 26. and thetaLab1 < 130.;
797 const bool firstIsNeutral = not firstCluster.isTrack;
798 const bool secondIsNeutral = not secondCluster.isTrack;
799
800 if (secondEnergy > 0.3 and inHieRegion0 and inHieRegion1 and firstIsNeutral and secondIsNeutral) {
801 const ROOT::Math::PxPyPzEVector ggP4CMS = firstCluster.p4CMS + secondCluster.p4CMS;
802 if (ggP4CMS.pt() > 1.) {calculationResult["ggHighPt"] = 1;}
803 }
804
805 } // end of two-clusters lines
806
807 // Bhabha accept triggers.
808 // Use theta_lab of negative track if available; otherwise cluster.
809 double thetaFlatten = 0;
810
811 // One leg, one cluster.
812 for (short charge : { -1, 1}) {
813 const auto& maximumPtTrack = maximumPtTracks.at(charge);
814 if (not maximumPtTrack) {
815 continue;
816 }
817
818 if (maximumPtTrack->clusterEnergySumCMS > 1.5) {
819 double invMass = 0.;
820 double tempFlatten = 0.;
821 for (const SelectedECLCluster& cluster : selectedClusters) {
822 double tempInvMass = (maximumPtTrack->p4Lab + cluster.p4Lab).M();
823 if (tempInvMass > invMass) {
824 invMass = tempInvMass;
825 if (charge == 1) {
826 tempFlatten = cluster.p4Lab.Theta() * TMath::RadToDeg();
827 }
828 }
829 }
830 if (charge == -1) {
831 tempFlatten = maximumPtTrack->p4Lab.Theta() * TMath::RadToDeg();
832 }
833 if (invMass > 5.29) {
834 calculationResult["selectee1leg1clst"] = 1;
835 thetaFlatten = tempFlatten;
836 }
837 }
838 }
839
840 // Two legs
841 if (maximumPtTracks.at(-1) and maximumPtTracks.at(1)) {
842 const MaximumPtTrack& negativeTrack = *maximumPtTracks.at(-1);
843 const MaximumPtTrack& positiveTrack = *maximumPtTracks.at(1);
844 const double invMass = (negativeTrack.p4Lab + positiveTrack.p4Lab).M();
845 if (invMass > 5.29 and (negativeTrack.clusterEnergySumCMS > 1.5 or positiveTrack.clusterEnergySumCMS > 1.5)) {
846 calculationResult["selectee1leg1trk"] = 1;
847 }
848
849 thetaFlatten = negativeTrack.p4Lab.Theta() * TMath::RadToDeg();
850
851 // Two tracks but (exactly) 1 high energy cluster
852 const bool missNegClust = positiveTrack.clusterEnergySumCMS > 4.5 and negativeTrack.clusterEnergySumCMS < 1.;
853 const bool missPosClust = negativeTrack.clusterEnergySumCMS > 4.5 and positiveTrack.clusterEnergySumCMS < 1.;
854 if ((invMass > 9.) and (missNegClust or missPosClust)) {
855 calculationResult["eeOneClust"] = 1;
856 }
857 }
858
859 if (calculationResult["selectee1leg1trk"] == 1 or calculationResult["selectee1leg1clst"] == 1) {
860 for (int iflat = 0; iflat < 9; iflat++) {
861 const std::string& eeFlatName = "eeFlat" + std::to_string(iflat);
862 calculationResult[eeFlatName] =
863 thetaFlatten >= flatBoundaries[iflat] and thetaFlatten < flatBoundaries[iflat + 1];
864 if (calculationResult[eeFlatName]) {
865 calculationResult["selectee"] = 1;
866 }
867 }
868 }
869
870 // Single tag pi0 / eta dedicated lines
871 if (calculationResult["nTrkLoose"] == 1 and calculationResult["maximumPCMS"] > 0.8 and selectedClusters.size() >= 2) {
872
873 decltype(selectedClusters) selectedSingleTagClusters(selectedClusters.size());
874 auto lastItem = std::copy_if(selectedClusters.begin(), selectedClusters.end(), selectedSingleTagClusters.begin(),
875 [](auto & cluster) {
876 const bool isNeutralCluster = not cluster.isTrack;
877 const bool hasEnoughEnergy = cluster.energyLab > 0.1;
878 const double clusterThetaLab = cluster.p4Lab.Theta() * TMath::RadToDeg();
879 const bool isInAcceptance = 17 < clusterThetaLab and clusterThetaLab < 150.;
880 return isNeutralCluster and hasEnoughEnergy and isInAcceptance;
881 });
882 selectedSingleTagClusters.resize(std::distance(selectedSingleTagClusters.begin(), lastItem));
883
884 if (selectedSingleTagClusters.size() >= 2) { // One track and at least two clusters are found
885
886 const auto& track = maximumPtTracks.at(-1) ? *maximumPtTracks.at(-1) : *maximumPtTracks.at(1);
887 // in real signal, the pi0 decay daughters are always the two most-energetic neutral clusters.
888 const auto& firstCluster = selectedSingleTagClusters[0];
889 const auto& secondCluster = selectedSingleTagClusters[1];
890
891 const ROOT::Math::PxPyPzEVector trackP4CMS = track.p4CMS;
892 const ROOT::Math::PxPyPzEVector pi0P4CMS = firstCluster.p4CMS + secondCluster.p4CMS;
893
894 const bool passPi0ECMS = pi0P4CMS.E() > 1. and pi0P4CMS.E() < 0.525 * p4ofCOM.M();
895 const double thetaSumCMS = (pi0P4CMS.Theta() + trackP4CMS.Theta()) * TMath::RadToDeg();
896 const bool passThetaSum = thetaSumCMS < 170. or thetaSumCMS > 190.;
897
898 double dphiCMS = std::abs(trackP4CMS.Phi() - pi0P4CMS.Phi()) * TMath::RadToDeg();
899 if (dphiCMS > 180) {
900 dphiCMS = 360 - dphiCMS;
901 }
902 const bool passdPhi = dphiCMS > 160.;
903
904 if (passPi0ECMS and passThetaSum and passdPhi and pi0P4CMS.M() < 0.7) {
905 calculationResult["singleTagLowMass"] = 1;
906 } else if (passPi0ECMS and passThetaSum and passdPhi and pi0P4CMS.M() > 0.7) {
907 calculationResult["singleTagHighMass"] = 1;
908 }
909 }
910 }
911
912 //..Updated to use new track definitions
913 if (calculationResult["nTrkLooseB"] == 1 and calculationResult["maximumPCMSB"] > 0.8 and selectedClusters.size() >= 2) {
914
915 decltype(selectedClusters) selectedSingleTagClusters(selectedClusters.size());
916 auto lastItem = std::copy_if(selectedClusters.begin(), selectedClusters.end(), selectedSingleTagClusters.begin(),
917 [](auto & cluster) {
918 const bool isNeutralCluster = not cluster.isTrack;
919 const bool hasEnoughEnergy = cluster.energyLab > 0.1;
920 const double clusterThetaLab = cluster.p4Lab.Theta() * TMath::RadToDeg();
921 const bool isInAcceptance = 17 < clusterThetaLab and clusterThetaLab < 150.;
922 return isNeutralCluster and hasEnoughEnergy and isInAcceptance;
923 });
924 selectedSingleTagClusters.resize(std::distance(selectedSingleTagClusters.begin(), lastItem));
925
926 if (selectedSingleTagClusters.size() >= 2) { // One track and at least two clusters are found
927
928 const auto& track = maximumPtTracks.at(-1) ? *maximumPtTracks.at(-1) : *maximumPtTracks.at(1);
929 // in real signal, the pi0 decay daughters are always the two most-energetic neutral clusters.
930 const auto& firstCluster = selectedSingleTagClusters[0];
931 const auto& secondCluster = selectedSingleTagClusters[1];
932
933 const ROOT::Math::PxPyPzEVector trackP4CMS = track.p4CMS;
934 const ROOT::Math::PxPyPzEVector pi0P4CMS = firstCluster.p4CMS + secondCluster.p4CMS;
935
936 const bool passPi0ECMS = pi0P4CMS.E() > 1. and pi0P4CMS.E() < 0.525 * p4ofCOM.M();
937 const double thetaSumCMS = (pi0P4CMS.Theta() + trackP4CMS.Theta()) * TMath::RadToDeg();
938 const bool passThetaSum = thetaSumCMS < 170. or thetaSumCMS > 190.;
939
940 double dphiCMS = std::abs(trackP4CMS.Phi() - pi0P4CMS.Phi()) * TMath::RadToDeg();
941 if (dphiCMS > 180) {
942 dphiCMS = 360 - dphiCMS;
943 }
944 const bool passdPhi = dphiCMS > 160.;
945
946 if (passPi0ECMS and passThetaSum and passdPhi and pi0P4CMS.M() < 0.7) {
947 calculationResult["singleTagLowMassB"] = 1;
948 } else if (passPi0ECMS and passThetaSum and passdPhi and pi0P4CMS.M() > 0.7) {
949 calculationResult["singleTagHighMassB"] = 1;
950 }
951 }
952 }
953
954 //..Cosmic selection. Need exactly two tracks.
955 if (m_tracks.getEntries() == 2) {
956
957 const auto negTrack = maximumPtTracksWithoutZCut.at(-1);
958 const auto posTrack = maximumPtTracksWithoutZCut.at(1);
959
960 if (negTrack and posTrack) {
961
962 const double maxNegpT = negTrack->pT;
963 const double maxPospT = posTrack->pT;
964 const double maxClusterENeg = negTrack->clusterEnergySumLab;
965 const double maxClusterEPos = posTrack->clusterEnergySumLab;
966
967 const ROOT::Math::PxPyPzEVector& momentumLabNeg(negTrack->p4Lab);
968 const ROOT::Math::PxPyPzEVector& momentumLabPos(posTrack->p4Lab);
969
970 const double& z0Neg = negTrack->track->getTrackFitResultWithClosestMass(Const::pion)->getZ0();
971 const double& d0Neg = negTrack->track->getTrackFitResultWithClosestMass(Const::pion)->getD0();
972 const double& z0Pos = posTrack->track->getTrackFitResultWithClosestMass(Const::pion)->getZ0();
973 const double& d0Pos = posTrack->track->getTrackFitResultWithClosestMass(Const::pion)->getD0();
974
975 // Select cosmic using these tracks
976 const bool goodMagneticRegion = (z0Neg<m_goodMagneticRegionZ0 or abs(d0Neg)>m_goodMagneticRegionD0)
977 and (z0Pos<m_goodMagneticRegionZ0 or abs(d0Pos)>m_goodMagneticRegionD0);
978 if (maxNegpT > m_cosmicMinPt and maxPospT > m_cosmicMinPt and maxClusterENeg < m_cosmicMaxClusterEnergy
979 and maxClusterEPos < m_cosmicMaxClusterEnergy and goodMagneticRegion) {
980 double dphiLab = std::abs(momentumLabNeg.Phi() - momentumLabPos.Phi()) * TMath::RadToDeg();
981 if (dphiLab > 180) {
982 dphiLab = 360 - dphiLab;
983 }
984
985 const double thetaSumLab = (momentumLabNeg.Theta() + momentumLabPos.Theta()) * TMath::RadToDeg();
986
987 constexpr double phiBackToBackTolerance = 2.;
988 constexpr double thetaBackToBackTolerance = 2.;
989 if ((180 - dphiLab) < phiBackToBackTolerance and std::abs(180 - thetaSumLab) < thetaBackToBackTolerance) {
990 calculationResult["cosmic"] = 1;
991 }
992 }
993 }
994 }
995
996 //..Displaced vertex
997 // See https://indico.belle2.org/event/8973/ and references therein
998 if (maximumPtTracksWithoutZCut.at(-1) and maximumPtTracksWithoutZCut.at(1)) {
999
1000 //..Make particles of the two highest pt tracks (without IP cut)
1001 const auto nTrack = maximumPtTracksWithoutZCut.at(-1)->track;
1002 Particle* nParticle = new Particle(nTrack, Const::pion);
1003
1004 const auto pTrack = maximumPtTracksWithoutZCut.at(1)->track;
1005 Particle* pParticle = new Particle(pTrack, Const::pion);
1006
1007 //..Make a vertex of these two
1009 vertexFit->addParticle(nParticle);
1010 vertexFit->addParticle(pParticle);
1011 vertexFit->doFit();
1012
1013 //..Vertex properties
1014 const double chisq = vertexFit->getCHIsq();
1015 const int ndf = vertexFit->getNDF();
1016 const double vertexProb = TMath::Prob(chisq, ndf);
1017 const auto vertexLocation = vertexFit->getVertex();
1018 const double vertexXY = vertexLocation.perp();
1019 const double vertexTheta = vertexLocation.theta() * TMath::RadToDeg();
1020
1021 //..Angular differance of two tracks to reject cosmics
1022 // Tolerance could be reduced from 10 deg to 2 deg if needed for physics reasons,
1023 // for a 5% increase in the rate of selected displaced vertex triggers.
1024 // See https://gitlab.desy.de/belle2/software/basf2/-/merge_requests/1867
1025 const ROOT::Math::PxPyPzEVector& momentumLabNeg(maximumPtTracksWithoutZCut.at(-1)->p4Lab);
1026 const ROOT::Math::PxPyPzEVector& momentumLabPos(maximumPtTracksWithoutZCut.at(1)->p4Lab);
1027 const double thetaSumLab = (momentumLabNeg.Theta() + momentumLabPos.Theta()) * TMath::RadToDeg();
1028 double dPhiLab = std::abs(momentumLabNeg.Phi() - momentumLabPos.Phi()) * TMath::RadToDeg();
1029 if (dPhiLab > 180) {
1030 dPhiLab = 360 - dPhiLab;
1031 }
1032 const double backToBackTolerance = 10.; // degrees
1033 const bool backToBackLab = std::abs(thetaSumLab - 180.) < backToBackTolerance and std::abs(dPhiLab - 180.) < backToBackTolerance;
1034
1035 //..Select a displaced vertex
1036 const double minProbChiVertex = 0.005; // minimum probability chi square. Many backgrounds have bad chi sq.
1037 const double minXYVertex = 3.; // minimum xy of vertex (cm). Should pass track filters below this.
1038 const double maxXYVertex = 60.; // maximum xy. Insufficient CDC for good reconstruction beyond this.
1039 const double minThetaVertex = 30.; // Large Bhabha background at low angles.
1040 const double maxThetaVertex = 120.; // Large Bhabha background at low angles.
1041 if (vertexProb > minProbChiVertex and vertexXY > minXYVertex and vertexXY < maxXYVertex and vertexTheta > minThetaVertex
1042 and vertexTheta < maxThetaVertex
1043 and not backToBackLab) {calculationResult["displacedVertex"] = 1;}
1044
1045 //..Clean up
1046 delete nParticle;
1047 delete pParticle;
1048 delete vertexFit;
1049
1050 }
1051
1052 // HLT prefilter stuff
1053 // Initialise parameters of HLTPrefilter
1054 if (!m_hltPrefilterParameters.isValid())
1055 B2FATAL("HLTprefilter parameters are not available.");
1056
1057 // Timing mode thresholds
1058 m_timingPrefilter.LERtimeSinceLastInjectionMin = m_hltPrefilterParameters->getLERtimeSinceLastInjectionMin();
1059 m_timingPrefilter.LERtimeSinceLastInjectionMax = m_hltPrefilterParameters->getLERtimeSinceLastInjectionMax();
1060 m_timingPrefilter.HERtimeSinceLastInjectionMin = m_hltPrefilterParameters->getHERtimeSinceLastInjectionMin();
1061 m_timingPrefilter.HERtimeSinceLastInjectionMax = m_hltPrefilterParameters->getHERtimeSinceLastInjectionMax();
1066 m_timingPrefilter.prescale = m_hltPrefilterParameters->getHLTPrefilterPrescale();
1067
1068 // CDC-ECL mode thresholds
1071 m_cdceclPrefilter.prescale = m_hltPrefilterParameters->getHLTPrefilterPrescale();
1072
1073 //find out if we are in the passive veto (i=0) or in the active veto window (i=1)
1074 int index = 0; //events accepted in the passive veto window but not in the active
1075 try {
1076 if (m_l1Trigger->testInput("passive_veto") == 1 && m_l1Trigger->testInput("cdcecl_veto") == 0) index = 1; //events in active veto
1077 } catch (const std::exception&) {
1078 }
1079
1080 // Check if event is in active veto
1081 if (index == 1) {
1082
1084 calculationResult["Prefilter_InjectionStrip"] = 1;
1085 }
1086
1088 calculationResult["Prefilter_CDCECLthreshold"] = 1;
1089 }
1090 }
1091
1092
1093}
DataType Angle(const B2Vector3< DataType > &q) const
The angle w.r.t.
Definition: B2Vector3.h:302
Class to provide momentum-related information from ECLClusters.
Definition: ClusterUtils.h:36
const ROOT::Math::PxPyPzEVector Get4MomentumFromCluster(const ECLCluster *cluster, ECLCluster::EHypothesisBit hypo)
Returns four momentum vector.
Definition: ClusterUtils.cc:25
const ROOT::Math::XYZVector GetIPPosition()
Returns default IP position from beam parameters.
static const ChargedStable pion
charged pion particle
Definition: Const.h:661
ECL cluster data.
Definition: ECLCluster.h:27
@ c_nPhotons
CR is split into n photons (N1)
uint32_t nECLDigitsMax
Maximum threshold for ECL Digits.
Definition: HLTPrefilter.h:113
uint32_t nCDCHitsMax
Define thresholds for variables.
Definition: HLTPrefilter.h:111
unsigned int prescale
Prescale for accepting HLTPrefilter lines, by default we randomly accept 1 out of every 1000 events.
Definition: HLTPrefilter.h:115
double LERtimeSinceLastInjectionMin
Define thresholds for variables.
Definition: HLTPrefilter.h:46
double HERtimeInBeamCycleMax
Maximum threshold of timeInBeamCycle for LER injection.
Definition: HLTPrefilter.h:60
double HERtimeSinceLastInjectionMin
Minimum threshold of timeSinceLastInjection for HER injection.
Definition: HLTPrefilter.h:50
double HERtimeSinceLastInjectionMax
Maximum threshold of timeSinceLastInjection for HER injection.
Definition: HLTPrefilter.h:52
double LERtimeSinceLastInjectionMax
Maximum threshold of timeSinceLastInjection for LER injection.
Definition: HLTPrefilter.h:48
double HERtimeInBeamCycleMin
Minimum threshold of timeInBeamCycle for HER injection.
Definition: HLTPrefilter.h:58
double LERtimeInBeamCycleMax
Maximum threshold of timeInBeamCycle for LER injection.
Definition: HLTPrefilter.h:56
unsigned int prescale
Prescale for accepting HLTPrefilter lines, by default we randomly accept 1 out of every 1000 events.
Definition: HLTPrefilter.h:62
double LERtimeInBeamCycleMin
Minimum threshold of timeInBeamCycle for LER injection.
Definition: HLTPrefilter.h:54
Class to hold Lorentz transformations from/to CMS and boost vector.
double getCMSEnergy() const
Returns CMS energy of e+e- (aka.
const ROOT::Math::LorentzRotation rotateLabToCms() const
Returns Lorentz transformation from Lab to CMS.
Class to store reconstructed particles.
Definition: Particle.h:75
double m_EminLab
which lab energy defines nE180Lab
double m_reducedEsinglePhoton
which CMS energy defines nReducedEsingle clusters
StoreObjPtr< TRGSummary > m_l1Trigger
Store Object with the trigger result.
double m_E0min
which CMS energy defines nEmedium
double m_goodMagneticRegionZ0
maximum z0 for well understood magnetic field (cm)
double m_cosmicMinPt
which LAB pt defines a cosmic
double m_EsinglePhoton
which CMS energy defines nEsingleClust
double m_E2min
which CMS energy defines nElow
double m_tightTrkZ0
which Z0 defines a tight track
StoreArray< Track > m_tracks
Store Array of the tracks to be used.
double m_EminLab3Cluster
which lab energy defines nE500Lab
double m_Ehigh
which CMS energy defines nEhigh
DBObjPtr< HLTPrefilterParameters > m_hltPrefilterParameters
Objects relevant to HLTPrefilter monitoring HLTprefilterParameters Database OjbPtr.
StoreArray< ECLCluster > m_eclClusters
Store Array of the ecl clusters to be used.
double m_EminLab4Cluster
which lab energy defines nE300Lab
HLTPrefilter::TimingCutState m_timingPrefilter
Helper instance for timing based prefilter.
double m_cosmicMaxClusterEnergy
which LAB cluster energy vetoes a cosmic candidate
HLTPrefilter::CDCECLCutState m_cdceclPrefilter
Helper instance for CDC-ECL occupancy based prefilter.
double m_goodMagneticRegionD0
minimum d0 for well understood magnetic field, if z0 is large (cm)
double m_looseTrkZ0
which Z0 defines a loose track
@ TTYP_DPHY
delayed physics events for background
Definition: TRGSummary.h:65
@ TTYP_POIS
poisson random trigger
Definition: TRGSummary.h:73
@ TTYP_RAND
random trigger events
Definition: TRGSummary.h:67
Values of the result of a track fit with a given particle hypothesis.
Class that bundles various TrackFitResults.
Definition: Track.h:25
virtual int getNDF(void) const
Get an NDF of the fit.
Definition: KFitBase.cc:114
enum KFitError::ECode addParticle(const Particle *particle)
Add a particle to the fitter.
Definition: KFitBase.cc:59
VertexFitKFit is a derived class from KFitBase to perform vertex-constraint kinematical fit.
Definition: VertexFitKFit.h:34
double getCHIsq(void) const override
Get a chi-square of the fit.
enum KFitError::ECode doFit(void)
Perform a vertex-constraint fit.
const HepPoint3D getVertex(const int flag=KFitConst::kAfterFit) const
Get a vertex position.
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:516
double charge(int pdgCode)
Returns electric charge of a particle with given pdg code.
Definition: EvtPDLUtil.cc:44
Temporary data structure holding the track(s) with the maximum pT.
double pLab
the momentum magnitude in lab system
double pT
the pT of the track
ROOT::Math::PxPyPzEVector p4CMS
the 4 momentum in CMS system
double clusterEnergySumLab
the sum of related cluster energies in lab system
ROOT::Math::PxPyPzEVector p4Lab
the 4 momentum in lab system
double clusterEnergySumCMS
the sum of related cluster energies in CMS system
double pCMS
the momentum magnitude in CMS system
const Track * track
the track
Temporary data structure holding the ECL clusters used for this analysis.
double energyLab
the energy in Lab system
double clusterTime
the time of the cluster
ROOT::Math::PxPyPzEVector p4CMS
the 4 momentum in CMS system
ROOT::Math::PxPyPzEVector p4Lab
the 4 momentum in lab system
const ECLCluster * cluster
The ECL cluster.
bool isTrack
is this ECL cluster likely from a track (or a photon) = is it charged?
double energyCMS
the energy in CMS system

◆ fillInCalculations()

const SoftwareTriggerObject & fillInCalculations ( )
inherited

Main function of this class: calculate the needed variables using the overwritten doCalculation function and write out the values into the results object (with their names).

Please make sure to override (or clear) the variables! Otherwise it can happen that their old values are still in the object.

What variables exactly are added to the result depends on the implementation details of the class.

Definition at line 44 of file SoftwareTriggerCalculation.cc.

45 {
46 const unsigned int sizeBeforeCheck = m_calculationResult.size();
48
49 if (m_calculationResult.size() != sizeBeforeCheck and sizeBeforeCheck > 0) {
50 B2WARNING("Calculator added more variables (" << m_calculationResult.size() <<
51 ") than there were before (" << sizeBeforeCheck << "). Probably something strange is going on!");
52 }
53
55 }
virtual void doCalculation(SoftwareTriggerObject &m_calculationResult)=0
Override this function to implement your calculation.

◆ requireStoreArrays()

void requireStoreArrays ( )
overridevirtual

Require the particle list. We do not need more here.

Implements SoftwareTriggerCalculation.

Definition at line 77 of file FilterCalculator.cc.

78{
79 m_tracks.isRequired();
80 m_eclClusters.isRequired();
81 m_l1Trigger.isOptional();
82 m_bitsNN.isOptional();
83}

◆ writeDebugOutput()

void writeDebugOutput ( const std::unique_ptr< TTree > &  debugOutputTTree)
inherited

Function to write out debug output into the given TTree.

Needs an already prefilled calculationResult for this (probably using the fillInCalculations function).

Definition at line 19 of file SoftwareTriggerCalculation.cc.

20 {
21 if (not m_debugPrepared) {
22 for (auto& identifierWithValue : m_calculationResult) {
23 const std::string& identifier = identifierWithValue.first;
24 double& value = identifierWithValue.second;
25
26 debugOutputTTree->Branch(identifier.c_str(), &value);
27 }
28 m_debugPrepared = true;
29 }
30
31 debugOutputTTree->Fill();
32 }
bool m_debugPrepared
Flag to not add the branches twice to the TTree.

Member Data Documentation

◆ m_bitsNN

Store Object with the trigger NN bits.

Definition at line 53 of file FilterCalculator.h.

◆ m_calculationResult

SoftwareTriggerObject m_calculationResult
privateinherited

Internal storage of the result of the calculation.

Definition at line 74 of file SoftwareTriggerCalculation.h.

◆ m_cdceclPrefilter

HLTPrefilter::CDCECLCutState m_cdceclPrefilter
private

Helper instance for CDC-ECL occupancy based prefilter.

Definition at line 90 of file FilterCalculator.h.

◆ m_cosmicMaxClusterEnergy

double m_cosmicMaxClusterEnergy = 1.0 * Unit::GeV
private

which LAB cluster energy vetoes a cosmic candidate

Definition at line 78 of file FilterCalculator.h.

◆ m_cosmicMinPt

double m_cosmicMinPt = 0.5 * Unit::GeV
private

which LAB pt defines a cosmic

Definition at line 76 of file FilterCalculator.h.

◆ m_debugPrepared

bool m_debugPrepared = false
privateinherited

Flag to not add the branches twice to the TTree.

Definition at line 76 of file SoftwareTriggerCalculation.h.

◆ m_E0min

double m_E0min = 0.3
private

which CMS energy defines nEmedium

Definition at line 62 of file FilterCalculator.h.

◆ m_E2min

double m_E2min = 0.2
private

which CMS energy defines nElow

Definition at line 60 of file FilterCalculator.h.

◆ m_eclClusters

StoreArray<ECLCluster> m_eclClusters
private

Store Array of the ecl clusters to be used.

Definition at line 49 of file FilterCalculator.h.

◆ m_Ehigh

double m_Ehigh = 2
private

which CMS energy defines nEhigh

Definition at line 64 of file FilterCalculator.h.

◆ m_EminLab

double m_EminLab = 0.18
private

which lab energy defines nE180Lab

Definition at line 66 of file FilterCalculator.h.

◆ m_EminLab3Cluster

double m_EminLab3Cluster = 0.5
private

which lab energy defines nE500Lab

Definition at line 70 of file FilterCalculator.h.

◆ m_EminLab4Cluster

double m_EminLab4Cluster = 0.3
private

which lab energy defines nE300Lab

Definition at line 68 of file FilterCalculator.h.

◆ m_EsinglePhoton

double m_EsinglePhoton = 1
private

which CMS energy defines nEsingleClust

Definition at line 72 of file FilterCalculator.h.

◆ m_goodMagneticRegionD0

double m_goodMagneticRegionD0 = 26.5
private

minimum d0 for well understood magnetic field, if z0 is large (cm)

Definition at line 82 of file FilterCalculator.h.

◆ m_goodMagneticRegionZ0

double m_goodMagneticRegionZ0 = 57.
private

maximum z0 for well understood magnetic field (cm)

Definition at line 80 of file FilterCalculator.h.

◆ m_hltPrefilterParameters

DBObjPtr<HLTPrefilterParameters> m_hltPrefilterParameters
private

Objects relevant to HLTPrefilter monitoring HLTprefilterParameters Database OjbPtr.

HLT prefilter parameters

Definition at line 86 of file FilterCalculator.h.

◆ m_l1Trigger

StoreObjPtr<TRGSummary> m_l1Trigger
private

Store Object with the trigger result.

Definition at line 51 of file FilterCalculator.h.

◆ m_looseTrkZ0

double m_looseTrkZ0 = 10 * Unit::cm
private

which Z0 defines a loose track

Definition at line 56 of file FilterCalculator.h.

◆ m_reducedEsinglePhoton

double m_reducedEsinglePhoton = 0.5
private

which CMS energy defines nReducedEsingle clusters

Definition at line 74 of file FilterCalculator.h.

◆ m_tightTrkZ0

double m_tightTrkZ0 = 2 * Unit::cm
private

which Z0 defines a tight track

Definition at line 58 of file FilterCalculator.h.

◆ m_timingPrefilter

HLTPrefilter::TimingCutState m_timingPrefilter
private

Helper instance for timing based prefilter.

Definition at line 88 of file FilterCalculator.h.

◆ m_tracks

StoreArray<Track> m_tracks
private

Store Array of the tracks to be used.

Definition at line 47 of file FilterCalculator.h.


The documentation for this class was generated from the following files: