18 double dptRel_PrimaryTrack,
19 double dptRel_MergedTrack,
20 double ptErrorSecondary,
24 dptRel_PrimaryTrack_ = dptRel_PrimaryTrack;
25 dptRel_MergedTrack_ = dptRel_MergedTrack;
26 ptErrorSecondary_ = ptErrorSecondary;
28 if (nuclCalibFactors.size() == 5) {
29 fConst_[0] = nuclCalibFactors[0];
30 fConst_[1] = nuclCalibFactors[1];
32 fNorm_[0] = nuclCalibFactors[2];
33 fNorm_[1] = nuclCalibFactors[3];
35 fExp_[0] = nuclCalibFactors[4];
38 <<
"Wrong calibration factors for nuclear interactions. The calibration procedure would not be applyed." 40 bCalibPrimary_ =
false;
44 edm::LogInfo(
"PFCandConnector") <<
" ====================== The PFCandConnector is switched " << sCorrect.c_str()
45 <<
" ==================== " << std::endl;
46 std::string sCalibPrimary = bCalibPrimary_ ?
"used for calibration" :
"not used for calibration";
48 edm::LogInfo(
"PFCandConnector") <<
"Primary Tracks are " << sCalibPrimary.c_str() << std::endl;
49 if (bCorrect_ && bCalibPrimary_)
50 edm::LogInfo(
"PFCandConnector") <<
"Under the condition that the precision on the Primary track is better than " 51 << dptRel_PrimaryTrack_ <<
" % " << std::endl;
52 if (bCorrect_ && bCalibPrimary_)
53 edm::LogInfo(
"PFCandConnector") <<
" and on merged tracks better than " << dptRel_MergedTrack_ <<
" % " 55 if (bCorrect_ && bCalibPrimary_)
56 edm::LogInfo(
"PFCandConnector") <<
" and secondary tracks in some cases more precise than " 57 << ptErrorSecondary_ <<
" GeV" << std::endl;
58 if (bCorrect_ && bCalibPrimary_)
59 edm::LogInfo(
"PFCandConnector") <<
"factor = (" << fConst_[0] <<
" + " << fConst_[1] <<
"*cFrac) - (" << fNorm_[0]
60 <<
" - " << fNorm_[1] <<
"cFrac)*exp( " << -1 * fExp_[0] <<
"*pT )" << std::endl;
61 edm::LogInfo(
"PFCandConnector") <<
" =========================================================== " << std::endl;
68 std::vector<bool> bMask;
69 bMask.resize(pfCand.size(),
false);
73 LogTrace(
"PFCandConnector|connect") <<
"pfCand.size()=" << pfCand.size() <<
"bCalibPrimary_=" << bCalibPrimary_;
75 for (
unsigned int ce1 = 0; ce1 < pfCand.size(); ++ce1) {
76 if (isPrimaryNucl(pfCand.at(ce1))) {
79 <<
"Nuclear Interaction w Primary Candidate " << ce1 <<
" " << pfCand.at(ce1) << endl
80 <<
" based on the Track " << pfCand.at(ce1).trackRef().key()
81 <<
" w pT = " << pfCand.at(ce1).trackRef()->pt() <<
" #pm " 82 << pfCand.at(ce1).trackRef()->ptError() / pfCand.at(ce1).trackRef()->pt() * 100 <<
" %" 83 <<
" ECAL = " << pfCand.at(ce1).ecalEnergy() <<
" HCAL = " << pfCand.at(ce1).hcalEnergy() << endl;
86 (pfCand.at(ce1)).displacedVertexRef(fT_TO_DISP_)->Dump();
89 analyseNuclearWPrim(pfCand, bMask, ce1);
93 <<
"After Connection the candidate " << ce1 <<
" is " << pfCand.at(ce1) << endl
97 for (
unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
99 LogTrace(
"PFCandConnector|connect") << *(elementsInBlocks[blockElem].first) << endl;
100 LogTrace(
"PFCandConnector|connect") <<
" position " << elementsInBlocks[blockElem].second;
106 for (
unsigned int ce1 = 0; ce1 < pfCand.size(); ++ce1) {
107 if (!bMask[ce1] && isSecondaryNucl(pfCand.at(ce1))) {
110 <<
"Nuclear Interaction w no Primary Candidate " << ce1 <<
" " << pfCand.at(ce1) << endl
111 <<
" based on the Track " << pfCand.at(ce1).trackRef().key()
112 <<
" w pT = " << pfCand.at(ce1).trackRef()->pt() <<
" #pm " << pfCand.at(ce1).trackRef()->ptError() <<
" %" 113 <<
" ECAL = " << pfCand.at(ce1).ecalEnergy() <<
" HCAL = " << pfCand.at(ce1).hcalEnergy()
114 <<
" dE(Trk-CALO) = " 115 << pfCand.at(ce1).trackRef()->p() - pfCand.at(ce1).ecalEnergy() - pfCand.at(ce1).hcalEnergy()
116 <<
" Nmissing hits = " 120 (pfCand.at(ce1)).displacedVertexRef(fT_FROM_DISP_)->Dump();
123 analyseNuclearWSec(pfCand, bMask, ce1);
126 LogTrace(
"PFCandConnector|connect") <<
"After Connection the candidate " << ce1 <<
" is " << pfCand.at(ce1)
127 <<
" and elements connected to it are: " << endl;
130 for (
unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
132 LogTrace(
"PFCandConnector|connect") << *(elementsInBlocks[blockElem].first) << endl;
133 LogTrace(
"PFCandConnector|connect") <<
" position " << elementsInBlocks[blockElem].second;
140 for (
unsigned int ce1 = 0; ce1 < pfCand.size(); ++ce1)
142 pfC.push_back(pfCand.at(ce1));
144 LogTrace(
"PFCandConnector|connect") <<
"end of function";
150 std::vector<bool>& bMask,
151 unsigned int ce1)
const {
162 momentumSec = momentumPrim / momentumPrim.E() * (primaryCand.
ecalEnergy() + primaryCand.
hcalEnergy());
164 map<double, math::XYZTLorentzVectorD> candidatesWithTrackExcess;
165 map<double, math::XYZTLorentzVectorD> candidatesWithoutCalo;
169 for (
unsigned int ce2 = 0; ce2 < pfCand.size(); ++ce2) {
170 if (ce2 != ce1 && isSecondaryNucl(pfCand.at(ce2))) {
171 ref2 = (pfCand.at(ce2)).displacedVertexRef(fT_FROM_DISP_);
174 LogTrace(
"PFCandConnector|analyseNuclearWPrim")
175 <<
"\t here is a Secondary Candidate " << ce2 <<
" " << pfCand.at(ce2) << endl
176 <<
"\t based on the Track " << pfCand.at(ce2).trackRef().key()
177 <<
" w p = " << pfCand.at(ce2).trackRef()->p() <<
" w pT = " << pfCand.at(ce2).trackRef()->pt() <<
" #pm " 178 << pfCand.at(ce2).trackRef()->ptError() <<
" %" 179 <<
" ECAL = " << pfCand.at(ce2).ecalEnergy() <<
" HCAL = " << pfCand.at(ce2).hcalEnergy()
180 <<
" dE(Trk-CALO) = " 181 << pfCand.at(ce2).trackRef()->p() - pfCand.at(ce2).ecalEnergy() - pfCand.at(ce2).hcalEnergy()
182 <<
" Nmissing hits = " 185 if (isPrimaryNucl(pfCand.at(ce2))) {
186 LogTrace(
"PFCandConnector|analyseNuclearWPrim") <<
"\t\t but it is also a Primary Candidate " << ce2 << endl;
188 ref1_bis = (pfCand.at(ce2)).displacedVertexRef(fT_TO_DISP_);
190 analyseNuclearWPrim(pfCand, bMask, ce2);
197 for (
unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
198 bool isAlreadyHere =
false;
199 for (
unsigned alreadyBlock = 0; alreadyBlock < elementsAlreadyInBlocks.size(); alreadyBlock++) {
200 if (elementsAlreadyInBlocks[alreadyBlock].
second == elementsInBlocks[blockElem].
second)
201 isAlreadyHere =
true;
204 pfCand.at(ce1).addElementInBlock(elementsInBlocks[blockElem].
first, elementsInBlocks[blockElem].
second);
207 double caloEn = pfCand.at(ce2).ecalEnergy() + pfCand.at(ce2).hcalEnergy();
208 double deltaEn = pfCand.at(ce2).p4().E() - caloEn;
213 if (deltaEn > 1 && nMissOuterHits > 1) {
215 momentumSec += momentumToAdd;
216 LogTrace(
"PFCandConnector|analyseNuclearWPrim")
217 <<
"The difference track-calo s really large and the track miss at least 2 hits. A secondary NI may " 218 "have happened. Let's trust the calo energy" 220 <<
"add " << momentumToAdd << endl;
224 if (caloEn > 0.01 && deltaEn > 1 && nMissOuterHits > 0) {
226 candidatesWithTrackExcess[pfCand.at(ce2).trackRef()->pt() / pfCand.at(ce2).trackRef()->ptError()] =
228 }
else if (caloEn < 0.01)
229 candidatesWithoutCalo[pfCand.at(ce2).trackRef()->pt() / pfCand.at(ce2).trackRef()->ptError()] =
231 momentumSec += (pfCand.at(ce2)).
p4();
241 if (momentumPrim.E() < momentumSec.E()) {
242 LogTrace(
"PFCandConnector|analyseNuclearWPrim")
243 <<
"Size of 0 calo Energy secondary candidates" << candidatesWithoutCalo.size() << endl;
244 for (map<double, math::XYZTLorentzVectorD>::iterator iter = candidatesWithoutCalo.begin();
245 iter != candidatesWithoutCalo.end() && momentumPrim.E() < momentumSec.E();
247 if (momentumSec.E() > iter->second.E() + 0.1) {
248 momentumSec -= iter->second;
250 LogTrace(
"PFCandConnector|analyseNuclearWPrim")
251 <<
"\t Remove a SecondaryCandidate with 0 calo energy " << iter->second << endl;
252 LogTrace(
"PFCandConnector|analyseNuclearWPrim")
253 <<
"momentumPrim.E() = " << momentumPrim.E() <<
" and momentumSec.E() = " << momentumSec.E() << endl;
257 if (momentumPrim.E() < momentumSec.E()) {
258 LogTrace(
"PFCandConnector|analyseNuclearWPrim")
259 <<
"0 Calo Energy rejected but still not sufficient. Size of not enough calo Energy secondary candidates" 260 << candidatesWithTrackExcess.size() << endl;
261 for (map<double, math::XYZTLorentzVectorD>::iterator iter = candidatesWithTrackExcess.begin();
262 iter != candidatesWithTrackExcess.end() && momentumPrim.E() < momentumSec.E();
264 if (momentumSec.E() > iter->second.E() + 0.1)
265 momentumSec -= iter->second;
268 double dpt = pfCand.at(ce1).trackRef()->ptError() / pfCand.at(ce1).trackRef()->pt() * 100;
270 if (momentumSec.E() < 0.1) {
278 if (((ref1->isTherePrimaryTracks() && dpt < dptRel_PrimaryTrack_) ||
279 (ref1->isThereMergedTracks() && dpt < dptRel_MergedTrack_)) &&
280 momentumPrim.E() > momentumSec.E() && momentumSec.E() > 0.1) {
281 if (bCalibPrimary_) {
282 double factor = rescaleFactor(momentumPrim.Pt(), momentumSec.E() / momentumPrim.E());
283 LogTrace(
"PFCandConnector|analyseNuclearWPrim") <<
"factor = " << factor << endl;
284 if (factor * momentumPrim.Pt() < momentumSec.Pt())
285 momentumSec = momentumPrim;
287 momentumSec += (1 -
factor) * momentumPrim;
290 double px = momentumPrim.Px() * momentumSec.P() / momentumPrim.P();
291 double py = momentumPrim.Py() * momentumSec.P() / momentumPrim.P();
292 double pz = momentumPrim.Pz() * momentumSec.P() / momentumPrim.P();
293 double E =
sqrt(px * px + py * py + pz * pz + pion_mass2);
295 pfCand.at(ce1).setP4(momentum);
302 if (primDir.Mag2() < 0.1) {
304 edm::LogWarning(
"PFCandConnector") <<
"A Nuclear Interaction do not have primary direction" << std::endl;
305 pfCand.at(ce1).setP4(momentumSec);
309 double momentumS = momentumSec.P();
310 if (momentumS < 1
e-4)
312 double px = momentumS * primDir.x();
313 double py = momentumS * primDir.y();
314 double pz = momentumS * primDir.z();
315 double E =
sqrt(px * px + py * py + pz * pz + pion_mass2);
318 pfCand.at(ce1).setP4(momentum);
325 std::vector<bool>& bMask,
326 unsigned int ce1)
const {
331 double caloEn = pfCand.at(ce1).ecalEnergy() + pfCand.at(ce1).hcalEnergy();
332 double deltaEn = pfCand.at(ce1).p4().E() - caloEn;
335 ref1 = pfCand.at(ce1).displacedVertexRef(fT_FROM_DISP_);
340 if (ref1->isTherePrimaryTracks() || ref1->isThereMergedTracks()) {
342 for (
unsigned it = 0; it < refittedTracks.size(); it++) {
344 if (ref1->isIncomingTrack(primaryBaseRef))
345 LogTrace(
"PFCandConnector|analyseNuclearWSec")
346 <<
"There is a Primary track ref with pt = " << primaryBaseRef->
pt() << endl;
348 for (
unsigned int ce = 0; ce < pfCand.size(); ++ce) {
352 LogTrace(
"PFCandConnector|analyseNuclearWSec")
353 <<
" It is an electron and it has a ref to a track " << (pfCand.at(ce)).trackRef().isNonnull() << endl;
355 if ((pfCand.at(ce)).trackRef().isNonnull()) {
358 LogTrace(
"PFCandConnector|analyseNuclearWSec")
359 <<
"With Track Ref pt = " << (pfCand.at(ce)).trackRef()->pt() << endl;
361 if (bRef == primaryBaseRef) {
363 LogTrace(
"PFCandConnector|analyseNuclearWSec")
364 <<
"It is a NI from electron. NI Discarded. Just release the candidate." << endl;
366 LogTrace(
"PFCandConnector|analyseNuclearWSec")
367 <<
"It is a NI from muon. NI Discarded. Just release the candidate" << endl;
372 if (caloEn < 0.1 && pfCand.at(ce1).trackRef()->ptError() > ptErrorSecondary_) {
374 <<
"discarded track since no calo energy and ill measured" << endl;
377 if (caloEn > 0.1 && deltaEn > ptErrorSecondary_ &&
378 pfCand.at(ce1).trackRef()->ptError() > ptErrorSecondary_) {
380 <<
"rescaled momentum of the track since no calo energy and ill measured" << endl;
382 double factor = caloEn / pfCand.at(ce1).p4().E();
383 pfCand.at(ce1).rescaleMomentum(factor);
398 if (deltaEn > ptErrorSecondary_ && nMissOuterHits > 1) {
400 momentumSec = momentumToAdd;
401 LogTrace(
"PFCandConnector|analyseNuclearWSec")
402 <<
"The difference track-calo s really large and the track miss at least 2 hits. A secondary NI may have " 403 "happened. Let's trust the calo energy" 405 <<
"add " << momentumToAdd << endl;
409 for (
unsigned int ce2 = ce1 + 1; ce2 < pfCand.size(); ++ce2) {
410 if (isSecondaryNucl(pfCand.at(ce2))) {
411 ref2 = (pfCand.at(ce2)).displacedVertexRef(fT_FROM_DISP_);
414 LogTrace(
"PFCandConnector|analyseNuclearWSec")
415 <<
"\t here is a Secondary Candidate " << ce2 <<
" " << pfCand.at(ce2) << endl
416 <<
"\t based on the Track " << pfCand.at(ce2).trackRef().key()
417 <<
" w pT = " << pfCand.at(ce2).trackRef()->pt() <<
" #pm " << pfCand.at(ce2).trackRef()->ptError() <<
" %" 418 <<
" ECAL = " << pfCand.at(ce2).ecalEnergy() <<
" HCAL = " << pfCand.at(ce2).hcalEnergy()
419 <<
" dE(Trk-CALO) = " 420 << pfCand.at(ce2).trackRef()->p() - pfCand.at(ce2).ecalEnergy() - pfCand.at(ce2).hcalEnergy()
421 <<
" Nmissing hits = " 427 for (
unsigned blockElem = 0; blockElem < elementsInBlocks.size(); blockElem++) {
428 bool isAlreadyHere =
false;
429 for (
unsigned alreadyBlock = 0; alreadyBlock < elementsAlreadyInBlocks.size(); alreadyBlock++) {
430 if (elementsAlreadyInBlocks[alreadyBlock].
second == elementsInBlocks[blockElem].
second)
431 isAlreadyHere =
true;
434 pfCand.at(ce1).addElementInBlock(elementsInBlocks[blockElem].
first, elementsInBlocks[blockElem].
second);
437 double caloEn = pfCand.at(ce2).ecalEnergy() + pfCand.at(ce2).hcalEnergy();
438 double deltaEn = pfCand.at(ce2).p4().E() - caloEn;
441 if (deltaEn > ptErrorSecondary_ && nMissOuterHits > 1) {
443 momentumSec += momentumToAdd;
444 LogTrace(
"PFCandConnector|analyseNuclearWSec")
445 <<
"The difference track-calo s really large and the track miss at least 2 hits. A secondary NI may " 446 "have happened. Let's trust the calo energy" 448 <<
"add " << momentumToAdd << endl;
450 momentumSec += (pfCand.at(ce2)).
p4();
460 if (primDir.Mag2() < 0.1) {
462 pfCand.at(ce1).setP4(momentumSec);
463 edm::LogWarning(
"PFCandConnector") <<
"A Nuclear Interaction do not have primary direction" << std::endl;
467 double momentumS = momentumSec.P();
468 if (momentumS < 1
e-4)
470 double px = momentumS * primDir.x();
471 double py = momentumS * primDir.y();
472 double pz = momentumS * primDir.z();
473 double E =
sqrt(px * px + py * py + pz * pz + pion_mass2);
477 pfCand.at(ce1).setP4(momentum);
485 if (pf.
flag(fT_FROM_DISP_)) {
490 else if (ref1->isNucl() || ref1->isNucl_Loose() || ref1->isNucl_Kink())
501 if (pf.
flag(fT_TO_DISP_)) {
507 else if (ref1->isNucl() || ref1->isNucl_Loose() || ref1->isNucl_Kink())
544 double fConst, fNorm, fExp;
546 fConst = fConst_[0] + fConst_[1] * cFrac;
547 fNorm = fNorm_[0] - fNorm_[1] * cFrac;
550 double factor = fConst - fNorm *
exp(-fExp * pt);
556 iDesc.
add<
bool>(
"bCorrect",
true);
557 iDesc.
add<
bool>(
"bCalibPrimary",
true);
558 iDesc.
add<
double>(
"dptRel_PrimaryTrack", 10.0);
559 iDesc.
add<
double>(
"dptRel_MergedTrack", 5.0);
560 iDesc.
add<
double>(
"ptErrorSecondary", 1.0);
561 iDesc.
add<std::vector<double>>(
"nuclCalibFactors", {0.8, 0.15, 0.5, 0.5, 0.05});
double ecalEnergy() const
return corrected Ecal energy
bool isNonnull() const
Checks for non-null.
bool isPrimaryNucl(const reco::PFCandidate &pf) const
ROOT::Math::LorentzVector< ROOT::Math::PxPyPzE4D< double > > XYZTLorentzVectorD
Lorentz vector with cylindrical internal representation using pseudorapidity.
bool isSecondaryNucl(const reco::PFCandidate &pf) const
void analyseNuclearWSec(reco::PFCandidateCollection &, std::vector< bool > &, unsigned int) const
Analyse nuclear interactions where a secondary track is present.
double rescaleFactor(const double pt, const double cFrac) const
Return a calibration factor for a reconstructed nuclear interaction.
static const reco::PFCandidate::Flags fT_FROM_DISP_
static const reco::PFCandidate::Flags fT_TO_DISP_
std::vector< ElementInBlock > ElementsInBlocks
reco::PFDisplacedVertexRef displacedVertexRef(Flags type) const
U second(std::pair< T, U > const &p)
double pt() const
track transverse momentum
bool flag(Flags theFlag) const
return a given flag
const LorentzVector & p4() const final
four-momentum Lorentz vector
reco::PFCandidateCollection connect(reco::PFCandidateCollection &pfCand) const
void analyseNuclearWPrim(reco::PFCandidateCollection &, std::vector< bool > &, unsigned int) const
Analyse nuclear interactions where a primary or merged track is present.
static const double pion_mass2
Useful constants.
ParameterDescriptionBase * add(U const &iLabel, T const &value)
static void fillPSetDescription(edm::ParameterSetDescription &iDesc)
std::vector< reco::PFCandidate > PFCandidateCollection
collection of PFCandidates
XYZVectorD XYZVector
spatial vector with cartesian internal representation
Particle reconstructed by the particle flow algorithm.
double hcalEnergy() const
return corrected Hcal energy
void setParameters(const edm::ParameterSet &iCfgCandConnector)