ATLAS Offline Software
Loading...
Searching...
No Matches
JpsiPlusPsiCascade.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3 Contact: Xin Chen <xin.chen@cern.ch>
4*/
11#include "BPhysPVCascadeTools.h"
16#include <algorithm>
17
18namespace DerivationFramework {
19 typedef ElementLink<xAOD::VertexContainer> VertexLink;
20 typedef std::vector<VertexLink> VertexLinkVector;
21
23 // retrieving vertex Fitter
24 ATH_CHECK( m_iVertexFitter.retrieve() );
25
26 // retrieve PV refitter
27 ATH_CHECK( m_pvRefitter.retrieve() );
28
29 // retrieving the V0 tools
30 ATH_CHECK( m_V0Tools.retrieve() );
31
32 // retrieving the Cascade tools
33 ATH_CHECK( m_CascadeTools.retrieve() );
34
35 ATH_CHECK( m_vertexContainerKey.initialize() );
36 ATH_CHECK( m_vertexPsiContainerKey.initialize() );
37 ATH_CHECK( m_VxPrimaryCandidateName.initialize() );
38 ATH_CHECK( m_trackContainerName.initialize() );
39 ATH_CHECK( m_refPVContainerName.initialize() );
40 ATH_CHECK( m_cascadeOutputsKeys.initialize() );
41 ATH_CHECK( m_eventInfo_key.initialize() );
42
43 auto gendata = std::make_shared<GenData>();
44 if(m_mass_jpsi < 0.) m_mass_jpsi = gendata->particleMass(MC::JPSI).value();
45 if(m_mass_jpsi2 < 0.) m_mass_jpsi2 = gendata->particleMass(MC::JPSI).value();
46 if(m_mass_psi < 0.) m_mass_psi = gendata->particleMass(MC::PSI2S).value();
47 if(m_vtx1Daug1MassHypo < 0.) m_vtx1Daug1MassHypo = gendata->particleMass(MC::MUON).value();
48 if(m_vtx1Daug2MassHypo < 0.) m_vtx1Daug2MassHypo = gendata->particleMass(MC::MUON).value();
49 if(m_vtx1Daug3MassHypo < 0.) m_vtx1Daug3MassHypo = gendata->particleMass(MC::PIPLUS).value();
50 if(m_vtx1Daug4MassHypo < 0.) m_vtx1Daug4MassHypo = gendata->particleMass(MC::PIPLUS).value();
51 if(m_vtx2Daug1MassHypo < 0.) m_vtx2Daug1MassHypo = gendata->particleMass(MC::MUON).value();
52 if(m_vtx2Daug2MassHypo < 0.) m_vtx2Daug2MassHypo = gendata->particleMass(MC::MUON).value();
53
54 return StatusCode::SUCCESS;
55 }
56
57 StatusCode JpsiPlusPsiCascade::addBranches(const EventContext& ctx) const {
58 if (m_vtx1Daug_num != 3 && m_vtx1Daug_num != 4) {
59 ATH_MSG_FATAL("Incorrect number of Psi daughters (should be 3 or 4)");
60 return StatusCode::FAILURE;
61 }
62
63 constexpr int topoN = 3;
64 if(m_cascadeOutputsKeys.size() != topoN) {
65 ATH_MSG_FATAL("Incorrect number of VtxContainers");
66 return StatusCode::FAILURE;
67 }
68 std::array<SG::WriteHandle<xAOD::VertexContainer>, topoN> VtxWriteHandles; int ikey(0);
70 VtxWriteHandles[ikey] = SG::WriteHandle<xAOD::VertexContainer>(key, ctx);
71 ATH_CHECK( VtxWriteHandles[ikey].record(std::make_unique<xAOD::VertexContainer>(), std::make_unique<xAOD::VertexAuxContainer>()) );
72 ikey++;
73 }
74
75 //----------------------------------------------------
76 // retrieve primary vertices
77 //----------------------------------------------------
79 ATH_CHECK( pvContainer.isValid() );
80 if (pvContainer.cptr()->size()==0) {
81 ATH_MSG_WARNING("You have no primary vertices: " << pvContainer.cptr()->size());
82 return StatusCode::RECOVERABLE;
83 }
84
85 //----------------------------------------------------
86 // Record refitted primary vertices
87 //----------------------------------------------------
89 if(m_refitPV) {
91 ATH_CHECK( refPvContainer.record(std::make_unique<xAOD::VertexContainer>(), std::make_unique<xAOD::VertexAuxContainer>()) );
92 }
93
94 std::vector<Trk::VxCascadeInfo*> cascadeinfoContainer;
95 std::vector<Trk::VxCascadeInfo*> cascadeinfoContainer_noConstr;
96 ATH_CHECK(performSearch(&cascadeinfoContainer,&cascadeinfoContainer_noConstr,ctx));
97
99 ATH_CHECK( evt.isValid() );
100 BPhysPVCascadeTools helper(&(*m_CascadeTools), evt.cptr());
101 helper.SetMinNTracksInPV(m_PV_minNTracks);
102
103 // Decorators for the vertices
104 SG::Decorator<VertexLinkVector> CascadeLinksDecor("CascadeVertexLinks");
105 SG::Decorator<VertexLinkVector> PsiLinksDecor("PsiVertexLinks");
106 SG::Decorator<VertexLinkVector> JpsiLinksDecor("JpsiVertexLinks");
107 SG::Decorator<float> chi2_decor("ChiSquared");
108 SG::Decorator<int> ndof_decor("nDoF");
109 SG::Decorator<float> chi2_nc_decor("ChiSquared_nc");
110 SG::Decorator<int> ndof_nc_decor("nDoF_nc");
111 SG::Decorator<float> Pt_decor("Pt");
112 SG::Decorator<float> PtErr_decor("PtErr");
113 SG::Decorator<float> chi2_SV1_decor("ChiSquared_SV1");
114 SG::Decorator<float> chi2_nc_SV1_decor("ChiSquared_nc_SV1");
115 SG::Decorator<float> chi2_V1_decor("ChiSquared_V1");
116 SG::Decorator<int> ndof_V1_decor("nDoF_V1");
117 SG::Decorator<float> lxy_SV1_decor("lxy_SV1");
118 SG::Decorator<float> lxyErr_SV1_decor("lxyErr_SV1");
119 SG::Decorator<float> a0xy_SV1_decor("a0xy_SV1");
120 SG::Decorator<float> a0xyErr_SV1_decor("a0xyErr_SV1");
121 SG::Decorator<float> a0z_SV1_decor("a0z_SV1");
122 SG::Decorator<float> a0zErr_SV1_decor("a0zErr_SV1");
123 SG::Decorator<float> chi2_SV2_decor("ChiSquared_SV2");
124 SG::Decorator<float> chi2_nc_SV2_decor("ChiSquared_nc_SV2");
125 SG::Decorator<float> chi2_V2_decor("ChiSquared_V2");
126 SG::Decorator<int> ndof_V2_decor("nDoF_V2");
127 SG::Decorator<float> lxy_SV2_decor("lxy_SV2");
128 SG::Decorator<float> lxyErr_SV2_decor("lxyErr_SV2");
129 SG::Decorator<float> a0xy_SV2_decor("a0xy_SV2");
130 SG::Decorator<float> a0xyErr_SV2_decor("a0xyErr_SV2");
131 SG::Decorator<float> a0z_SV2_decor("a0z_SV2");
132 SG::Decorator<float> a0zErr_SV2_decor("a0zErr_SV2");
133
134 // Get the containers and identify the input Jpsi and Psi
136 ATH_CHECK( psiContainer.isValid() );
138 ATH_CHECK( jpsiContainer.isValid() );
139
140 for(size_t ic=0; ic<cascadeinfoContainer.size(); ic++) {
141 Trk::VxCascadeInfo* cascade_info = cascadeinfoContainer[ic];
142 if(cascade_info==nullptr) {
143 ATH_MSG_ERROR("CascadeInfo is null");
144 continue;
145 }
146
147 Trk::VxCascadeInfo* cascade_info_noConstr = cascadeinfoContainer_noConstr[ic];
148
149 const std::vector<xAOD::Vertex*> &cascadeVertices = cascade_info->vertices();
150 if(cascadeVertices.size() != topoN) ATH_MSG_ERROR("Incorrect number of vertices");
151 if(cascadeVertices[0]==nullptr || cascadeVertices[1]==nullptr || cascadeVertices[2]==nullptr) ATH_MSG_ERROR("Error null vertex");
152 // Keep vertices
153 for(int i=0; i<topoN; i++) VtxWriteHandles[i].ptr()->push_back(cascadeVertices[i]);
154
155 cascade_info->setSVOwnership(false); // Prevent Container from deleting vertices
156 const auto mainVertex = cascadeVertices[2]; // this is the mother vertex
157 const std::vector< std::vector<TLorentzVector> > &moms = cascade_info->getParticleMoms();
158
159 // Set links to cascade vertices
160 std::vector<VertexLink> precedingVertexLinks;
161 VertexLink vertexLink1;
162 vertexLink1.setElement(cascadeVertices[0]);
163 vertexLink1.setStorableObject(*VtxWriteHandles[0].ptr(), ctx);
164 if( vertexLink1.isValid() ) precedingVertexLinks.push_back( vertexLink1 );
165 VertexLink vertexLink2;
166 vertexLink2.setElement(cascadeVertices[1]);
167 vertexLink2.setStorableObject(*VtxWriteHandles[1].ptr(), ctx);
168 if( vertexLink2.isValid() ) precedingVertexLinks.push_back( vertexLink2 );
169 CascadeLinksDecor(*mainVertex) = std::move(precedingVertexLinks);
170
171 // Identify the input Jpsi
172 const xAOD::Vertex* jpsiVertex = BPhysPVCascadeTools::FindVertex<2>(jpsiContainer.cptr(), cascadeVertices[1]);
173 // Identify the input Psi
174 const xAOD::Vertex* psiVertex(0);
175 if(m_vtx1Daug_num==4) psiVertex = BPhysPVCascadeTools::FindVertex<4>(psiContainer.cptr(), cascadeVertices[0]);
176 else psiVertex = BPhysPVCascadeTools::FindVertex<3>(psiContainer.cptr(), cascadeVertices[0]);
177
178 // Set links to input vertices
179 std::vector<const xAOD::Vertex*> jpsiVerticestoLink;
180 if(jpsiVertex) jpsiVerticestoLink.push_back(jpsiVertex);
181 else ATH_MSG_WARNING("Could not find linking Jpsi");
182 if(!BPhysPVCascadeTools::LinkVertices(JpsiLinksDecor, jpsiVerticestoLink, jpsiContainer.cptr(), mainVertex)) ATH_MSG_ERROR("Error decorating with Jpsi vertex");
183
184 std::vector<const xAOD::Vertex*> psiVerticestoLink;
185 if(psiVertex) psiVerticestoLink.push_back(psiVertex);
186 else ATH_MSG_WARNING("Could not find linking Psi");
187 if(!BPhysPVCascadeTools::LinkVertices(PsiLinksDecor, psiVerticestoLink, psiContainer.cptr(), mainVertex)) ATH_MSG_ERROR("Error decorating with Psi vertex");
188
189 xAOD::BPhysHypoHelper vtx(m_hypoName, mainVertex);
190
191 // Get refitted track momenta from all vertices, charged tracks only
192 BPhysPVCascadeTools::SetVectorInfo(vtx, cascade_info);
193 vtx.setPass(true);
194
195 //
196 // Decorate main vertex
197 //
198 // mass, mass error
199 // https://gitlab.cern.ch/atlas/athena/-/blob/21.2/Tracking/TrkVertexFitter/TrkVKalVrtFitter/TrkVKalVrtFitter/VxCascadeInfo.h
200 BPHYS_CHECK( vtx.setMass(m_CascadeTools->invariantMass(moms[2])) );
201 BPHYS_CHECK( vtx.setMassErr(m_CascadeTools->invariantMassError(moms[2],cascade_info->getCovariance()[2])) );
202 // pt and pT error (the default pt of mainVertex is != the pt of the full cascade fit!)
203 Pt_decor(*mainVertex) = m_CascadeTools->pT(moms[2]);
204 PtErr_decor(*mainVertex) = m_CascadeTools->pTError(moms[2],cascade_info->getCovariance()[2]);
205 // chi2 and ndof (the default chi2 of mainVertex is != the chi2 of the full cascade fit!)
206 chi2_decor(*mainVertex) = cascade_info->fitChi2();
207 ndof_decor(*mainVertex) = cascade_info->nDoF();
208 chi2_nc_decor(*mainVertex) = cascade_info_noConstr ? cascade_info_noConstr->fitChi2() : -999999.;
209 ndof_nc_decor(*mainVertex) = cascade_info_noConstr ? cascade_info_noConstr->nDoF() : -1;
210
211 // decorate the Psi vertex
212 chi2_SV1_decor(*cascadeVertices[0]) = m_V0Tools->chisq(cascadeVertices[0]);
213 chi2_nc_SV1_decor(*cascadeVertices[0]) = cascade_info_noConstr ? m_V0Tools->chisq(cascade_info_noConstr->vertices()[0]) : -999999.;
214 //
215 auto chisq = psiVertex ? m_V0Tools->chisq(psiVertex) : 0.;
216 auto ndof = psiVertex ? m_V0Tools->ndof(psiVertex) : 0.;
217 chi2_V1_decor(*cascadeVertices[0]) = chisq;
218 ndof_V1_decor(*cascadeVertices[0]) = ndof;
219 //
220 lxy_SV1_decor(*cascadeVertices[0]) = m_CascadeTools->lxy(moms[0],cascadeVertices[0],mainVertex);
221 lxyErr_SV1_decor(*cascadeVertices[0]) = m_CascadeTools->lxyError(moms[0],cascade_info->getCovariance()[0],cascadeVertices[0],mainVertex);
222 a0z_SV1_decor(*cascadeVertices[0]) = m_CascadeTools->a0z(moms[0],cascadeVertices[0],mainVertex);
223 a0zErr_SV1_decor(*cascadeVertices[0]) = m_CascadeTools->a0zError(moms[0],cascade_info->getCovariance()[0],cascadeVertices[0],mainVertex);
224 a0xy_SV1_decor(*cascadeVertices[0]) = m_CascadeTools->a0xy(moms[0],cascadeVertices[0],mainVertex);
225 a0xyErr_SV1_decor(*cascadeVertices[0]) = m_CascadeTools->a0xyError(moms[0],cascade_info->getCovariance()[0],cascadeVertices[0],mainVertex);
226
227 // decorate the Jpsi vertex
228 chi2_SV2_decor(*cascadeVertices[1]) = m_V0Tools->chisq(cascadeVertices[1]);
229 chi2_nc_SV2_decor(*cascadeVertices[1]) = cascade_info_noConstr ? m_V0Tools->chisq(cascade_info_noConstr->vertices()[1]) : -999999.;
230 //
231 chisq = jpsiVertex ? m_V0Tools->chisq(jpsiVertex) : 0.;
232 ndof = jpsiVertex ? m_V0Tools->chisq(jpsiVertex) : 0.;
233 chi2_V2_decor(*cascadeVertices[1]) = chisq;
234 ndof_V2_decor(*cascadeVertices[1]) = ndof;
235 //
236 lxy_SV2_decor(*cascadeVertices[1]) = m_CascadeTools->lxy(moms[1],cascadeVertices[1],mainVertex);
237 lxyErr_SV2_decor(*cascadeVertices[1]) = m_CascadeTools->lxyError(moms[1],cascade_info->getCovariance()[1],cascadeVertices[1],mainVertex);
238 a0z_SV2_decor(*cascadeVertices[1]) = m_CascadeTools->a0z(moms[1],cascadeVertices[1],mainVertex);
239 a0zErr_SV2_decor(*cascadeVertices[1]) = m_CascadeTools->a0zError(moms[1],cascade_info->getCovariance()[1],cascadeVertices[1],mainVertex);
240 a0xy_SV2_decor(*cascadeVertices[1]) = m_CascadeTools->a0xy(moms[1],cascadeVertices[1],mainVertex);
241 a0xyErr_SV2_decor(*cascadeVertices[1]) = m_CascadeTools->a0xyError(moms[1],cascade_info->getCovariance()[1],cascadeVertices[1],mainVertex);
242
243 double Mass_Moth = m_CascadeTools->invariantMass(moms[2]); //size=2
244 ATH_CHECK(helper.FillCandwithRefittedVertices(m_refitPV, pvContainer.cptr(), m_refitPV ? refPvContainer.ptr() : 0, &(*m_pvRefitter), m_PV_max, m_DoVertexType, cascade_info, 2, Mass_Moth, vtx));
245 } // loop over cascadeinfoContainer
246
247 // Deleting cascadeinfo since this won't be stored.
248 // Vertices have been kept in m_cascadeOutputs and should be owned by their container
249 for (auto cascade_info : cascadeinfoContainer) delete cascade_info;
250 for (auto cascade_info_noConstr : cascadeinfoContainer_noConstr) delete cascade_info_noConstr;
251
252 return StatusCode::SUCCESS;
253 }
254
255 JpsiPlusPsiCascade::JpsiPlusPsiCascade(const std::string& type, const std::string& name, const IInterface* parent) : base_class(type,name,parent),
258 m_cascadeOutputsKeys({"JpsiPlusPsiCascadeVtx1", "JpsiPlusPsiCascadeVtx2", "JpsiPlusPsiCascadeVtx3"}),
259 m_VxPrimaryCandidateName("PrimaryVertices"),
260 m_trackContainerName("InDetTrackParticles"),
261 m_eventInfo_key("EventInfo"),
262 m_jpsiMassLower(0.0),
263 m_jpsiMassUpper(20000.0),
264 m_diTrackMassLower(-1.0),
265 m_diTrackMassUpper(-1.0),
266 m_psiMassLower(0.0),
267 m_psiMassUpper(25000.0),
268 m_jpsi2MassLower(0.0),
269 m_jpsi2MassUpper(20000.0),
270 m_MassLower(0.0),
271 m_MassUpper(31000.0),
272 m_vtx1Daug_num(4),
273 m_vtx1Daug1MassHypo(-1),
274 m_vtx1Daug2MassHypo(-1),
275 m_vtx1Daug3MassHypo(-1),
276 m_vtx1Daug4MassHypo(-1),
277 m_vtx2Daug1MassHypo(-1),
278 m_vtx2Daug2MassHypo(-1),
279 m_mass_jpsi(-1),
280 m_mass_diTrk(-1),
281 m_mass_psi(-1),
282 m_mass_jpsi2(-1),
283 m_constrPsi(false),
284 m_constrJpsi(false),
285 m_constrDiTrk(false),
286 m_constrJpsi2(false),
287 m_chi2cut_Psi(-1.0),
288 m_chi2cut_Jpsi(-1.0),
289 m_chi2cut(-1.0),
290 m_maxCandidates(0),
291 m_iVertexFitter("Trk::TrkVKalVrtFitter"),
292 m_pvRefitter("Analysis::PrimaryVertexRefitter"),
293 m_V0Tools("Trk::V0Tools"),
294 m_CascadeTools("DerivationFramework::CascadeTools")
295 {
296 declareProperty("JpsiVertices", m_vertexContainerKey);
297 declareProperty("PsiVertices", m_vertexPsiContainerKey);
298 declareProperty("JpsiVtxHypoNames", m_vertexJpsiHypoNames);
299 declareProperty("PsiVtxHypoNames", m_vertexPsiHypoNames);
300 declareProperty("VxPrimaryCandidateName", m_VxPrimaryCandidateName);
301 declareProperty("TrackContainerName", m_trackContainerName);
302 declareProperty("RefPVContainerName", m_refPVContainerName = "RefittedPrimaryVertices");
303 declareProperty("JpsiMassLowerCut", m_jpsiMassLower);
304 declareProperty("JpsiMassUpperCut", m_jpsiMassUpper);
305 declareProperty("DiTrackMassLower", m_diTrackMassLower); // only effective when m_vtx1Daug_num=4
306 declareProperty("DiTrackMassUpper", m_diTrackMassUpper); // only effective when m_vtx1Daug_num=4
307 declareProperty("PsiMassLowerCut", m_psiMassLower);
308 declareProperty("PsiMassUpperCut", m_psiMassUpper);
309 declareProperty("Jpsi2MassLowerCut", m_jpsi2MassLower);
310 declareProperty("Jpsi2MassUpperCut", m_jpsi2MassUpper);
311 declareProperty("MassLowerCut", m_MassLower);
312 declareProperty("MassUpperCut", m_MassUpper);
313 declareProperty("HypothesisName", m_hypoName = "TQ");
314 declareProperty("NumberOfPsiDaughters", m_vtx1Daug_num); // 3 or 4 only
315 declareProperty("Vtx1Daug1MassHypo", m_vtx1Daug1MassHypo);
316 declareProperty("Vtx1Daug2MassHypo", m_vtx1Daug2MassHypo);
317 declareProperty("Vtx1Daug3MassHypo", m_vtx1Daug3MassHypo);
318 declareProperty("Vtx1Daug4MassHypo", m_vtx1Daug4MassHypo);
319 declareProperty("Vtx2Daug1MassHypo", m_vtx2Daug1MassHypo);
320 declareProperty("Vtx2Daug2MassHypo", m_vtx2Daug2MassHypo);
321 declareProperty("JpsiMass", m_mass_jpsi);
322 declareProperty("DiTrackMass", m_mass_diTrk);
323 declareProperty("PsiMass", m_mass_psi);
324 declareProperty("Jpsi2Mass", m_mass_jpsi2);
325 declareProperty("ApplyJpsiMassConstraint", m_constrJpsi);
326 declareProperty("ApplyDiTrackMassConstraint", m_constrDiTrk); // only effective when m_vtx1Daug_num=4
327 declareProperty("ApplyPsiMassConstraint", m_constrPsi);
328 declareProperty("ApplyJpsi2MassConstraint", m_constrJpsi2);
329 declareProperty("Chi2CutPsi", m_chi2cut_Psi);
330 declareProperty("Chi2CutJpsi", m_chi2cut_Jpsi);
331 declareProperty("Chi2Cut", m_chi2cut);
332 declareProperty("MaxCandidates", m_maxCandidates);
333 declareProperty("RefitPV", m_refitPV = true);
334 declareProperty("MaxnPV", m_PV_max = 1000);
335 declareProperty("MinNTracksInPV", m_PV_minNTracks = 0);
336 declareProperty("DoVertexType", m_DoVertexType = 7);
337 declareProperty("TrkVertexFitterTool", m_iVertexFitter);
338 declareProperty("PVRefitter", m_pvRefitter);
339 declareProperty("V0Tools", m_V0Tools);
340 declareProperty("CascadeTools", m_CascadeTools);
341 declareProperty("CascadeVertexCollections", m_cascadeOutputsKeys);
342 }
343
344 StatusCode JpsiPlusPsiCascade::performSearch(std::vector<Trk::VxCascadeInfo*> *cascadeinfoContainer, std::vector<Trk::VxCascadeInfo*> *cascadeinfoContainer_noConstr, const EventContext& ctx) const {
345 ATH_MSG_DEBUG( "JpsiPlusPsiCascade::performSearch" );
346 assert(cascadeinfoContainer!=nullptr && cascadeinfoContainer_noConstr!=nullptr);
347
348 // Get TrackParticle container (for setting links to the original tracks)
350 ATH_CHECK( trackContainer.isValid() );
351
352 std::vector<const xAOD::TrackParticle*> tracksJpsi;
353 std::vector<const xAOD::TrackParticle*> tracksDiTrk;
354 std::vector<const xAOD::TrackParticle*> tracksPsi;
355 std::vector<const xAOD::TrackParticle*> tracksJpsi2;
356 std::vector<double> massesPsi;
357 massesPsi.push_back(m_vtx1Daug1MassHypo);
358 massesPsi.push_back(m_vtx1Daug2MassHypo);
359 massesPsi.push_back(m_vtx1Daug3MassHypo);
360 if(m_vtx1Daug_num==4) massesPsi.push_back(m_vtx1Daug4MassHypo);
361 std::array<double,2> massesJpsi2{m_vtx2Daug1MassHypo, m_vtx2Daug2MassHypo};
362
363 // Get Psi container
365 ATH_CHECK( psiContainer.isValid() );
366
367 // Get Jpsi container
369 ATH_CHECK( jpsiContainer.isValid() );
370
371 // Select the J/psi candidates before calling cascade fit
372 std::vector<const xAOD::Vertex*> selectedJpsiCandidates;
373 for(auto vxcItr=jpsiContainer.cptr()->cbegin(); vxcItr!=jpsiContainer.cptr()->cend(); ++vxcItr) {
374 // Check the passed flag first
375 const xAOD::Vertex* vtx = *vxcItr;
376 bool passed = false;
377 for(size_t i=0; i<m_vertexJpsiHypoNames.size(); i++) {
378 SG::Accessor<Char_t> flagAcc("passed_"+m_vertexJpsiHypoNames[i]);
379 if(flagAcc.isAvailable(*vtx) && flagAcc(*vtx)) {
380 passed |= 1;
381 }
382 }
383 if(m_vertexJpsiHypoNames.size() && !passed) continue;
384
385 // Check Jpsi candidate invariant mass and skip if need be
386 double mass_jpsi2 = m_V0Tools->invariantMass(*vxcItr, massesJpsi2);
387 if (mass_jpsi2 < m_jpsi2MassLower || mass_jpsi2 > m_jpsi2MassUpper) continue;
388
389 double chi2DOF = (*vxcItr)->chiSquared()/(*vxcItr)->numberDoF();
390 if(m_chi2cut_Jpsi>0 && chi2DOF>m_chi2cut_Jpsi) continue;
391
392 selectedJpsiCandidates.push_back(*vxcItr);
393 }
394 if(selectedJpsiCandidates.size()==0) return StatusCode::SUCCESS;
395
396 // Select the Psi candidates before calling cascade fit
397 std::vector<const xAOD::Vertex*> selectedPsiCandidates;
398 for(auto vxcItr=psiContainer.cptr()->cbegin(); vxcItr!=psiContainer.cptr()->cend(); ++vxcItr) {
399 // Check the passed flag first
400 const xAOD::Vertex* vtx = *vxcItr;
401 bool passed = false;
402 for(size_t i=0; i<m_vertexPsiHypoNames.size(); i++) {
403 SG::Accessor<Char_t> flagAcc("passed_"+m_vertexPsiHypoNames[i]);
404 if(flagAcc.isAvailable(*vtx) && flagAcc(*vtx)) {
405 passed |= 1;
406 }
407 }
408 if(m_vertexPsiHypoNames.size() && !passed) continue;
409
410 // Check Psi candidate invariant mass and skip if need be
411 double mass_psi = m_V0Tools->invariantMass(*vxcItr,massesPsi);
412 if(mass_psi < m_psiMassLower || mass_psi > m_psiMassUpper) continue;
413
414 // Add loose cut on Jpsi mass from Psi -> Jpsi pi+ pi-, or on phi mass from Ds+ -> phi pi+
415 TLorentzVector p4_mu1, p4_mu2;
416 p4_mu1.SetPtEtaPhiM( (*vxcItr)->trackParticle(0)->pt(),
417 (*vxcItr)->trackParticle(0)->eta(),
418 (*vxcItr)->trackParticle(0)->phi(), m_vtx1Daug1MassHypo);
419 p4_mu2.SetPtEtaPhiM( (*vxcItr)->trackParticle(1)->pt(),
420 (*vxcItr)->trackParticle(1)->eta(),
421 (*vxcItr)->trackParticle(1)->phi(), m_vtx1Daug2MassHypo);
422 double mass_jpsi = (p4_mu1 + p4_mu2).M();
423 if (mass_jpsi < m_jpsiMassLower || mass_jpsi > m_jpsiMassUpper) continue;
424
426 TLorentzVector p4_trk1, p4_trk2;
427 p4_trk1.SetPtEtaPhiM( (*vxcItr)->trackParticle(2)->pt(),
428 (*vxcItr)->trackParticle(2)->eta(),
429 (*vxcItr)->trackParticle(2)->phi(), m_vtx1Daug3MassHypo);
430 p4_trk2.SetPtEtaPhiM( (*vxcItr)->trackParticle(3)->pt(),
431 (*vxcItr)->trackParticle(3)->eta(),
432 (*vxcItr)->trackParticle(3)->phi(), m_vtx1Daug4MassHypo);
433 double mass_diTrk = (p4_trk1 + p4_trk2).M();
434 if (mass_diTrk < m_diTrackMassLower || mass_diTrk > m_diTrackMassUpper) continue;
435 }
436
437 double chi2DOF = (*vxcItr)->chiSquared()/(*vxcItr)->numberDoF();
438 if(m_chi2cut_Psi>0 && chi2DOF>m_chi2cut_Psi) continue;
439
440 selectedPsiCandidates.push_back(*vxcItr);
441 }
442 if(selectedPsiCandidates.size()==0) return StatusCode::SUCCESS;
443
444 std::vector<std::pair<const xAOD::Vertex*, const xAOD::Vertex*> > candidatePairs;
445 for(auto jpsiItr=selectedJpsiCandidates.cbegin(); jpsiItr!=selectedJpsiCandidates.cend(); ++jpsiItr) {
446 tracksJpsi2.clear();
447 for(size_t i=0; i<(*jpsiItr)->nTrackParticles(); i++) tracksJpsi2.push_back((*jpsiItr)->trackParticle(i));
448 for(auto psiItr=selectedPsiCandidates.cbegin(); psiItr!=selectedPsiCandidates.cend(); ++psiItr) {
449 bool skip = false;
450 for(size_t j=0; j<(*psiItr)->nTrackParticles(); j++) {
451 if(std::find(tracksJpsi2.cbegin(), tracksJpsi2.cend(), (*psiItr)->trackParticle(j)) != tracksJpsi2.cend()) { skip = true; break; }
452 }
453 if(skip) continue;
454 candidatePairs.push_back(std::pair<const xAOD::Vertex*, const xAOD::Vertex*>(*jpsiItr,*psiItr));
455 }
456 }
457
458 std::sort( candidatePairs.begin(), candidatePairs.end(), [](std::pair<const xAOD::Vertex*, const xAOD::Vertex*> a, std::pair<const xAOD::Vertex*, const xAOD::Vertex*> b) { return a.first->chiSquared()/a.first->numberDoF()+a.second->chiSquared()/a.second->numberDoF() < b.first->chiSquared()/b.first->numberDoF()+b.second->chiSquared()/b.second->numberDoF(); } );
459 if(m_maxCandidates>0 && candidatePairs.size()>m_maxCandidates) {
460 candidatePairs.erase(candidatePairs.begin()+m_maxCandidates, candidatePairs.end());
461 }
462
463 for(size_t ic=0; ic<candidatePairs.size(); ic++) {
464 const xAOD::Vertex* jpsiVertex = candidatePairs[ic].first;
465 const xAOD::Vertex* psiVertex = candidatePairs[ic].second;
466
467 tracksJpsi2.clear();
468 for(size_t it=0; it<jpsiVertex->nTrackParticles(); it++) tracksJpsi2.push_back(jpsiVertex->trackParticle(it));
469 if (tracksJpsi2.size() != 2 || massesJpsi2.size() != 2) {
470 ATH_MSG_ERROR("Problems with Jpsi input: number of tracks or track mass inputs is not 2!");
471 }
472 tracksPsi.clear();
473 for(size_t it=0; it<psiVertex->nTrackParticles(); it++) tracksPsi.push_back(psiVertex->trackParticle(it));
474 if (tracksPsi.size() != massesPsi.size()) {
475 ATH_MSG_ERROR("Problems with Psi input: number of tracks or track mass inputs is not correct!");
476 }
477
478 tracksJpsi.clear();
479 tracksJpsi.push_back(psiVertex->trackParticle(0));
480 tracksJpsi.push_back(psiVertex->trackParticle(1));
481 tracksDiTrk.clear();
482 if(m_vtx1Daug_num==4) {
483 tracksDiTrk.push_back(psiVertex->trackParticle(2));
484 tracksDiTrk.push_back(psiVertex->trackParticle(3));
485 }
486
487 TLorentzVector p4_moth;
488 TLorentzVector tmp;
489 for(size_t it=0; it<jpsiVertex->nTrackParticles(); it++) {
490 tmp.SetPtEtaPhiM(jpsiVertex->trackParticle(it)->pt(),jpsiVertex->trackParticle(it)->eta(),jpsiVertex->trackParticle(it)->phi(),massesJpsi2[it]);
491 p4_moth += tmp;
492 }
493 for(size_t it=0; it<psiVertex->nTrackParticles(); it++) {
494 tmp.SetPtEtaPhiM(psiVertex->trackParticle(it)->pt(),psiVertex->trackParticle(it)->eta(),psiVertex->trackParticle(it)->phi(),massesPsi[it]);
495 p4_moth += tmp;
496 }
497 if (p4_moth.M() < m_MassLower || p4_moth.M() > m_MassUpper) continue;
498
499 // Apply the user's settings to the fitter
500 std::unique_ptr<Trk::IVKalState> state = m_iVertexFitter->makeState(ctx);
501 // Robustness: http://cdsweb.cern.ch/record/685551
502 int robustness = 0;
503 m_iVertexFitter->setRobustness(robustness, *state);
504 // Build up the topology
505 // Vertex list
506 std::vector<Trk::VertexID> vrtList;
507 // Psi vertex
508 Trk::VertexID vID1;
509 // https://gitlab.cern.ch/atlas/athena/-/blob/21.2/Tracking/TrkVertexFitter/TrkVKalVrtFitter/TrkVKalVrtFitter/IVertexCascadeFitter.h
510 if (m_constrPsi) {
511 vID1 = m_iVertexFitter->startVertex(tracksPsi,massesPsi,*state,m_mass_psi);
512 } else {
513 vID1 = m_iVertexFitter->startVertex(tracksPsi,massesPsi,*state);
514 }
515 vrtList.push_back(vID1);
516 // Jpsi vertex
517 Trk::VertexID vID2;
518 if (m_constrJpsi2) {
519 vID2 = m_iVertexFitter->nextVertex(tracksJpsi2,massesJpsi2,*state,m_mass_jpsi2);
520 } else {
521 vID2 = m_iVertexFitter->nextVertex(tracksJpsi2,massesJpsi2,*state);
522 }
523 vrtList.push_back(vID2);
524 // Mother vertex including Jpsi and Psi
525 std::vector<const xAOD::TrackParticle*> tp; tp.clear();
526 std::vector<double> tp_masses; tp_masses.clear();
527 m_iVertexFitter->nextVertex(tp,tp_masses,vrtList,*state);
528 if (m_constrJpsi) {
529 std::vector<Trk::VertexID> cnstV; cnstV.clear();
530 if ( !m_iVertexFitter->addMassConstraint(vID1,tracksJpsi,cnstV,*state,m_mass_jpsi).isSuccess() ) {
531 ATH_MSG_WARNING("addMassConstraint for Jpsi failed");
532 }
533 }
535 std::vector<Trk::VertexID> cnstV; cnstV.clear();
536 if ( !m_iVertexFitter->addMassConstraint(vID1,tracksDiTrk,cnstV,*state,m_mass_diTrk).isSuccess() ) {
537 ATH_MSG_WARNING("addMassConstraint for DiTrk failed");
538 }
539 }
540 // Do the work
541 std::unique_ptr<Trk::VxCascadeInfo> result(m_iVertexFitter->fitCascade(*state));
542
543 bool pass = false;
544 if (result != nullptr) {
545 for(auto v : result->vertices()) {
546 if(v->nTrackParticles()==0) {
547 std::vector<ElementLink<xAOD::TrackParticleContainer> > nullLinkVector;
548 v->setTrackParticleLinks(nullLinkVector);
549 }
550 }
551 // reset links to original tracks
552 BPhysPVCascadeTools::PrepareVertexLinks(result.get(), trackContainer.cptr());
553
554 // necessary to prevent memory leak
555 result->setSVOwnership(true);
556
557 // Chi2/DOF cut
558 double chi2DOF = result->fitChi2()/result->nDoF();
559 bool chi2CutPassed = (m_chi2cut <= 0.0 || chi2DOF < m_chi2cut);
560
561 if(chi2CutPassed) {
562 cascadeinfoContainer->push_back(result.release());
563 pass = true;
564 }
565 }
566
567 // do cascade fit again without any mass constraints
568 if(pass) {
570 std::unique_ptr<Trk::IVKalState> state (m_iVertexFitter->makeState(ctx));
571 m_iVertexFitter->setRobustness(robustness, *state);
572 std::vector<Trk::VertexID> vrtList_nc;
573 // Psi vertex
574 Trk::VertexID vID1_nc = m_iVertexFitter->startVertex(tracksPsi,massesPsi,*state);
575 vrtList_nc.push_back(vID1_nc);
576 Trk::VertexID vID2_nc = m_iVertexFitter->nextVertex(tracksJpsi2,massesJpsi2,*state);
577 vrtList_nc.push_back(vID2_nc);
578 // Mother vertex including Jpsi and Psi
579 std::vector<const xAOD::TrackParticle*> tp; tp.clear();
580 std::vector<double> tp_masses; tp_masses.clear();
581 m_iVertexFitter->nextVertex(tp,tp_masses,vrtList_nc,*state);
582 // Do the work
583 std::unique_ptr<Trk::VxCascadeInfo> result_nc(m_iVertexFitter->fitCascade(*state));
584
585 if (result_nc != nullptr) {
586 for(auto v : result_nc->vertices()) {
587 if(v->nTrackParticles()==0) {
588 std::vector<ElementLink<xAOD::TrackParticleContainer> > nullLinkVector;
589 v->setTrackParticleLinks(nullLinkVector);
590 }
591 }
592 // reset links to original tracks
593 BPhysPVCascadeTools::PrepareVertexLinks(result_nc.get(), trackContainer.cptr());
594
595 // necessary to prevent memory leak
596 result_nc->setSVOwnership(true);
597 cascadeinfoContainer_noConstr->push_back(result_nc.release());
598 }
599 else cascadeinfoContainer_noConstr->push_back(0);
600 }
601 else cascadeinfoContainer_noConstr->push_back(0);
602 }
603 } //Iterate over candidatePairs
604
605 return StatusCode::SUCCESS;
606 }
607}
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_DEBUG(x,...)
#define ATH_MSG_ERROR(x,...)
#define ATH_MSG_WARNING(x,...)
#define ATH_MSG_FATAL(x,...)
#define BPHYS_CHECK(EXP)
Useful CHECK macro.
: B-physics xAOD helpers.
ATLAS-specific HepMC functions.
bool passed(DecisionID id, const DecisionIDContainer &)
checks if required decision ID is in the set of IDs in the container
static Double_t a
static void PrepareVertexLinks(Trk::VxCascadeInfo *result, const xAOD::TrackParticleContainer *importedTrackCollection)
static const xAOD::Vertex * FindVertex(const xAOD::VertexContainer *c, const xAOD::Vertex *v)
static void SetVectorInfo(xAOD::BPhysHelper &, const Trk::VxCascadeInfo *)
static bool LinkVertices(SG::Decorator< VertexLinkVector > &decor, const std::vector< const xAOD::Vertex * > &vertices, const xAOD::VertexContainer *vertexContainer, const xAOD::Vertex *vert)
SG::WriteHandleKeyArray< xAOD::VertexContainer > m_cascadeOutputsKeys
SG::WriteHandleKey< xAOD::VertexContainer > m_refPVContainerName
virtual StatusCode initialize() override
PublicToolHandle< DerivationFramework::CascadeTools > m_CascadeTools
StatusCode performSearch(std::vector< Trk::VxCascadeInfo * > *cascadeinfoContainer, std::vector< Trk::VxCascadeInfo * > *cascadeinfoContainer_noConstr, const EventContext &ctx) const
SG::ReadHandleKey< xAOD::VertexContainer > m_vertexPsiContainerKey
std::vector< std::string > m_vertexJpsiHypoNames
SG::ReadHandleKey< xAOD::TrackParticleContainer > m_trackContainerName
PublicToolHandle< Analysis::PrimaryVertexRefitter > m_pvRefitter
SG::ReadHandleKey< xAOD::VertexContainer > m_VxPrimaryCandidateName
Name of primary vertex container.
PublicToolHandle< Trk::V0Tools > m_V0Tools
JpsiPlusPsiCascade(const std::string &t, const std::string &n, const IInterface *p)
virtual StatusCode addBranches(const EventContext &ctx) const override
SG::ReadHandleKey< xAOD::VertexContainer > m_vertexContainerKey
ToolHandle< Trk::TrkVKalVrtFitter > m_iVertexFitter
std::vector< std::string > m_vertexPsiHypoNames
SG::ReadHandleKey< xAOD::EventInfo > m_eventInfo_key
Helper class to provide type-safe access to aux data.
bool isAvailable(const ELT &e) const
Test to see if this variable exists in the store.
virtual bool isValid() override final
Can the handle be successfully dereferenced?
const_pointer_type cptr()
Dereference the pointer.
Property holding a SG store/key/clid from which a WriteHandle is made.
StatusCode record(std::unique_ptr< T > data)
Record a const object to the store.
pointer_type ptr()
Dereference the pointer.
double fitChi2() const
const std::vector< Amg::MatrixX > & getCovariance() const
const std::vector< std::vector< TLorentzVector > > & getParticleMoms() const
const std::vector< xAOD::Vertex * > & vertices() const
void setSVOwnership(bool Ownership)
bool setMass(const float val)
Set given invariant mass and its error.
bool setPass(bool passVal)
get the pass flag for this hypothesis
bool setMassErr(const float val)
invariant mass error
virtual double phi() const override final
The azimuthal angle ( ) of the particle (has range to .).
virtual double pt() const override final
The transverse momentum ( ) of the particle.
virtual double eta() const override final
The pseudorapidity ( ) of the particle.
size_t nTrackParticles() const
Get the number of tracks associated with this vertex.
const TrackParticle * trackParticle(size_t i) const
Get the pointer to a given track that was used in vertex reco.
THE reconstruction tool.
ElementLink< xAOD::VertexContainer > VertexLink
std::vector< VertexLink > VertexLinkVector
static const int PSI2S
static const int MUON
static const int PIPLUS
static const int JPSI
SG::Decorator< T, ALLOC > Decorator
Helper class to provide type-safe access to aux data, specialized for JaggedVecElt.
Definition AuxElement.h:576
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.
Vertex_v1 Vertex
Define the latest version of the vertex class.