28 #include "Math/GenVector/VectorUtil.h"
29 #include "Math/Vector2D.h"
36 using ROOT::Math::XYVector;
75 if (
item.second.size() > 1) {
76 for (
size_t i = 0;
i <
item.second.size();
i++) {
93 ATH_MSG_DEBUG(
"No GenericMonitoringTool configured: no monitoring histograms will be available" );
96 return StatusCode::SUCCESS;
106 ATH_CHECK( previousDecisionsHandle.isValid() );
107 ATH_MSG_DEBUG(
"Running with " << previousDecisionsHandle->size() <<
" previous decisions" );
112 ATH_CHECK( trigBphysHandle.record(std::make_unique<xAOD::TrigBphysContainer>(),
113 std::make_unique<xAOD::TrigBphysAuxContainer>()) );
118 auto state = std::make_unique<TrigBmumuxState>(context, *previousDecisionsHandle, *outputDecisionsHandle, trigBphysHandle.
ptr(), *beamSpotHandle);
123 if (!state->dimuons.empty()) {
129 ATH_MSG_DEBUG(
"TrigBmumuxComboHypo::execute() terminates with StatusCode::SUCCESS" );
130 return StatusCode::SUCCESS;
136 auto& muons = state.
muons;
144 if (!
muon->trackParticle(xAOD::Muon::TrackParticleType::CombinedTrackParticle))
continue;
147 auto itr = std::find_if(muons.begin(), muons.end(), [
this,
muon](
const auto&
x){ return isIdenticalTracks(muon, *x.link); });
148 if (itr == muons.end()) {
149 muons.push_back({muonEL, std::vector<ElementLink<DecisionContainer>>(1, decisionEL),
DecisionIDContainer()});
152 (*itr).decisionLinks.push_back(decisionEL);
158 std::sort(muons.begin(), muons.end(), [](
const auto& lhs,
const auto& rhs){ return ((*lhs.link)->pt() > (*rhs.link)->pt()); });
161 for (
auto&
item : muons) {
168 ATH_MSG_DEBUG(
"Dump found muons before vertex fit: " << muons.size() <<
" candidates" );
169 for (
const auto&
item : muons) {
181 return StatusCode::SUCCESS;
187 auto& tracks = state.
tracks;
190 size_t viewCounter = 0;
194 auto view = *viewLinkInfo.link;
198 const auto roi = *roiLinkInfo.link;
204 std::vector<ElementLink<xAOD::TrackParticleContainer>> tracksFromView;
205 tracksFromView.reserve(tracksHandle->size());
206 for (
size_t idx = 0;
idx < tracksHandle->size(); ++
idx) {
207 tracksFromView.emplace_back(ViewHelper::makeLink<xAOD::TrackParticleContainer>(
view, tracksHandle,
idx));
210 for (
const auto& trackEL : tracksFromView) {
212 if (
track->definingParametersCovMatrixVec().empty())
continue;
214 if (viewCounter == 0 ||
215 std::find_if(tracks.begin(), tracks.end(),
216 [
this,
track](
const auto&
x){ return isIdenticalTracks(track, *x); }) == tracks.end()) {
217 tracks.emplace_back(trackEL);
221 if (roi->composite()) {
226 std::sort(tracks.begin(), tracks.end(), [](
const auto& lhs,
const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); });
229 ATH_MSG_DEBUG(
"Dump found tracks before vertex fit: " << tracks.size() <<
" candidates" );
230 for (
const auto& trackEL : tracks) {
235 return StatusCode::SUCCESS;
244 const auto& muons = state.
muons;
246 std::vector<const DecisionIDContainer*> previousDecisionIDs(2,
nullptr);
248 for (
size_t itrk1 = 0; itrk1 < muons.size(); ++itrk1) {
251 previousDecisionIDs[0] = &muons[itrk1].decisionIDs;
255 auto charge1 = trk1->
charge();
257 for (
size_t itrk2 = itrk1 + 1; itrk2 < muons.size(); ++itrk2) {
260 previousDecisionIDs[1] = &muons[itrk2].decisionIDs;
264 auto charge2 = trk2->
charge();
273 ATH_MSG_DEBUG(
"muon pair is rejected by opposite charge check" );
278 ATH_MSG_DEBUG(
"muon pair did not pass passDimuonTrigger() check" );
283 ATH_MSG_DEBUG(
"muon pair is out of the requested mass range" );
294 ATH_MSG_ERROR(
"xAOD::Vertex could not be converted to xAOD::TrigBphys object: please enable MakeExtendedVertex option in vertex fitter " <<
m_vertexFitter->name() );
295 return StatusCode::FAILURE;
307 return StatusCode::SUCCESS;
313 std::vector<int> nSelectedTrk;
319 for (
size_t dimuonIndex = 0; dimuonIndex < state.
dimuons.
size(); ++dimuonIndex) {
330 mon_nTrk = state.
tracks.size();
333 return StatusCode::SUCCESS;
342 selectedTracks.clear();
343 selectedTrackZ0.clear();
347 std::vector<const xAOD::Muon*> muons(2,
nullptr);
349 for (
size_t i = 0;
i < 2; ++
i) {
350 const auto&
muon = state.
muons.at(muonIndices[
i]);
351 muons[
i] = *
muon.link;
360 for (
const auto& trackEL : state.
tracks) {
364 if (perigee && std::abs(perigee->parameters()[
Trk::z0]) <
m_trkZ0) {
365 selectedTracks.push_back(trackEL);
366 selectedTrackZ0[*trackEL] = perigee->parameters()[
Trk::z0];
370 selectedTracks.push_back(trackEL);
371 selectedTrackZ0[*trackEL] = -1000.;
376 if (selectedTracks.size() < 2) {
377 ATH_MSG_DEBUG(
"Found no tracks consistent with dimuon vertex " << dimuonIndex );
378 selectedTracks.clear();
379 selectedTrackZ0.clear();
380 return StatusCode::SUCCESS;
382 std::sort(selectedTracks.begin(), selectedTracks.end(), [p_mu=mu1->
genvecP4()](
const auto& lhs,
const auto& rhs){ return ROOT::Math::VectorUtil::DeltaR(p_mu, (*lhs)->genvecP4()) > ROOT::Math::VectorUtil::DeltaR(p_mu, (*rhs)->genvecP4()); });
384 std::sort(selectedTracks.begin(), selectedTracks.end(), [p_mu=mu2->
genvecP4()](
const auto& lhs,
const auto& rhs){ return ROOT::Math::VectorUtil::DeltaR(p_mu, (*lhs)->genvecP4()) > ROOT::Math::VectorUtil::DeltaR(p_mu, (*rhs)->genvecP4()); });
386 std::sort(selectedTracks.begin(), selectedTracks.end(), [](
const auto& lhs,
const auto& rhs){ return ((*lhs)->pt() > (*rhs)->pt()); });
388 ATH_MSG_DEBUG(
"Found " << selectedTracks.size() <<
" tracks consistent with dimuon vertex " << dimuonIndex );
390 return StatusCode::SUCCESS;
401 ATH_CHECK( dimuonTriggerObjectEL.isValid() );
404 std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx1(dimuon->
trackParticleLinks());
405 trackParticleLinks_vtx1.emplace_back();
408 std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx2(trackParticleLinks_vtx1);
409 trackParticleLinks_vtx2.emplace_back();
412 std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_vtx3(3);
418 size_t iterations = 0;
420 bool isOverWarningThreshold =
false;
422 for (
size_t itrk1 = 0; itrk1 < selectedTracks.size(); ++itrk1) {
425 trackParticleLinks_vtx1[2] = selectedTracks[itrk1];
427 auto charge1 = trk1->
charge();
429 std::unique_ptr<xAOD::Vertex> vtx1;
430 bool makeFit_vtx1 = !makeCombinations;
431 bool passFastFit_vtx1 = (!makeCombinations && !state.
isBadCombination(itrk1));
441 makeFit_vtx1 =
false;
459 makeFit_vtx1 =
false;
470 for (
size_t itrk2 = itrk1 + 1; itrk2 < selectedTracks.size(); ++itrk2) {
473 trackParticleLinks_vtx2[2] = selectedTracks[itrk1];
474 trackParticleLinks_vtx2[3] = selectedTracks[itrk2];
476 auto charge2 = trk2->
charge();
478 std::unique_ptr<xAOD::Vertex> vtx2;
479 bool makeFit_vtx2 = !makeCombinations;
496 makeFit_vtx2 =
false;
519 makeFit_vtx2 =
false;
541 makeFit_vtx2 =
false;
565 makeFit_vtx2 =
false;
588 makeFit_vtx2 =
false;
598 for (
size_t itrk3 = 0; itrk3 < selectedTracks.size(); ++itrk3) {
600 if (itrk3 == itrk1 || itrk3 == itrk2)
continue;
602 trackParticleLinks_vtx3[0] = selectedTracks[itrk1];
603 trackParticleLinks_vtx3[1] = selectedTracks[itrk2];
604 trackParticleLinks_vtx3[2] = selectedTracks[itrk3];
608 std::unique_ptr<xAOD::Vertex> vtx3;
609 bool makeFit_vtx3 = !makeCombinations;
610 bool passFastFit_vtx3 = (!makeCombinations && !state.
isBadCombination(itrk1, itrk2, itrk3));
617 charge1 * charge2 < 0. &&
628 vtx3 =
fit(state.
context(), trackParticleLinks_vtx3,
kDs, dimuon);
629 makeFit_vtx3 =
false;
643 charge1 * charge2 > 0. && charge1 *
charge3 < 0. &&
654 makeFit_vtx3 =
false;
669 isOverWarningThreshold =
true;
672 ATH_MSG_WARNING(
"Dimuon + tracks: the number of fit attempts has exceeded the limit; breaking the loop at this point" );
680 isOverWarningThreshold =
false;
683 std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_D0(2);
685 for (
size_t itrk1 = 0; itrk1 < selectedTracks.size(); ++itrk1) {
688 trackParticleLinks_D0[0] = selectedTracks[itrk1];
691 auto charge1 = trk1->
charge();
693 for (
size_t itrk2 = 0; itrk2 < selectedTracks.size(); ++itrk2) {
694 if (itrk2 == itrk1)
continue;
697 trackParticleLinks_D0[1] = selectedTracks[itrk2];
700 auto charge2 = trk2->
charge();
702 std::unique_ptr<xAOD::Vertex> D0;
703 if (charge1 * charge2 < 0. &&
708 D0 =
fit(state.
context(), trackParticleLinks_D0,
kD0, dimuon);
711 bool isValidD0 =
false;
714 ATH_MSG_DEBUG(
"Partially reconstructed B_c+(-> mu+ mu- D0 X) candidate has been created" );
722 for (
size_t itrk3 = 0; itrk3 < selectedTracks.size(); ++itrk3) {
724 if (itrk3 == itrk1 || itrk3 == itrk2)
continue;
727 trackParticleLinks_vtx1[2] = selectedTracks[itrk3];
738 ATH_MSG_DEBUG(
"Decay vertex(mu+ mu- D*+.pi+) for B_c+ candidate has been created" );
742 ATH_CHECK( triggerObjectEL_vtx1.isValid() );
745 auto Bc_vtx2 =
fit(state.
context(), trackParticleLinks_D0,
kD0, Bc_vtx1.get());
748 ATH_MSG_DEBUG(
"Fully reconstructed B_c+(-> mu+ mu- D*+) candidate has been created" );
761 isOverWarningThreshold =
true;
764 ATH_MSG_WARNING(
"B_c+ -> mu+ mu- D*+: the number of fit attempts has exceeded the limit; breaking the loop at this point" );
772 return StatusCode::SUCCESS;
783 std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_2mu1trk(dimuon->
trackParticleLinks());
784 trackParticleLinks_2mu1trk.emplace_back();
787 std::vector<ElementLink<xAOD::TrackParticleContainer>> trackParticleLinks_2trk(2);
790 size_t iterations = 0;
792 if (
item.second < 5)
continue;
812 return StatusCode::SUCCESS;
822 ATH_MSG_DEBUG(
"Found xAOD::TrigBphys object: mass = " << triggerObject->mass() );
829 if (!dimuonTriggerObject) {
830 ATH_MSG_ERROR(
"Failed to found a valid link for preceding dimuon trigger object" );
831 return StatusCode::FAILURE;
837 auto dimuonIndex = dimuonTriggerObject->
index();
840 ATH_MSG_ERROR(
"Failed to find original muons the dimuon vertex had been built from" );
841 return StatusCode::FAILURE;
847 std::vector<const DecisionIDContainer*> previousDecisionIDs;
853 previousDecisionIDs.push_back(&
muon.decisionIDs);
861 ATH_CHECK(
tool->decideOnSingleObject(decision, previousDecisionIDs) );
865 return StatusCode::SUCCESS;
870 const EventContext& context,
878 ATH_MSG_WARNING(
"At least two tracks should be given to the vertex fitter" );
882 std::vector<const xAOD::TrackParticle*> tracklist(
trackParticleLinks.size(),
nullptr);
891 if (decay != Decay::kPsi_2mu && decay != Decay::kB_PsiPi && decay != Decay::kFastFit_2trk) {
892 ATH_MSG_WARNING(
"Already fitted dimuon vertex should be provided for B -> mu1 mu2 trk1 .. trkN decay as a starting point for fitter" );
896 const Trk::Perigee& perigee1 = tracklist[0]->perigeeParameters();
897 const Trk::Perigee& perigee2 = tracklist[1]->perigeeParameters();
901 ATH_MSG_VERBOSE(
"Starting point: (" << startingPoint(0) <<
", " << startingPoint(1) <<
", " << startingPoint(2) <<
")" );
907 if (decay == Decay::kDs || decay == Decay::kDplus || decay == Decay::kD0) {
912 std::unique_ptr<xAOD::Vertex>
vertex(
m_vertexFitter->fit(tracklist, startingPoint, *fitterState));
918 ATH_MSG_VERBOSE(
"Fit is successful, but vertex chi2 is too high, we are not going to save it (chi2 = " <<
vertex->chiSquared() <<
")" );
936 const std::vector<double>&
trkMass,
944 std::vector<xAOD::TrackParticle::GenVecFourMom_t> momenta;
945 if (!
vertex.vxTrackAtVertexAvailable())
return nullptr;
946 for (
size_t i = 0;
i <
vertex.vxTrackAtVertex().size(); ++
i) {
948 if (!perigee)
return nullptr;
950 momenta.emplace_back(
p.x(),
p.y(),
p.z(),
trkMass[
i]);
953 if (isCascadeDecay) {
954 momentum += ROOT::Math::PtEtaPhiMVector(dimuon->
pt(), dimuon->
eta(), dimuon->
phi(), dimuon->
mass());
958 result->makePrivateStore();
965 mass +=
PDG::mJpsi - (isCascadeDecay ? dimuon->
mass() : (momenta[0] + momenta[1]).M());
981 result->setTrackParticleLinks(
vertex.trackParticleLinks());
985 result->setLowerChainLink(dimuonLink);
989 "TrigBphys object:\n\t " <<
990 "roiId: " <<
result->roiId() <<
"\n\t " <<
991 "particleType: " <<
result->particleType() <<
"\n\t " <<
992 "level: " <<
result->level() <<
"\n\t " <<
993 "eta: " <<
result->eta() <<
"\n\t " <<
994 "phi: " <<
result->phi() <<
"\n\t " <<
995 "mass: " <<
result->mass() <<
"\n\t " <<
996 "fitmass: " <<
result->fitmass() <<
"\n\t " <<
997 "chi2/NDF: " <<
result->fitchi2() <<
" / " <<
result->fitndof() <<
"\n\t " <<
998 "vertex: (" <<
result->fitx() <<
", " <<
result->fity() <<
", " <<
result->fitz() <<
")" <<
"\n\t " <<
999 "Lxy: " <<
result->lxy() );
1020 auto p_mu =
muon->genvecP4();
1021 auto p_trk =
track->genvecP4();
1022 return (ROOT::Math::VectorUtil::DeltaPhi(p_mu, p_trk) <
m_roiPhiWidth && std::abs(p_mu.eta() - p_trk.eta()) <
m_roiEtaWidth);
1028 XYVector
R(decayVertex.
x() - productionVertex.x(), decayVertex.
y() - productionVertex.y());
1033 for (
const auto&
track : tracks) {
1035 if (!perigee)
return -100.;
1039 return R.Dot(
pT.unit());
1046 for (
size_t i = 0;
i <
vertex.vxTrackAtVertex().
size(); ++
i) {
1057 if (previousDecisionIDs.size() != 2) {
1058 ATH_MSG_WARNING(
"TrigBmumuxComboHypo::passDimuonTrigger() expects exactly two containers with previous decision IDs" );
1063 const std::vector<HLT::Identifier>& legDecisionIDs =
tool->legDecisionIds();
1064 if (legDecisionIDs.size() == 1 &&
tool->legMultiplicity().at(0) >= 2) {
1066 const DecisionID id = legDecisionIDs[0].numeric();
1069 else if (legDecisionIDs.size() == 2) {
1073 for (
size_t i = 0;
i < 2; ++
i) {