ATLAS Offline Software
Loading...
Searching...
No Matches
HiggsTemplateCrossSections.h
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5#ifndef TRUTHRIVETTOOLS_HIGGSTEMPLATECROSSSECTIONS_H
6#define TRUTHRIVETTOOLS_HIGGSTEMPLATECROSSSECTIONS_H
7
8// -*- C++ -*-
9#include "Rivet/Analysis.hh"
10#include "Rivet/Particle.hh"
11#include "Rivet/Projections/FastJets.hh"
12
13// Definition of the StatusCode and Category enums
14// Note: the Template XSec Defs *depends* on having included
15// the TLorentzVector header *before* it is included -- it
16// uses the include guard from TLorentzVector to decide
17// what is available
18#include "TLorentzVector.h"
21#include "AtlasHepMC/GenEvent.h"
25#include <string>
26#include <string_view>
27
28
29namespace Rivet {
30
36 public:
37 // Constructor
39 : Analysis("HiggsTemplateCrossSections"),
40 m_HiggsProdMode(HTXS::UNKNOWN), m_HiggsDecayMode(HTXS::UNKNOWNDecay) {}
41
42 public:
43
48
50 Particle getLastInstance(const Particle & ptcl) const {
51 if ( ptcl.genParticle()->end_vertex() ) {
52 if ( !hasChild(ptcl.genParticle(),ptcl.pid()) ) return ptcl;
53 else return getLastInstance(ptcl.children()[0]);
54 }
55 return ptcl;
56 }
57
59 bool originateFrom(const Particle& p, const Particles& ptcls ) const {
60 auto prodVtx = p.genParticle()->production_vertex();
61 if (prodVtx == nullptr) return false;
62 // for each ancestor, check if it matches any of the input particles
63 for (auto ancestor:Rivet::HepMCUtils::particles(std::move(prodVtx),Relatives::ANCESTORS)){
64 for ( const auto & part:ptcls )
65 if ( ancestor==part.genParticle() ) return true;
66 }
67 // if we get here, no ancestor matched any input particle
68 return false;
69 }
70
72 bool originateFrom(const Particle& p, const Particle& p2 ) const {
73 Particles ptcls = {p2}; return originateFrom(p,ptcls);
74 }
75
77 bool hasChild(HepMC::ConstGenParticlePtr ptcl, int pdgID) const {
78 for (const Particle& child:Particle(*ptcl).children())
79 if (child.pid()==pdgID) return true;
80 return false;
81 }
82
84 bool hasParent(HepMC::ConstGenParticlePtr ptcl, int pdgID) const {
85 for (auto parent:Rivet::HepMCUtils::particles(ptcl->production_vertex(),Relatives::PARENTS))
86 if (parent->pdg_id()==pdgID) return true;
87 return false;
88 }
89
91 bool quarkDecay(const Particle &p) const {
92 for (const Particle& child:p.children())
93 if (PID::isQuark(child.pid())) return true;
94 return false;
95 }
96
98 bool ChLeptonDecay(const Particle &p) const {
99 for (const Particle& child:p.children())
100 if (
101#if RIVET_VERSION_CODE >= 40000
102 PID::isChargedLepton(child.pid())
103#else
104 PID::isChLepton(child.pid())
105#endif // RIVET_VERSION_CODE
106 ) return true;
107 return false;
108 }
109
112 HiggsClassification error(HiggsClassification &cat, HTXS::ErrorCode err,
113 std::string_view msg={}, int NmaxWarnings=20) const {
114 // Set the error, and keep statistics
115 cat.errorCode = err;
116 const auto errIndex = static_cast<std::size_t>(err);
117 if (errIndex < std::size(m_errorCount)) {
118 ++m_errorCount[errIndex];
119 } else {
120 MSG_WARNING("Invalid HTXS error code: " << errIndex);
121 }
122 // Print warning message to the screen/log
123 static std::atomic<int> Nwarnings = 0;
124 if ( !msg.empty() && ++Nwarnings < NmaxWarnings )
126
127 return cat;
128 }
129
130
132 HiggsClassification classifyEvent(const Event& event, const HTXS::HiggsProdMode prodMode, const HTXS::HiggsDecayMode decayMode ) const {
133
134 // the classification object
135 HiggsClassification cat;
136 cat.prodMode = prodMode;
137 cat.errorCode = HTXS::UNDEFINED;
138 cat.stage0_cat = HTXS::Stage0::UNKNOWN;
139 cat.stage1_cat_pTjet25GeV = HTXS::Stage1::UNKNOWN;
140 cat.stage1_cat_pTjet30GeV = HTXS::Stage1::UNKNOWN;
141 cat.stage1_2_cat_pTjet25GeV = HTXS::Stage1_2::UNKNOWN;
142 cat.stage1_2_cat_pTjet30GeV = HTXS::Stage1_2::UNKNOWN;
143 cat.stage1_2_fine_cat_pTjet25GeV = HTXS::Stage1_2_Fine::UNKNOWN;
144 cat.stage1_2_fine_cat_pTjet30GeV = HTXS::Stage1_2_Fine::UNKNOWN;
145 cat.stage1_3_cat_pTjet25GeV = HTXS::Stage1_3::UNKNOWN;
146 cat.stage1_3_cat_pTjet30GeV = HTXS::Stage1_3::UNKNOWN;
147 cat.stage1_3_fine_cat_pTjet25GeV = HTXS::Stage1_3_Fine::UNKNOWN;
148 cat.stage1_3_fine_cat_pTjet30GeV = HTXS::Stage1_3_Fine::UNKNOWN;
149 cat.isTHW = false;
150 cat.decaystage0_cat = HTXS::Stage0::UNKNOWNDecay;
151
152 if (prodMode == HTXS::UNKNOWN)
153 return error(cat,HTXS::PRODMODE_DEFINED,
154 "Unkown Higgs production mechanism. Cannot classify event."
155 " Classification for all events will most likely fail.");
156
157 /*****
158 * Step 1.
159 * Idenfify the Higgs boson and the hard scatter vertex
160 * There should be only one of each.
161 */
162
163 auto HSvtx = HepMC::signal_process_vertex(event.genEvent());
164 int Nhiggs=0;
165 for (auto ptcl : Rivet::HepMCUtils::particles(event.genEvent()) ) {
166
167 // a) Reject all non-Higgs particles
168 if ( !PID::isHiggs(ptcl->pdg_id()) ) continue;
169 // b) select only the final Higgs boson copy, prior to decay
170 if ( ptcl->end_vertex() && !hasChild(ptcl,PID::HIGGS) ) {
171 cat.higgs = Particle(ptcl); ++Nhiggs;
172 }
173 // c) if HepMC::signal_proces_vertex is missing
174 // set hard-scatter vertex based on first Higgs boson
175 if ( HSvtx==nullptr && ptcl->production_vertex() && !hasParent(ptcl,PID::HIGGS) )
176 HSvtx = ptcl->production_vertex();
177 }
178
179 // Make sure things are in order so far
180 if (Nhiggs!=1)
182 "Current event has "+std::to_string(Nhiggs)+" Higgs bosons. There must be only one.");
183 if (cat.higgs.children().size()<2)
185 "Could not identify Higgs boson decay products.");
186
187 if (HSvtx == nullptr)
188 return error(cat,HTXS::HS_VTX_IDENTIFICATION,"Cannot find hard-scatter vertex of current event.");
189
190 /*****
191 * Step 2.
192 * Identify associated vector bosons
193 */
194
195 // Find associated vector bosons
196 bool is_uncatdV = false;
197 Particles uncatV_decays;
198 FourMomentum uncatV_p4(0,0,0,0);
199 FourVector uncatV_v4(0,0,0,0);
200 int nWs=0, nZs=0;
201 if ( isVH(prodMode) ) {
202 for (auto ptcl:Rivet::HepMCUtils::particles(HSvtx,Relatives::CHILDREN)) {
203 if (PID::isW(ptcl->pdg_id())) { ++nWs; cat.V=Particle(ptcl); }
204 if (PID::isZ(ptcl->pdg_id())) { ++nZs; cat.V=Particle(std::move(ptcl)); }
205 }
206 if(nWs+nZs>0) cat.V = getLastInstance(cat.V);
207 else {
208 for (auto ptcl:Rivet::HepMCUtils::particles(HSvtx,Relatives::CHILDREN)) {
209 if (!PID::isHiggs(ptcl->pdg_id())) {
210 uncatV_decays += Particle(ptcl);
211 uncatV_p4 += Particle(ptcl).momentum();
212 uncatV_v4 += Particle(std::move(ptcl)).origin();
213 }
214 }
215 is_uncatdV = true; cat.V = Particle(24,uncatV_p4,uncatV_v4);
216 }
217 }
218
219 if ( !is_uncatdV ){
220
221 if ( isVH(prodMode) && !cat.V.genParticle()->end_vertex() )
222 return error(cat,HTXS::VH_DECAY_IDENTIFICATION,"Vector boson does not decay!");
223
224 if ( isVH(prodMode) && cat.V.children().size()<2 )
225 return error(cat,HTXS::VH_DECAY_IDENTIFICATION,"Vector boson does not decay!");
226
227 if ( ( prodMode==HTXS::WH && (nZs>0||nWs!=1) ) ||
228 ( (prodMode==HTXS::QQ2ZH||prodMode==HTXS::GG2ZH) && (nZs!=1||nWs>0) ) )
229 return error(cat,HTXS::VH_IDENTIFICATION,"Found "+std::to_string(nWs)+" W-bosons and "+
230 std::to_string(nZs)+" Z-bosons. Inconsitent with VH expectation.");
231 }
232
233 // Find and store the W-bosons from ttH->WbWbH
234 Particles Ws;
235 if ( prodMode==HTXS::TTH || prodMode==HTXS::TH ){
236 // loop over particles produced in hard-scatter vertex
237 for ( auto ptcl : Rivet::HepMCUtils::particles(HSvtx,Relatives::CHILDREN) ) {
238 if ( !PID::isTop(ptcl->pdg_id()) ) continue;
239 Particle top = getLastInstance(Particle(std::move(ptcl)));
240 if ( top.genParticle()->end_vertex() )
241 for (const auto &child:top.children())
242 if ( PID::isW(child.pid()) ) Ws += getLastInstance(child);
243 }
244 }
245
246 // Make sure result make sense
247 if ( (prodMode==HTXS::TTH && Ws.size()<2) || (prodMode==HTXS::TH && Ws.size()<1 ) )
248 return error(cat,HTXS::TOP_W_IDENTIFICATION,"Failed to identify W-boson(s) from t-decay!");
249
250 // Differentiate tHq from tHW by presence of W in HSvtx children.
251 if (prodMode == HTXS::TH) {
252 for ( auto ptcl : Rivet::HepMCUtils::particles(std::move(HSvtx),Relatives::CHILDREN) ) {
253 if (PID::isW(ptcl->pdg_id())) {
254 cat.isTHW = true;
255 break;
256 }
257 }
258 }
259
260 /*****
261 * Step 3.
262 * Build jets
263 * Make sure all stable particles are present
264 */
265
266 // Create a list of the vector bosons that decay leptonically
267 // Either the vector boson produced in association with the Higgs boson,
268 // or the ones produced from decays of top quarks produced with the Higgs
269 Particles leptonicVs;
270 if ( !is_uncatdV ){
271 if ( isVH(prodMode) && !quarkDecay(cat.V) ) leptonicVs += cat.V;
272 }else leptonicVs = std::move(uncatV_decays);
273 for ( const auto & W:Ws ) if ( W.genParticle()->end_vertex() && !quarkDecay(W) ) leptonicVs += W;
274
275 // Obtain all stable, final-state particles
276 const Particles FS = apply<FinalState>(event, "FS").particles();
277 Particles hadrons;
278 Particles decayparticles;
279
280 FourMomentum sum(0,0,0,0), vSum(0,0,0,0), hSum(0,0,0,0);
281 for ( const Particle &p : FS ) {
282 // Add up the four momenta of all stable particles as a cross check
283 sum += p.momentum();
284 // ignore particles from the Higgs boson
285 if ( originateFrom(p,cat.higgs) ) { hSum += p.momentum(); decayparticles += p; continue; }
286 // Cross-check the V decay products for VH
287 if ( isVH(prodMode) && !is_uncatdV && originateFrom(p,Ws) ) vSum += p.momentum();
288 // ignore final state particles from leptonic V decays
289 if ( leptonicVs.size() && originateFrom(p,leptonicVs) ) continue;
290 // All particles reaching here are considered hadrons and will be used to build jets
291 hadrons += p;
292 }
293
294 cat.p4decay_higgs = hSum;
295 cat.p4decay_V = vSum;
296
297 FinalState fps_temp;
298 FastJets jets(fps_temp,
299#if RIVET_VERSION_CODE >= 40000
300 JetAlg::ANTIKT,
301#else
302 FastJets::ANTIKT,
303#endif // RIVET_VERSION_CODE
304 0.4 );
305 jets.calc(hadrons);
306
307 cat.jets25 = jets.jetsByPt( Cuts::pT > 25.0 );
308 cat.jets30 = jets.jetsByPt( Cuts::pT > 30.0 );
309
310 // check that four mometum sum of all stable particles satisfies momentum consevation
311 if ( sum.pt()>0.1 )
312 return error(cat,HTXS::MOMENTUM_CONSERVATION,"Four vector sum does not amount to pT=0, m=E=sqrt(s), but pT="+
313 std::to_string(sum.pt())+" GeV and m = "+std::to_string(sum.mass())+" GeV");
314
315 // check if V-boson was not included in the event record but decay particles were
316 // EFT contact interaction: return UNKNOWN for category but set all event/particle kinematics
317 if(is_uncatdV)
318 return error(cat,HTXS::VH_IDENTIFICATION,"Failed to identify associated V-boson!");
319
320 /*****
321 * Step 4.
322 * Classify and save output
323 */
324
325 // Apply the categorization categorization
326 cat.isZ2vvDecay = false;
327 if( (prodMode==HTXS::GG2ZH || prodMode==HTXS::QQ2ZH) && !quarkDecay(cat.V) && !ChLeptonDecay(cat.V) ) cat.isZ2vvDecay = true;
328 cat.stage0_cat = getStage0Category(prodMode,cat.higgs,cat.V);
329 cat.stage1_cat_pTjet25GeV = getStage1Category(prodMode,cat.higgs,cat.jets25,cat.V);
330 cat.stage1_cat_pTjet30GeV = getStage1Category(prodMode,cat.higgs,cat.jets30,cat.V);
331 cat.stage1_2_cat_pTjet25GeV = getStage1_2_Category(prodMode,cat.higgs,cat.jets25,cat.V);
332 cat.stage1_2_cat_pTjet30GeV = getStage1_2_Category(prodMode,cat.higgs,cat.jets30,cat.V);
333 cat.stage1_2_fine_cat_pTjet25GeV = getStage1_2_Fine_Category(prodMode,cat.higgs,cat.jets25,cat.V);
334 cat.stage1_2_fine_cat_pTjet30GeV = getStage1_2_Fine_Category(prodMode,cat.higgs,cat.jets30,cat.V);
335 cat.stage1_3_cat_pTjet25GeV = getStage1_3_Category(prodMode,cat.higgs,cat.jets25,cat.V);
336 cat.stage1_3_cat_pTjet30GeV = getStage1_3_Category(prodMode,cat.higgs,cat.jets30,cat.V);
337 cat.stage1_3_fine_cat_pTjet25GeV = getStage1_3_Fine_Category(prodMode,cat.higgs,cat.jets25,cat.V,cat.isTHW);
338 cat.stage1_3_fine_cat_pTjet30GeV = getStage1_3_Fine_Category(prodMode,cat.higgs,cat.jets30,cat.V,cat.isTHW);
339 cat.errorCode = HTXS::SUCCESS; ++m_errorCount[HTXS::SUCCESS];
340
341 // Apply the Higgs decay categorization
342 if (decayMode != HTXS::HiggsDecayMode::UNKNOWNDecay) cat.decaystage0_cat = getStage0DecayCategory(cat.higgs, decayparticles, cat.decay_observables, cat.decay_cuts_passed);
343
344 return cat;
345 }
346
352
354int getBin(double x, const std::vector<double>& bins) const {
355 if (bins.empty() || x < bins.front()) {
356 throw std::invalid_argument("Input value is out of bin range or bins vector is empty.");
357 }
358
359 for (size_t i = 1; i < bins.size(); ++i) {
360 if (x < bins[i]) {
361 return static_cast<int>(i - 1);
362 }
363 }
364
365 return static_cast<int>(bins.size() - 1);
366}
367
371 int vbfTopology(const Jets &jets, const Particle &higgs) const {
372 if (jets.size()<2) return 0;
373 const FourMomentum &j1=jets[0].momentum(), &j2=jets[1].momentum();
374 bool VBFtopo = (j1+j2).mass() > 400.0 && std::abs(j1.rapidity()-j2.rapidity()) > 2.8;
375 return VBFtopo ? (j1+j2+higgs.momentum()).pt()<25 ? 2 : 1 : 0;
376 }
377
381 int vbfTopology_Stage1_2(const Jets &jets, const Particle &higgs) const {
382 if (jets.size()<2) return 0;
383 const FourMomentum &j1=jets[0].momentum(), &j2=jets[1].momentum();
384 double mjj = (j1+j2).mass();
385 if(mjj>350 && mjj<=700) return (j1+j2+higgs.momentum()).pt()<25 ? 1 : 2;
386 else if(mjj>700) return (j1+j2+higgs.momentum()).pt()<25 ? 3 : 4;
387 else return 0;
388 }
389
395 int vbfTopology_Stage1_2_Fine(const Jets &jets, const Particle &higgs) const {
396 if (jets.size()<2) return 0;
397 const FourMomentum &j1=jets[0].momentum(), &j2=jets[1].momentum();
398 double mjj = (j1+j2).mass();
399 if(mjj>350 && mjj<=700) return (j1+j2+higgs.momentum()).pt()<25 ? 1 : 2;
400 else if(mjj>700 && mjj<=1000) return (j1+j2+higgs.momentum()).pt()<25 ? 3 : 4;
401 else if(mjj>1000 && mjj<=1500) return (j1+j2+higgs.momentum()).pt()<25 ? 5 : 6;
402 else if(mjj>1500) return (j1+j2+higgs.momentum()).pt()<25 ? 7 : 8;
403 else return 0;
404 }
405
408 int vbfTopology_Stage1_3_Fine(const Jets &jets, const Particle &higgs) const {
409 if (jets.size() < 2) return 0;
410 const FourMomentum &j1 = jets[0].momentum(), &j2 = jets[1].momentum();
411 double mjj = (j1 + j2).mass();
412 double pthjj = (j1 + j2 + higgs.momentum()).pt();
413 double deltaphijj =
414 j1.eta() > j2.eta()
415 ? deltaPhi(j1, j2)
416 : -1*deltaPhi(j1, j2);
417 // mjj-pthjj binning
418 int mjj_pthjj_bin = 0;
419 if (mjj > 350 && mjj <= 700)
420 mjj_pthjj_bin = pthjj < 25 ? 1 : 2;
421 else if (mjj > 700 && mjj <= 1000)
422 mjj_pthjj_bin = pthjj < 25 ? 3 : 4;
423 else if (mjj > 1000 && mjj <= 1500)
424 mjj_pthjj_bin = pthjj < 25 ? 5 : 6;
425 else if (mjj > 1500)
426 mjj_pthjj_bin = pthjj < 25 ? 7 : 8;
427 else
428 mjj_pthjj_bin = 0;
429 // deltaphijj binning
430 constexpr double pi = 3.14159265358979323846;
431 int deltaphijj_bin = mjj > 350 ? 8*getBin(deltaphijj, {-1*pi, -0.5*pi, 0, 0.5*pi, pi}) : 0;
432 // total vbfTopo binning
433 return deltaphijj_bin + mjj_pthjj_bin;
434 }
435
436
438 bool isVH(HTXS::HiggsProdMode p) const { return p==HTXS::WH || p==HTXS::QQ2ZH || p==HTXS::GG2ZH; }
439
442 const Particle &higgs,
443 const Particle &V) const {
444 using namespace HTXS::Stage0;
445 int ctrlHiggs = std::abs(higgs.rapidity())<2.5;
446 // Special cases first, qq→Hqq
447 if ( (prodMode==HTXS::WH||prodMode==HTXS::QQ2ZH) && quarkDecay(V) ) {
448 return ctrlHiggs ? VH2HQQ : VH2HQQ_FWDH;
449 } else if ( prodMode==HTXS::GG2ZH && quarkDecay(V) ) {
450 return Category(HTXS::GGF*10 + ctrlHiggs);
451 }
452 // General case after
453 return Category(prodMode*10 + ctrlHiggs);
454 }
455
458 const Particle &higgs,
459 const Jets &jets,
460 const Particle &V) const {
461 using namespace HTXS::Stage1;
462 int Njets=jets.size(), ctrlHiggs = std::abs(higgs.rapidity())<2.5, fwdHiggs = !ctrlHiggs;
463 double pTj1 = jets.size() ? jets[0].momentum().pt() : 0;
464 int vbfTopo = vbfTopology(jets,higgs);
465
466 // 1. GGF Stage 1 categories
467 // Following YR4 write-up: XXXXX
468 if (prodMode==HTXS::GGF || (prodMode==HTXS::GG2ZH && quarkDecay(V)) ) {
469 if (fwdHiggs) return GG2H_FWDH;
470 if (Njets==0) return GG2H_0J;
471 else if (Njets==1) return Category(GG2H_1J_PTH_0_60+getBin(higgs.pt(),{0,60,120,200}));
472 else if (Njets>=2) {
473 // events with pT_H>200 get priority over VBF cuts
474 if(higgs.pt()<=200){
475 if (vbfTopo==2) return GG2H_VBFTOPO_JET3VETO;
476 else if (vbfTopo==1) return GG2H_VBFTOPO_JET3;
477 }
478 // Njets >= 2jets without VBF topology
479 return Category(GG2H_GE2J_PTH_0_60+getBin(higgs.pt(),{0,60,120,200}));
480 }
481 }
482 // 2. Electroweak qq->Hqq Stage 1 categories
483 else if (prodMode==HTXS::VBF || ( isVH(prodMode) && quarkDecay(V)) ) {
484 if (std::abs(higgs.rapidity())>2.5) return QQ2HQQ_FWDH;
485 if (pTj1>200) return QQ2HQQ_PTJET1_GT200;
486 if (vbfTopo==2) return QQ2HQQ_VBFTOPO_JET3VETO;
487 if (vbfTopo==1) return QQ2HQQ_VBFTOPO_JET3;
488 double mjj = jets.size()>1 ? (jets[0].mom()+jets[1].mom()).mass():0;
489 if ( 60 < mjj && mjj < 120 ) return QQ2HQQ_VH2JET;
490 return QQ2HQQ_REST;
491 }
492 // 3. WH->Hlv categories
493 else if (prodMode==HTXS::WH) {
494 if (fwdHiggs) return QQ2HLNU_FWDH;
495 else if (V.pt()<150) return QQ2HLNU_PTV_0_150;
496 else if (V.pt()>250) return QQ2HLNU_PTV_GT250;
497 // 150 < pTV/GeV < 250
498 return jets.size()==0 ? QQ2HLNU_PTV_150_250_0J : QQ2HLNU_PTV_150_250_GE1J;
499 }
500 // 4. qq->ZH->llH categories
501 else if (prodMode==HTXS::QQ2ZH) {
502 if (fwdHiggs) return QQ2HLL_FWDH;
503 else if (V.pt()<150) return QQ2HLL_PTV_0_150;
504 else if (V.pt()>250) return QQ2HLL_PTV_GT250;
505 // 150 < pTV/GeV < 250
506 return jets.size()==0 ? QQ2HLL_PTV_150_250_0J : QQ2HLL_PTV_150_250_GE1J;
507 }
508 // 5. gg->ZH->llH categories
509 else if (prodMode==HTXS::GG2ZH ) {
510 if (fwdHiggs) return GG2HLL_FWDH;
511 if (V.pt()<150) return GG2HLL_PTV_0_150;
512 else if (jets.size()==0) return GG2HLL_PTV_GT150_0J;
514 }
515 // 6.ttH,bbH,tH categories
516 else if (prodMode==HTXS::TTH) return Category(TTH_FWDH+ctrlHiggs);
517 else if (prodMode==HTXS::BBH) return Category(BBH_FWDH+ctrlHiggs);
518 else if (prodMode==HTXS::TH ) return Category(TH_FWDH+ctrlHiggs);
519 return UNKNOWN;
520 }
521
524 const Particle &higgs,
525 const Jets &jets,
526 const Particle &V) const {
527 using namespace HTXS::Stage1_2;
528 int Njets=jets.size(), ctrlHiggs = std::abs(higgs.rapidity())<2.5, fwdHiggs = !ctrlHiggs;
529 int vbfTopo = vbfTopology_Stage1_2(jets,higgs);
530
531 // 1. GGF Stage 1 categories
532 // Following YR4 write-up: XXXXX
533 if (prodMode==HTXS::GGF || (prodMode==HTXS::GG2ZH && quarkDecay(V)) ) {
534 if (fwdHiggs) return GG2H_FWDH;
535 if ( higgs.pt()>200 ) return Category(GG2H_PTH_200_300+getBin(higgs.pt(),{200,300,450,650}));
536 if (Njets==0) return higgs.pt()<10 ? GG2H_0J_PTH_0_10 : GG2H_0J_PTH_GT10;
537 if (Njets==1) return Category(GG2H_1J_PTH_0_60+getBin(higgs.pt(),{0,60,120,200}));
538 if (Njets>1){
539 //VBF topology
540 if(vbfTopo) return Category(GG2H_GE2J_MJJ_350_700_PTH_0_200_PTHJJ_0_25+vbfTopo-1);
541 //Njets >= 2jets without VBF topology (mjj<350)
542 return Category(GG2H_GE2J_MJJ_0_350_PTH_0_60+getBin(higgs.pt(),{0,60,120,200}));
543 }
544 }
545
546 // 2. Electroweak qq->Hqq Stage 1.2 categories
547 else if (prodMode==HTXS::VBF || ( isVH(prodMode) && quarkDecay(V)) ) {
548 if (std::abs(higgs.rapidity())>2.5) return QQ2HQQ_FWDH;
549 int Njets=jets.size();
550 if (Njets==0) return QQ2HQQ_0J;
551 else if (Njets==1) return QQ2HQQ_1J;
552 else if (Njets>=2) {
553 double mjj = (jets[0].mom()+jets[1].mom()).mass();
554 if ( mjj < 60 ) return QQ2HQQ_GE2J_MJJ_0_60;
555 else if ( 60 < mjj && mjj < 120 ) return QQ2HQQ_GE2J_MJJ_60_120;
556 else if ( 120 < mjj && mjj < 350 ) return QQ2HQQ_GE2J_MJJ_120_350;
557 else if ( mjj > 350 ) {
558 if (higgs.pt()>200) return QQ2HQQ_GE2J_MJJ_GT350_PTH_GT200;
559 if(vbfTopo) return Category(QQ2HQQ_GE2J_MJJ_GT350_PTH_GT200+vbfTopo);
560 }
561 }
562 }
563 // 3. WH->Hlv categories
564 else if (prodMode==HTXS::WH) {
565 if (fwdHiggs) return QQ2HLNU_FWDH;
566 else if (V.pt()<75) return QQ2HLNU_PTV_0_75;
567 else if (V.pt()<150) return QQ2HLNU_PTV_75_150;
568 else if (V.pt()>250) return QQ2HLNU_PTV_GT250;
569 // 150 < pTV/GeV < 250
570 return jets.size()==0 ? QQ2HLNU_PTV_150_250_0J : QQ2HLNU_PTV_150_250_GE1J;
571 }
572 // 4. qq->ZH->llH categories
573 else if (prodMode==HTXS::QQ2ZH) {
574 if (fwdHiggs) return QQ2HLL_FWDH;
575 else if (V.pt()<75) return QQ2HLL_PTV_0_75;
576 else if (V.pt()<150) return QQ2HLL_PTV_75_150;
577 else if (V.pt()>250) return QQ2HLL_PTV_GT250;
578 // 150 < pTV/GeV < 250
579 return jets.size()==0 ? QQ2HLL_PTV_150_250_0J : QQ2HLL_PTV_150_250_GE1J;
580 }
581 // 5. gg->ZH->llH categories
582 else if (prodMode==HTXS::GG2ZH ) {
583 if (fwdHiggs) return GG2HLL_FWDH;
584 else if (V.pt()<75) return GG2HLL_PTV_0_75;
585 else if (V.pt()<150) return GG2HLL_PTV_75_150;
586 else if (V.pt()>250) return GG2HLL_PTV_GT250;
587 return jets.size()==0 ? GG2HLL_PTV_150_250_0J : GG2HLL_PTV_150_250_GE1J;
588 }
589 // 6.ttH,bbH,tH categories
590 else if (prodMode==HTXS::TTH) {
591 if (fwdHiggs) return TTH_FWDH;
592 else return Category(TTH_PTH_0_60+getBin(higgs.pt(),{0,60,120,200,300}));
593 }
594 else if (prodMode==HTXS::BBH) return Category(BBH_FWDH+ctrlHiggs);
595 else if (prodMode==HTXS::TH ) return Category(TH_FWDH+ctrlHiggs);
596 return UNKNOWN;
597 }
598
601 const Particle &higgs,
602 const Jets &jets,
603 const Particle &V) const {
604 using namespace HTXS::Stage1_2_Fine;
605 int Njets=jets.size(), ctrlHiggs = std::abs(higgs.rapidity())<2.5, fwdHiggs = !ctrlHiggs;
606 int vbfTopo = vbfTopology_Stage1_2_Fine(jets,higgs);
607
608 // 1. GGF Stage 1.2 categories
609 // Following YR4 write-up: XXXXX
610 if (prodMode==HTXS::GGF || (prodMode==HTXS::GG2ZH && quarkDecay(V)) ) {
611 if (fwdHiggs) return GG2H_FWDH;
612 if ( higgs.pt()>200 ){
613 if (Njets>0){
614 double pTHj = (jets[0].momentum()+higgs.momentum()).pt();
615 if( pTHj/higgs.pt()>0.15 ) return Category(GG2H_PTH_200_300_PTHJoverPTH_GT15+getBin(higgs.pt(),{200,300,450,650}));
616 else return Category(GG2H_PTH_200_300_PTHJoverPTH_0_15+getBin(higgs.pt(),{200,300,450,650}));
617 }
618 else return Category(GG2H_PTH_200_300_PTHJoverPTH_0_15+getBin(higgs.pt(),{200,300,450,650}));
619 }
620 if (Njets==0) return higgs.pt()<10 ? GG2H_0J_PTH_0_10 : GG2H_0J_PTH_GT10;
621 if (Njets==1) return Category(GG2H_1J_PTH_0_60+getBin(higgs.pt(),{0,60,120,200}));
622 if (Njets>1){
623 //double mjj = (jets[0].mom()+jets[1].mom()).mass();
624 double pTHjj = (jets[0].momentum()+jets[1].momentum()+higgs.momentum()).pt();
625 //VBF topology
626 if(vbfTopo) return Category(GG2H_GE2J_MJJ_350_700_PTH_0_200_PTHJJ_0_25+vbfTopo-1);
627 //Njets >= 2jets without VBF topology (mjj<350)
628 if (pTHjj<25) return Category(GG2H_GE2J_MJJ_0_350_PTH_0_60_PTHJJ_0_25+getBin(higgs.pt(),{0,60,120,200}));
629 else return Category(GG2H_GE2J_MJJ_0_350_PTH_0_60_PTHJJ_GT25+getBin(higgs.pt(),{0,60,120,200}));
630 }
631 }
632
633 // 2. Electroweak qq->Hqq Stage 1.2 categories
634 else if (prodMode==HTXS::VBF || ( isVH(prodMode) && quarkDecay(V)) ) {
635 if (std::abs(higgs.rapidity())>2.5) return QQ2HQQ_FWDH;
636 int Njets=jets.size();
637 if (Njets==0) return QQ2HQQ_0J;
638 else if (Njets==1) return QQ2HQQ_1J;
639 else if (Njets>=2) {
640 double mjj = (jets[0].mom()+jets[1].mom()).mass();
641 double pTHjj = (jets[0].momentum()+jets[1].momentum()+higgs.momentum()).pt();
642 if (mjj<350){
643 if (pTHjj<25) return Category(QQ2HQQ_GE2J_MJJ_0_60_PTHJJ_0_25+getBin(mjj,{0,60,120,350}));
644 else return Category(QQ2HQQ_GE2J_MJJ_0_60_PTHJJ_GT25+getBin(mjj,{0,60,120,350}));
645 } else { //mjj>350 GeV
646 if (higgs.pt()<200){
648 } else {
650 }
651 }
652 }
653 }
654 // 3. WH->Hlv categories
655 else if (prodMode==HTXS::WH) {
656 if (fwdHiggs) return QQ2HLNU_FWDH;
657 int Njets=jets.size();
658 if (Njets==0) return Category(QQ2HLNU_PTV_0_75_0J+getBin(V.pt(),{0,75,150,250,400}));
659 if (Njets==1) return Category(QQ2HLNU_PTV_0_75_1J+getBin(V.pt(),{0,75,150,250,400}));
660 return Category(QQ2HLNU_PTV_0_75_GE2J+getBin(V.pt(),{0,75,150,250,400}));
661 }
662 // 4. qq->ZH->llH categories
663 else if (prodMode==HTXS::QQ2ZH) {
664 if (fwdHiggs) return QQ2HLL_FWDH;
665 int Njets=jets.size();
666 if (Njets==0) return Category(QQ2HLL_PTV_0_75_0J+getBin(V.pt(),{0,75,150,250,400}));
667 if (Njets==1) return Category(QQ2HLL_PTV_0_75_1J+getBin(V.pt(),{0,75,150,250,400}));
668 return Category(QQ2HLL_PTV_0_75_GE2J+getBin(V.pt(),{0,75,150,250,400}));
669 }
670 // 5. gg->ZH->llH categories
671 else if (prodMode==HTXS::GG2ZH ) {
672 if (fwdHiggs) return GG2HLL_FWDH;
673 int Njets=jets.size();
674 if (Njets==0) return Category(GG2HLL_PTV_0_75_0J+getBin(V.pt(),{0,75,150,250,400}));
675 if (Njets==1) return Category(GG2HLL_PTV_0_75_1J+getBin(V.pt(),{0,75,150,250,400}));
676 return Category(GG2HLL_PTV_0_75_GE2J+getBin(V.pt(),{0,75,150,250,400}));
677 }
678 // 6.ttH,bbH,tH categories
679 else if (prodMode==HTXS::TTH) {
680 if (fwdHiggs) return TTH_FWDH;
681 else return Category(TTH_PTH_0_60+getBin(higgs.pt(),{0,60,120,200,300,450}));
682 }
683 else if (prodMode==HTXS::BBH) return Category(BBH_FWDH+ctrlHiggs);
684 else if (prodMode==HTXS::TH ) return Category(TH_FWDH+ctrlHiggs);
685 return UNKNOWN;
686 }
687
689 const Jets &jets, const Particle &V) const {
690 using namespace HTXS::Stage1_3;
691 if (prodMode == HTXS::BBH) {
692 const Category ggFCategory = getStage1_3_Category(HTXS::GGF, higgs, jets, V);
693 if (ggFCategory == UNKNOWN) return UNKNOWN;
694 return Category(BBH_FWDH + static_cast<int>(ggFCategory) - GG2H_FWDH);
695 }
696
697 int Njets = jets.size(), ctrlHiggs = std::abs(higgs.rapidity()) < 2.5, fwdHiggs = !ctrlHiggs;
698 int vbfTopo = vbfTopology(jets, higgs);
699
700 // 1. GGF Stage 1.3 categories
701 if (prodMode == HTXS::GGF || (prodMode == HTXS::GG2ZH && quarkDecay(V))) {
702 if (fwdHiggs) return GG2H_FWDH;
703 if (higgs.pt() > 200) return Category(GG2H_PTH_200_300 + getBin(higgs.pt(), {200, 300, 450, 650, 1000}));
704 if (Njets == 0) return Category(GG2H_0J_PTH_0_5 + getBin(higgs.pt(), {0, 5, 10, 15, 20, 25, 30, 200}));
705 if (Njets == 1) return Category(GG2H_1J_PTH_0_30 + getBin(higgs.pt(), {0, 30, 60, 120, 200}));
706 if (Njets > 1) {
707 // VBF topology
708 if (vbfTopo) return Category(GG2H_GE2J_MJJ_350_700_PTH_0_200_PTHJJ_0_25 + vbfTopo - 1);
709 // Njets >= 2jets without VBF topology (mjj<350)
710 return Category(GG2H_GE2J_MJJ_0_350_PTH_0_30 + getBin(higgs.pt(), {0, 30, 60, 120, 200}));
711 }
712 }
713 // 2. Electroweak qq->Hqq Stage 1.3 categories
714 else if (prodMode == HTXS::VBF || (isVH(prodMode) && quarkDecay(V))) {
715 if (std::abs(higgs.rapidity()) > 2.5) return QQ2HQQ_FWDH;
716 int Njets = jets.size();
717 if (Njets == 0)
718 return QQ2HQQ_0J;
719 else if (Njets == 1)
720 return QQ2HQQ_1J;
721 else if (Njets >= 2) {
722 double mjj = (jets[0].mom() + jets[1].mom()).mass();
723 if (mjj < 60)
725 else if (60 < mjj && mjj < 120)
727 else if (120 < mjj && mjj < 350)
729 else if (mjj > 350) {
730 if (higgs.pt() > 200)
732 if (vbfTopo) return Category(QQ2HQQ_GE2J_MJJ_350_700_PTH_0_200_PTHJJ_0_25 + vbfTopo - 1);
733 }
734 }
735 }
736 // 3. WH->Hlv Stage 1.3 categories
737 else if (prodMode == HTXS::WH) {
738 if (fwdHiggs)
739 return QQ2HLNU_FWDH;
740 else if (V.pt() < 75)
741 return QQ2HLNU_PTV_0_75;
742 else if (V.pt() < 150)
743 return QQ2HLNU_PTV_75_150;
744 else if (V.pt() < 250)
745 return jets.size() == 0 ? QQ2HLNU_PTV_150_250_0J : QQ2HLNU_PTV_150_250_GE1J;
746 else if (V.pt() < 400)
747 return jets.size() == 0 ? QQ2HLNU_PTV_250_400_0J : QQ2HLNU_PTV_250_400_GE1J;
748 else if (V.pt() < 600)
749 return QQ2HLNU_PTV_400_600;
750 return QQ2HLNU_PTV_GT600;
751 }
752 // 4. qq->ZH->llH Stage 1.3 categories
753 else if (prodMode == HTXS::QQ2ZH) {
754 if (fwdHiggs)
755 return QQ2HLL_FWDH;
756 else if (V.pt() < 75)
757 return QQ2HLL_PTV_0_75;
758 else if (V.pt() < 150)
759 return QQ2HLL_PTV_75_150;
760 else if (V.pt() < 250)
761 return jets.size() == 0 ? QQ2HLL_PTV_150_250_0J : QQ2HLL_PTV_150_250_GE1J;
762 else if (V.pt() < 400)
763 return jets.size() == 0 ? QQ2HLL_PTV_250_400_0J : QQ2HLL_PTV_250_400_GE1J;
764 else if (V.pt() < 600)
765 return QQ2HLL_PTV_400_600;
766 return QQ2HLL_PTV_GT600;
767 }
768 // 5. gg->ZH->llH Stage 1.3 categories
769 else if (prodMode == HTXS::GG2ZH) {
770 if (fwdHiggs)
771 return GG2HLL_FWDH;
772 else if (V.pt() < 75)
773 return GG2HLL_PTV_0_75;
774 else if (V.pt() < 150)
775 return GG2HLL_PTV_75_150;
776 else if (V.pt() < 250)
777 return jets.size() == 0 ? GG2HLL_PTV_150_250_0J : GG2HLL_PTV_150_250_GE1J;
778 else if (V.pt() < 400)
779 return jets.size() == 0 ? GG2HLL_PTV_250_400_0J : GG2HLL_PTV_250_400_GE1J;
780 else if (V.pt() < 600)
781 return GG2HLL_PTV_400_600;
782 return GG2HLL_PTV_GT600;
783 }
784 // 6.ttH,bbH,tH Stage 1.3 categories
785 else if (prodMode == HTXS::TTH) {
786 if (fwdHiggs)
787 return TTH_FWDH;
788 else
789 return Category(TTH_PTH_0_60 + getBin(higgs.pt(), {0, 60, 120, 200, 300, 450, 650}));
790 } else if (prodMode == HTXS::TH)
791 return Category(TH_FWDH + ctrlHiggs);
792 return UNKNOWN;
793 }
794
797 const Jets &jets, const Particle &V, const bool isTHW) const {
798 using namespace HTXS::Stage1_3_Fine;
799 if (prodMode == HTXS::BBH) {
800 const Category ggFCategory = getStage1_3_Fine_Category(HTXS::GGF, higgs, jets, V, isTHW);
801 if (ggFCategory == UNKNOWN) return UNKNOWN;
802 return Category(BBH_FWDH + static_cast<int>(ggFCategory) - GG2H_FWDH);
803 }
804
805 int Njets = jets.size(), ctrlHiggs = std::abs(higgs.rapidity()) < 2.5, fwdHiggs = !ctrlHiggs;
806 int vbfTopo = vbfTopology_Stage1_3_Fine(jets, higgs);
807
808 // 1. GGF Stage 1.3 categories (fine)
809 if (prodMode == HTXS::GGF || (prodMode == HTXS::GG2ZH && quarkDecay(V))) {
810 if (fwdHiggs) return GG2H_FWDH;
811 if (higgs.pt() > 200) {
812 if (Njets > 0) {
813 double pthj = (jets[0].momentum() + higgs.momentum()).pt();
814 if (pthj / higgs.pt() > 0.15)
815 return Category(GG2H_PTH_200_300_PTHJoverPTH_GT15 + getBin(higgs.pt(), {200, 300, 450, 650, 1000}));
816 else
817 return Category(GG2H_PTH_200_300_PTHJoverPTH_0_15 + getBin(higgs.pt(), {200, 300, 450, 650, 1000}));
818 } else
819 return Category(GG2H_PTH_200_300_PTHJoverPTH_0_15 + getBin(higgs.pt(), {200, 300, 450, 650, 1000}));
820 }
821 if (Njets == 0) return Category(GG2H_0J_PTH_0_5 + getBin(higgs.pt(), {0, 5, 10, 15, 20, 25, 30, 200}));
822 if (Njets == 1) return Category(GG2H_1J_PTH_0_30 + getBin(higgs.pt(), {0, 30, 60, 120, 200}));
823 if (Njets > 1) {
824 double mjj = (jets[0].mom()+jets[1].mom()).mass();
825 double pthjj = (jets[0].momentum() + jets[1].momentum() + higgs.momentum()).pt();
826 // VBF topology
827 if (mjj < 350){
828 if (pthjj < 25)
829 return Category(GG2H_GE2J_MJJ_0_350_PTH_0_30_PTHJJ_0_25 + getBin(higgs.pt(), {0, 30, 60, 120, 200}));
830 else
831 return Category(GG2H_GE2J_MJJ_0_350_PTH_0_30_PTHJJ_GT25 + getBin(higgs.pt(), {0, 30, 60, 120, 200}));
832 } else
834 }
835 }
836
837 // 2. Electroweak qq->Hqq Stage 1.3 categories (fine)
838 else if (prodMode == HTXS::VBF || (isVH(prodMode) && quarkDecay(V))) {
839 if (std::abs(higgs.rapidity()) > 2.5) return QQ2HQQ_FWDH;
840 int Njets = jets.size();
841 if (Njets == 0)
842 return QQ2HQQ_0J;
843 else if (Njets == 1)
844 return Category(QQ2HQQ_1J_PTH_0_200 + getBin(higgs.pt(), {0, 200, 450, 650}));
845 else if (Njets >= 2) {
846 double mjj = (jets[0].mom() + jets[1].mom()).mass();
847 double pthjj = (jets[0].momentum() + jets[1].momentum() + higgs.momentum()).pt();
848 if (mjj < 350) {
849 if (higgs.pt() < 200){
850 if (pthjj < 25)
851 return Category(QQ2HQQ_GE2J_MJJ_0_60_PTH_0_200_PTHJJ_0_25 + getBin(mjj, {0, 60, 120, 350}));
852 else
853 return Category(QQ2HQQ_GE2J_MJJ_0_60_PTH_0_200_PTHJJ_GT25 + getBin(mjj, {0, 60, 120, 350}));
854 } else {
855 if (pthjj < 25)
856 return Category(QQ2HQQ_GE2J_MJJ_0_60_PTH_GT200_PTHJJ_0_25 + getBin(mjj, {0, 60, 120, 350}));
857 else
858 return Category(QQ2HQQ_GE2J_MJJ_0_60_PTH_GT200_PTHJJ_GT25 + getBin(mjj, {0, 60, 120, 350}));
859 }
860 } else { // mjj>350 GeV
861 if (higgs.pt() < 200)
863 else if (higgs.pt() < 450)
865 else
866 return Category(QQ2HQQ_GE2J_MJJ_350_700_PTH_GT450 + getBin(mjj, {350, 700, 1000, 1500}));
867 }
868 }
869 }
870
871 // 3. WH->Hlv Stage 1.3 categories (fine)
872 else if (prodMode == HTXS::WH) {
873 if (fwdHiggs) return QQ2HLNU_FWDH;
874 int Njets = jets.size();
875 if (Njets == 0) return Category(QQ2HLNU_PTV_0_75_0J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
876 if (Njets == 1) return Category(QQ2HLNU_PTV_0_75_1J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
877 return Category(QQ2HLNU_PTV_0_75_GE2J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
878 }
879
880 // 4. qq->ZH->llH Stage 1.3 categories (fine)
881 else if (prodMode == HTXS::QQ2ZH) {
882 if (fwdHiggs) return QQ2HLL_FWDH;
883 int Njets = jets.size();
884 if (Njets == 0) return Category(QQ2HLL_PTV_0_75_0J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
885 if (Njets == 1) return Category(QQ2HLL_PTV_0_75_1J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
886 return Category(QQ2HLL_PTV_0_75_GE2J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
887 }
888
889 // 5. gg->ZH->llH Stage 1.3 categories (fine)
890 else if (prodMode == HTXS::GG2ZH) {
891 if (fwdHiggs) return GG2HLL_FWDH;
892 int Njets = jets.size();
893 if (Njets == 0) return Category(GG2HLL_PTV_0_75_0J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
894 if (Njets == 1) return Category(GG2HLL_PTV_0_75_1J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
895 return Category(GG2HLL_PTV_0_75_GE2J + getBin(V.pt(), {0, 75, 150, 250, 400, 600}));
896 }
897
898 // 6.ttH,bbH,tH Stage 1.3 categories (fine)
899 else if (prodMode == HTXS::TTH) {
900 if (fwdHiggs)
901 return TTH_FWDH;
902 else
903 return Category(TTH_PTH_0_60 + getBin(higgs.pt(), {0, 60, 120, 200, 300, 450, 650}));
904 } else if (prodMode == HTXS::TH)
905 return Category(THQ_FWDH + 2*isTHW + ctrlHiggs);
906 return UNKNOWN;
907 }
908
913
918 const Particles &decayparticles,
919 std::vector<float> &decay_observables,
920 int &cuts_passed) const {
921 using namespace HTXS::Stage0;
922
923 decay_observables = std::vector<float>(11, -999);
924 // 0: Z1m
925 // 1: Z2m
926 // 2: cthstr
927 // 3: phi
928 // 4: phi1
929 // 5: cth1
930 // 6: cth2
931 // 7-10: m14, m23, m13, m24
932
933 // Set up kinematics in the Higgs rest frame
934 auto &higgsmom = higgs.momentum();
935
936 double higgsm2 = higgsmom.invariant();
937 double higgsm = (higgsm2 > 0 ? sqrt(higgsm2) : 0);
938
939 const int N = static_cast<int>(decayparticles.size());
940 std::vector<double> Ep(N), pp(N);
941 std::vector<std::vector<double>> cosangle(N, std::vector<double>(N, 1.0));
942
943 LorentzTransform toHiggs = LorentzTransform::mkFrameTransform(higgsmom);
944 for (int i = 0; i < N; ++i) {
945 auto &mom = decayparticles[i].momentum();
946 double m2 = mom.invariant();
947 double hp = higgsmom * mom;
948 Ep[i] = hp / higgsm;
949 double p2v = Ep[i] * Ep[i] - m2;
950 pp[i] = (p2v > 0 ? sqrt(p2v) : 0);
951 for (int j = 0; j < i; ++j) {
952 auto &mom2 = decayparticles[j].momentum();
953 cosangle[i][j] = (Ep[i]*Ep[j] - mom*mom2) / (pp[i]*pp[j]);
954 cosangle[j][i] = cosangle[i][j];
955 }
956 }
957
958 // Work on a mutable copy boosted into the Higgs rest frame
959 Particles dp_rest = decayparticles;
960 for (int i = 0; i < N; ++i)
961 dp_rest[i] = dp_rest[i].transformBy(toHiggs);
962
963 // Accumulate identified objects into these vectors
964 std::vector<FourMomentum> v_p4{};
965 std::vector<int> v_pid{};
966
967 doAngleDressing(dp_rest, cosangle, v_p4, v_pid);
968
969 // findZZ4ldecay outputs a byte, last 6 bits indicate cuts passed
970 cuts_passed = findZZ4ldecay(std::move(v_p4), std::move(v_pid), decay_observables);
971 bool isZZ4l = (cuts_passed & 0b111111) == 0b111111;
972
973 // H -> ZZ* -> 4l
974 if (isZZ4l) {
975 switch ((cuts_passed >> 6) & 0b11) { // 0b xx 111111 (read @returns of findZZ4ldecay)
976 case 0b01: return HZZ4e;
977 case 0b10: return HZZ4mu;
978 case 0b11: return HZZ2e2mu;
979 case 0b00: MSG_WARNING("Found HZZ4l event but unable to categorise. This shouldn't happen!");
980 }
981 }
982
983 //Here other final states will be added in future
984
986 }
987
990 int doAngleDressing(const Particles &dp,
991 const std::vector<std::vector<double>> &cosangle,
992 std::vector<FourMomentum> &v_p4,
993 std::vector<int> &v_pid) const {
994 const double cos_cut = cos(0.1);
995 int num_photons = 0;
996 // dressers[i] contains the index of photons that get dressed to dp[i]
997 std::vector<std::vector<int>> dressers(dp.size());
998
999 // Loop over all photons to find their closest lepton
1000 for (size_t i = 0; i < dp.size(); i++) {
1001 const Particle &p = dp[i];
1002 if (p.pid() != 22) continue;
1003 int max_cos_index = -1;
1004 for (size_t j = 0; j < dp.size(); j++) {
1005 if (!(PID::isElectron(dp[j].pid()) || PID::isMuon(dp[j].pid()))) continue;
1006 if (cosangle[i][j] < cos_cut) continue;
1007 if (max_cos_index != -1 && cosangle[i][j] < cosangle[i][max_cos_index]) continue;
1008 max_cos_index = j;
1009 }
1010 if(max_cos_index != -1)
1011 dressers[max_cos_index].push_back(i);
1012 else {
1013 v_p4.push_back(p.momentum());
1014 v_pid.push_back(p.pid());
1015 }
1016 }
1017
1018 // Then loop over leptons to dress photons into it
1019 for (size_t i = 0; i < dp.size(); i++) {
1020 const Particle &p = dp[i];
1021 if (!(PID::isElectron(p.pid()) || PID::isMuon(p.pid()))) continue;
1022 v_p4.push_back(p.momentum());
1023 v_pid.push_back(p.pid());
1024 for (auto photon : dressers[i]) {
1025 v_p4.back() += dp[photon].momentum();
1026 num_photons++;
1027 }
1028 }
1029 return num_photons;
1030 }
1031
1036 unsigned char findZZ4ldecay(std::vector<FourMomentum> v_p4,
1037 std::vector<int> v_pid,
1038 std::vector<float> &decay_observables) const {
1039
1040 int lepton_num = 0;
1041 for (int pid : v_pid) lepton_num += (std::abs(pid) == 11 || std::abs(pid) == 13);
1042
1043 std::vector<int> lepton_index{};
1044
1045 unsigned char cut_passed = 0;
1046 // bit 0: lepton_num, bit 1: > 4 leptons after removing mij < 5, bit 2: m12, bit 3: m34, bit 4: angle ij > 0.1, bit 5: higgs mass
1047 cut_passed |= (lepton_num >= 4);
1048 cut_passed |= ZZ4lJpsi_cut(v_p4, v_pid) << 1;
1049 cut_passed |= ZZ4lm12m34_cut(v_p4, v_pid, lepton_index) << 2; // <- this outputs 2 bits of data
1050 cut_passed |= ZZ4langle_cut(v_p4, v_pid) << 4; // that's why it's << 4 here
1051
1052 FourMomentum hsum{};
1053 for (uint i = 0; i < v_pid.size(); ++i) {
1054 if (std::abs(v_pid[i]) == 11 || std::abs(v_pid[i]) == 13) {
1055 hsum += v_p4[i];
1056 }
1057 }
1058
1059 cut_passed |= (hsum.mass() > 105 && hsum.mass() < 130) << 5;
1060
1061 // We put additional 2 bits of info to cut_passed to determine if is 4e, 2e2mu, or 4mu!
1062 // side note: [0] and [2] takes 1 particle from each lepton pair
1063 if(lepton_index.size() == 4) {
1064 if (PID::isElectron(v_pid[lepton_index[0]]) || PID::isElectron(v_pid[lepton_index[2]]))
1065 cut_passed |= 0b01000000; // 0b01111111 => 4e
1066 if (PID::isMuon(v_pid[lepton_index[0]]) || PID::isMuon(v_pid[lepton_index[2]]))
1067 cut_passed |= 0b10000000; // 0b10111111 => 4mu
1068 // 0b11111111 => 2e2mu
1069 }
1070
1071 calculate_decay_observables(v_p4, lepton_index, decay_observables);
1072 return cut_passed;
1073 }
1074
1076 bool ZZ4langle_cut(const std::vector<FourMomentum> &v_p4,
1077 const std::vector<int> &v_pid) const {
1078
1079 std::vector<FourMomentum> v_p4_lepton = v_p4;
1080
1081 const double cos_cut = cos(0.1);
1082 double cos_max = 0;
1083 for (uint i = 0; i < v_p4_lepton.size(); ++i) {
1084 if ( !(PID::isMuon(v_pid[i]) || PID::isElectron(v_pid[i])) ) continue;
1085 for (uint j = 0; j < i; ++j) {
1086 if ( !(PID::isMuon(v_pid[j]) || PID::isElectron(v_pid[j])) ) continue;
1087 double ca = (v_p4_lepton[i].E()*v_p4_lepton[j].E() - v_p4_lepton[i]*v_p4_lepton[j])
1088 / (v_p4_lepton[i].p()*v_p4_lepton[j].p());
1089 cos_max = std::max(ca, cos_max);
1090 }
1091 }
1092 return cos_cut > cos_max;
1093 }
1094
1099 unsigned char ZZ4lm12m34_cut(const std::vector<FourMomentum> &v_p4,
1100 const std::vector<int> &v_pid,
1101 std::vector<int> &lepton_index) const {
1102 std::vector<int> lepIdx;
1103 for (size_t i = 0; i < v_pid.size(); ++i)
1104 if (std::abs(v_pid[i]) == 11 || std::abs(v_pid[i]) == 13) lepIdx.push_back(i);
1105 if (lepIdx.size() < 2) return 0b00;
1106
1107 const double mZ = 91.1876;
1108
1109 double bestDM = 1e30;
1110 double bestM12 = -1, bestM34 = -1;
1111 size_t bestpair[4] = {999, 999, 999, 999};
1112 for (size_t i = 0; i < lepIdx.size(); i++) { // find pair for m12 first
1113 for (size_t j = i+1; j < lepIdx.size(); j++) {
1114 int a=lepIdx[i], b=lepIdx[j];
1115 if(v_pid[a] != -v_pid[b]) continue; // not SFOS
1116 double m12cur = (v_p4[a] + v_p4[b]).mass();
1117 if (std::fabs(m12cur - mZ) >= bestDM) continue;
1118 bestDM = std::fabs(m12cur - mZ);
1119 bestpair[0] = i; bestpair[1] = j;
1120 bestM12 = m12cur;
1121 }
1122 }
1123
1124 // Find pair for m34
1125 for (size_t i = 0; i < lepIdx.size(); i++) {
1126 if (i == bestpair[0] || i == bestpair[1]) continue;
1127 for (size_t j = i+1; j < lepIdx.size(); j++) {
1128 if (j == bestpair[0] || j == bestpair[1]) continue;
1129
1130 int a=lepIdx[i], b=lepIdx[j];
1131 if(v_pid[a] != -v_pid[b]) continue; // not SFOS
1132 double m34cur = (v_p4[a] + v_p4[b]).mass();
1133 if (m34cur <= bestM34) continue;
1134 bestpair[2] = i; bestpair[3] = j;
1135 bestM34 = m34cur;
1136 }
1137 }
1138
1139 if (bestpair[0] == 999) {
1140 MSG_WARNING("Unable to find lepton pairings, returning null");
1141 return 0b00;
1142 }
1143
1144 for (int i = 0; i < 4; i++) {
1145 if (bestpair[i] == 999) break;
1146 if(v_pid[lepIdx[bestpair[i]]] < 0 && ((i % 2) == 0)) { // we are at [0] or [2]... supposed to put matter
1147 std::swap(bestpair[i], bestpair[i+1]); // so we swap
1148 }
1149 lepton_index.push_back(lepIdx[bestpair[i]]);
1150 }
1151
1152 return passm12(bestM12) | (passm34(bestM34) << 1);
1153 }
1154
1158 bool ZZ4lJpsi_cut(std::vector<FourMomentum> &v_p4,
1159 std::vector<int> &v_pid) const {
1160
1161 while (remove_least_mij(v_p4, v_pid));
1162 int lepton_num = 0;
1163 for (int pid : v_pid) lepton_num += (std::abs(pid) == 11 || std::abs(pid) == 13);
1164 return lepton_num >= 4;
1165 }
1166
1169 bool remove_least_mij(std::vector<FourMomentum> &v_p4,
1170 std::vector<int> &v_pid) const {
1171 float mij_min = 5.0;
1172 size_t min_pair[2] = {999, 999};
1173 for (size_t i = 0; i < v_p4.size(); i++) {
1174 for (size_t j = i+1; j < v_p4.size(); j++) {
1175 if (v_pid[i] + v_pid[j] != 0) continue;
1176 if (!(PID::isMuon(v_pid[i]) || PID::isElectron(v_pid[i]))) continue;
1177 float mij = (v_p4[i] + v_p4[j]).mass();
1178 if (mij >= mij_min) continue;
1179 mij_min = mij;
1180 min_pair[0] = i;
1181 min_pair[1] = j;
1182 }
1183 }
1184 if (min_pair[0] == 999) return false;
1185 v_p4.erase(v_p4.begin() + min_pair[1]); // min_pair[1] > min_pair[0] always, so this is safe
1186 v_p4.erase(v_p4.begin() + min_pair[0]);
1187
1188 v_pid.erase(v_pid.begin() + min_pair[1]);
1189 v_pid.erase(v_pid.begin() + min_pair[0]);
1190
1191 return true;
1192 }
1193
1195 bool calculate_decay_observables(const std::vector<FourMomentum> &v_p4,
1196 const std::vector<int> &v_4l_index,
1197 std::vector<float> &decay_observable) const {
1198
1199 if (v_4l_index.size() != 4) {
1200 if (v_4l_index.size() >= 2) decay_observable[0] = (v_p4[v_4l_index[0]] + v_p4[v_4l_index[1]]).mass();
1201 return false;
1202 }
1203
1204 FourMomentum v1 = (v_p4[v_4l_index[0]]);
1205 FourMomentum v2 = (v_p4[v_4l_index[1]]);
1206 FourMomentum v3 = (v_p4[v_4l_index[2]]);
1207 FourMomentum v4 = (v_p4[v_4l_index[3]]);
1208
1209 float Z1m = (v1 + v2).mass();
1210 float Z2m = (v3 + v4).mass();
1211
1212 float m14 = (v1 + v4).mass();
1213 float m23 = (v2 + v3).mass();
1214 float m13 = (v1 + v3).mass();
1215 float m24 = (v2 + v4).mass();
1216
1217 FourMomentum Z1 = ( v1 + v2 );
1218 FourMomentum Z2 = ( v3 + v4 );
1219
1220 Vector3 z1 = Z1.vector3().unit();
1221 Vector3 z2 = Z2.vector3().unit();
1222
1223 // Costh*
1224 float cthstr = z1.z();
1225
1226 Vector3 v1p = v1.vector3();
1227 Vector3 v2p = v2.vector3();
1228 Vector3 v3p = v3.vector3();
1229 Vector3 v4p = v4.vector3();
1230 Vector3 nz(0, 0, 1.);
1231
1232 // Phi, Phi1
1233 Vector3 n1p = v1p.cross(v2p).unit();
1234 Vector3 n2p = v3p.cross(v4p).unit();
1235 Vector3 nscp = nz.cross(z1).unit();
1236 float phi = (z1.dot(n1p.cross(n2p)) / std::fabs(z1.dot(n1p.cross(n2p))) *
1237 std::acos(-n1p.dot(n2p)));
1238 float phi1 = (z1.dot(n1p.cross(nscp)) / std::fabs(z1.dot(n1p.cross(nscp))) *
1239 std::acos(n1p.dot(nscp)));
1240
1241 // Costh1,2
1242 LorentzTransform toZ1 = LorentzTransform::mkFrameTransform(Z1);
1243 LorentzTransform toZ2 = LorentzTransform::mkFrameTransform(Z2);
1244
1245 FourMomentum Z2_rfr_Z1 = toZ1.transform(Z2); // now it's in Z1 RFR (both Z1 and Z2 are in H RFR)
1246 Vector3 z2_rfr_Z1 = Z2_rfr_Z1.vector3();
1247
1248 FourMomentum Z1_rfr_Z2 = toZ2.transform(Z1); // now it's in Z2 RFR (both Z1 and Z2 are still in H RFR)
1249 Vector3 z1_rfr_Z2 = Z1_rfr_Z2.vector3();
1250
1251 FourMomentum v1_rfr_Z1 = toZ1.transform(v1); // Z1 and Z2 still in H RFR: put leptons
1252 // in their Z's reference frame
1253 FourMomentum v3_rfr_Z2 = toZ2.transform(v3);
1254
1255 float cth1 = -(z2_rfr_Z1.dot(v1_rfr_Z1.vector3()) /
1256 std::fabs(z2_rfr_Z1.mod() * v1_rfr_Z1.vector3().mod()));
1257 float cth2 = -(z1_rfr_Z2.dot(v3_rfr_Z2.vector3()) /
1258 std::fabs(z1_rfr_Z2.mod() * v3_rfr_Z2.vector3().mod()));
1259
1260 decay_observable[0] = Z1m;
1261 decay_observable[1] = Z2m;
1262 decay_observable[2] = cthstr;
1263 decay_observable[3] = phi;
1264 decay_observable[4] = phi1;
1265 decay_observable[5] = cth1;
1266 decay_observable[6] = cth2;
1267 decay_observable[7] = m14;
1268 decay_observable[8] = m23;
1269 decay_observable[9] = m13;
1270 decay_observable[10] = m24;
1271 return true;
1272 }
1273
1275 bool passm12(double m) const { return (m > 50.0 && m < 106.0); }
1276
1278 bool passm34(double m) const { return (m > 12.0 && m < 115.0); }
1279
1281
1282
1285
1288
1291
1295 void init() {
1296 printf("==============================================================\n");
1297 printf("======== HiggsTemplateCrossSections Initialization =========\n");
1298 printf("==============================================================\n");
1299 // check that the production mode has been set
1300 // if running in standalone Rivet the production mode is set through an env variable
1302 char *pm_env = getenv("HIGGSPRODMODE");
1303 string pm(pm_env==nullptr?"":pm_env);
1304 if ( pm == "GGF" ) m_HiggsProdMode = HTXS::GGF;
1305 else if ( pm == "VBF" ) m_HiggsProdMode = HTXS::VBF;
1306 else if ( pm == "WH" ) m_HiggsProdMode = HTXS::WH;
1307 else if ( pm == "ZH" ) m_HiggsProdMode = HTXS::QQ2ZH;
1308 else if ( pm == "QQ2ZH" ) m_HiggsProdMode = HTXS::QQ2ZH;
1309 else if ( pm == "GG2ZH" ) m_HiggsProdMode = HTXS::GG2ZH;
1310 else if ( pm == "TTH" ) m_HiggsProdMode = HTXS::TTH;
1311 else if ( pm == "BBH" ) m_HiggsProdMode = HTXS::BBH;
1312 else if ( pm == "TH" ) m_HiggsProdMode = HTXS::TH;
1313 else {
1314 MSG_WARNING("No HIGGSPRODMODE shell variable found. Needed when running Rivet stand-alone.");
1315 }
1316 }
1317
1318 // Projections for final state particles
1319 const FinalState FS;
1320 declare(FS,"FS");
1321
1322 // initialize the histograms with for each of the stages
1324 m_sumw = 0.0;
1325 printf("==============================================================\n");
1326 printf("======== Higgs prod mode %d =========\n",m_HiggsProdMode);
1327 printf("======== Sucessful Initialization =========\n");
1328 printf("==============================================================\n");
1329 }
1330
1331 // Perform the per-event analysis
1332 void analyze(const Event& event) {
1333
1334 // get the classification
1335 HiggsClassification cat = classifyEvent(event,m_HiggsProdMode,m_HiggsDecayMode);
1336
1337 // Fill histograms: categorization --> linerize the categories
1338 const double weight = 1.; // Event weights are now all 1 in Rivet
1339 m_sumw += weight;
1340
1341 int F=cat.stage0_cat%10, P=cat.stage1_cat_pTjet30GeV/100;
1342 m_hist_stage0->fill( cat.stage0_cat/10*2+F, weight );
1343
1344 // Stage 1 enum offsets for each production mode: GGF=12, VBF=6, WH= 5, QQ2ZH=5, GG2ZH=4, TTH=2, BBH=2, TH=2
1345 static const vector<int> offset({0,1,13,19,24,29,33,35,37,39});
1346 int off = offset[P];
1347 // Stage 1.2 enum offsets for each production mode: GGF=17, VBF=11, WH= 6, QQ2ZH=6, GG2ZH=6, TTH=6, BBH=2, TH=2
1348 static const vector<int> offset1_2({0,1,18,29,35,41,47,53,55,57});
1349 int off1_2 = offset1_2[P];
1350 // Stage 1.2-Fine enum offsets for each production mode: GGF=28, VBF=25, WH= 16, QQ2ZH=16, GG2ZH=16, TTH=7, BBH=2, TH=2
1351 static const vector<int> offset1_2_Fine({0,1,29,54,70,86,102,109,111,113});
1352 int off1_2_Fine = offset1_2_Fine[P];
1353 // Stage 1_3 enum offsets for each production mode: GGF=25, VBF=15, WH=9, QQ2ZH=9, GG2ZH=9, TTH=8, BBH=25, TH=2
1354 static const vector<int> offset1_3({0,1,26,41,50,59,68,76,101,103});
1355 int off1_3 = offset1_3[P];
1356 // Stage 1_3 Fine enum offsets for each production mode: GGF=62, VBF=86, WH=19, QQ2ZH=19, GG2ZH=19, TTH=8, BBH=62, TH=4
1357 static const vector<int> offset1_3_fine({0,1,63,149,168,187,206,214,276,280});
1358 int off1_3_fine = offset1_3_fine[P];
1359
1360
1361 m_hist_stage1_pTjet25->fill(cat.stage1_cat_pTjet25GeV%100 + off, weight);
1362 m_hist_stage1_pTjet30->fill(cat.stage1_cat_pTjet30GeV%100 + off, weight);
1363 m_hist_stage1_2_pTjet25->fill(cat.stage1_2_cat_pTjet25GeV%100 + off1_2, weight);
1364 m_hist_stage1_2_pTjet30->fill(cat.stage1_2_cat_pTjet30GeV%100 + off1_2, weight);
1365 m_hist_stage1_2_fine_pTjet25->fill(cat.stage1_2_fine_cat_pTjet25GeV%100 + off1_2_Fine, weight);
1366 m_hist_stage1_2_fine_pTjet30->fill(cat.stage1_2_fine_cat_pTjet30GeV%100 + off1_2_Fine, weight);
1367 m_hist_stage1_3_pTjet25->fill(cat.stage1_3_cat_pTjet25GeV%100 + off1_3, weight);
1368 m_hist_stage1_3_pTjet30->fill(cat.stage1_3_cat_pTjet30GeV%100 + off1_3, weight);
1369 m_hist_stage1_3_fine_pTjet25->fill(cat.stage1_3_fine_cat_pTjet25GeV%100 + off1_3_fine, weight);
1370 m_hist_stage1_3_fine_pTjet30->fill(cat.stage1_3_fine_cat_pTjet30GeV%100 + off1_3_fine, weight);
1371
1372 // Fill histograms: variables used in the categorization
1373 m_hist_pT_Higgs->fill(cat.higgs.pT(),weight);
1374 m_hist_y_Higgs->fill(cat.higgs.rapidity(),weight);
1375 m_hist_pT_V->fill(cat.V.pT(),weight);
1376
1377 m_hist_Njets25->fill(cat.jets25.size(),weight);
1378 m_hist_Njets30->fill(cat.jets30.size(),weight);
1379
1380 m_hist_isZ2vv->fill(cat.isZ2vvDecay, weight);
1381
1382 // Jet variables. Use jet collection with pT threshold at 30 GeV
1383 if (cat.jets30.size()) m_hist_pT_jet1->fill(cat.jets30[0].pt(),weight);
1384 if (cat.jets30.size()>=2) {
1385 const FourMomentum &j1 = cat.jets30[0].momentum(), &j2 = cat.jets30[1].momentum();
1386 m_hist_deltay_jj->fill(std::abs(j1.rapidity()-j2.rapidity()),weight);
1387 m_hist_dijet_mass->fill((j1+j2).mass(),weight);
1388 m_hist_pT_Hjj->fill((j1+j2+cat.higgs.momentum()).pt(),weight);
1389 }
1390 }
1391
1393 MSG_INFO (" ====================================================== ");
1394 MSG_INFO (" Higgs Template X-Sec Categorization Tool ");
1395 MSG_INFO (" Status Code Summary ");
1396 MSG_INFO (" ====================================================== ");
1397 bool allSuccess = (numEvents()==m_errorCount[HTXS::SUCCESS]);
1398 if ( allSuccess ) MSG_INFO (" >>>> All "<< m_errorCount[HTXS::SUCCESS] <<" events successfully categorized!");
1399 else{
1400 MSG_INFO (" >>>> "<< m_errorCount[HTXS::SUCCESS] <<" events successfully categorized");
1401 MSG_INFO (" >>>> --> the following errors occured:");
1402 MSG_INFO (" >>>> "<< m_errorCount[HTXS::PRODMODE_DEFINED] <<" had an undefined Higgs production mode.");
1403 MSG_INFO (" >>>> "<< m_errorCount[HTXS::MOMENTUM_CONSERVATION] <<" failed momentum conservation.");
1404 MSG_INFO (" >>>> "<< m_errorCount[HTXS::HIGGS_IDENTIFICATION] <<" failed to identify a valid Higgs boson.");
1405 MSG_INFO (" >>>> "<< m_errorCount[HTXS::HS_VTX_IDENTIFICATION] <<" failed to identify the hard scatter vertex.");
1406 MSG_INFO (" >>>> "<< m_errorCount[HTXS::VH_IDENTIFICATION] <<" VH: to identify a valid V-boson.");
1407 MSG_INFO (" >>>> "<< m_errorCount[HTXS::TOP_W_IDENTIFICATION] <<" failed to identify valid Ws from top decay.");
1408 }
1409 MSG_INFO (" ====================================================== ");
1410 MSG_INFO (" ====================================================== ");
1411 }
1412
1413
1421
1422 /*
1423 * initialize histograms
1424 */
1425
1427 book(m_hist_stage0,"HTXS_stage0",20,0,20);
1428 book(m_hist_stage1_pTjet25,"HTXS_stage1_pTjet25",40,0,40);
1429 book(m_hist_stage1_pTjet30,"HTXS_stage1_pTjet30",40,0,40);
1430 book(m_hist_stage1_2_pTjet25,"HTXS_stage1_2_pTjet25",57,0,57);
1431 book(m_hist_stage1_2_pTjet30,"HTXS_stage1_2_pTjet30",57,0,57);
1432 book(m_hist_stage1_2_fine_pTjet25,"HTXS_stage1_2_fine_pTjet25",113,0,113);
1433 book(m_hist_stage1_2_fine_pTjet30,"HTXS_stage1_2_fine_pTjet30",113,0,113);
1434 book(m_hist_stage1_3_pTjet25, "STXS_stage1_3_pTjet25", 103, 0, 103);
1435 book(m_hist_stage1_3_pTjet30, "STXS_stage1_3_pTjet30", 103, 0, 103);
1436 book(m_hist_stage1_3_fine_pTjet25, "STXS_stage1_3_fine_pTjet25", 280, 0, 280);
1437 book(m_hist_stage1_3_fine_pTjet30, "STXS_stage1_3_fine_pTjet30", 280, 0, 280);
1438 book(m_hist_pT_Higgs,"pT_Higgs",80,0,400);
1439 book(m_hist_y_Higgs,"y_Higgs",80,-4,4);
1440 book(m_hist_pT_V,"pT_V",80,0,400);
1441 book(m_hist_pT_jet1,"pT_jet1",80,0,400);
1442 book(m_hist_deltay_jj ,"deltay_jj",50,0,10);
1443 book(m_hist_dijet_mass,"m_jj",50,0,2000);
1444 book(m_hist_pT_Hjj,"pT_Hjj",50,0,250);
1445 book(m_hist_Njets25,"Njets25",10,0,10);
1446 book(m_hist_Njets30,"Njets30",10,0,10);
1447 book(m_hist_isZ2vv,"isZ2vv",2,0,2);
1448 }
1449
1450
1451 /*
1452 * initialize private members used in the classification procedure
1453 */
1454
1455 private:
1456 double m_sumw=0.0;
1459 mutable std::array<std::atomic<size_t>, HTXS::NUM_ERRORCODES> m_errorCount ATLAS_THREAD_SAFE {};
1460 Histo1DPtr m_hist_stage0;
1470 Histo1DPtr m_hist_isZ2vv;
1471 };
1472
1473 // the PLUGIN only needs to be decleared when running standalone Rivet
1474 // and causes compilation / linking issues if included in Athena / RootCore
1475 //check for Rivet environment variable RIVET_ANALYSIS_PATH
1476#ifdef RIVET_ANALYSIS_PATH
1477 // The hook for the plugin system
1478 DECLARE_RIVET_PLUGIN(HiggsTemplateCrossSections);
1479#endif
1480
1481}
1482
1483#endif
Scalar deltaPhi(const MatrixBase< Derived > &vec) const
Scalar phi() const
phi method
unsigned int uint
static Double_t a
static Double_t P(Double_t *tt, Double_t *par)
#define F(x, y, z)
Definition MD5.cxx:112
static const std::vector< std::string > bins
#define pi
#define x
double hsum(TH1 *h)
sum the bin contents including the over and underflow bins
Definition chains.cxx:101
Define macros for attributes used to control the static checker.
Rivet routine for classifying MC events according to the Higgs template cross section categories.
int doAngleDressing(const Particles &dp, const std::vector< std::vector< double > > &cosangle, std::vector< FourMomentum > &v_p4, std::vector< int > &v_pid) const
dress leptons with photons within a 0.1 radian angle.
bool ZZ4langle_cut(const std::vector< FourMomentum > &v_p4, const std::vector< int > &v_pid) const
Reject lepton pairs whose opening angle is too small (cos > cos(0.1)).
int vbfTopology_Stage1_2_Fine(const Jets &jets, const Particle &higgs) const
VBF topology selection for Stage1_2 0 = fail loose selection: m_jj > 350 GeV 1 pass loose,...
bool calculate_decay_observables(const std::vector< FourMomentum > &v_p4, const std::vector< int > &v_4l_index, std::vector< float > &decay_observable) const
calculate the relevant 4l decay observables
HiggsClassification classifyEvent(const Event &event, const HTXS::HiggsProdMode prodMode, const HTXS::HiggsDecayMode decayMode) const
Main classificaion method.
HiggsClassification error(HiggsClassification &cat, HTXS::ErrorCode err, std::string_view msg={}, int NmaxWarnings=20) const
Returns the classification object with the error code set.
bool originateFrom(const Particle &p, const Particle &p2) const
Whether particle p originates from p2.
void setHiggsProdMode(HTXS::HiggsProdMode prodMode)
Sets the Higgs production mode.
void init()
default Rivet Analysis::init method Booking of histograms, initializing Rivet projection Extracts Hig...
bool originateFrom(const Particle &p, const Particles &ptcls) const
Whether particle p originate from any of the ptcls.
int vbfTopology_Stage1_2(const Jets &jets, const Particle &higgs) const
VBF topology selection 0 = fail loose selection: m_jj > 350 GeV 1 pass loose, but fail additional cut...
HTXS::Stage1_3::Category getStage1_3_Category(const HTXS::HiggsProdMode prodMode, const Particle &higgs, const Jets &jets, const Particle &V) const
Stage-1.3 categorization.
bool isVH(HTXS::HiggsProdMode p) const
Whether the Higgs is produced in association with a vector boson (VH).
unsigned char findZZ4ldecay(std::vector< FourMomentum > v_p4, std::vector< int > v_pid, std::vector< float > &decay_observables) const
Select H->ZZ->4l candidate: 4 charged leptons, net charge=0, combined invariant mass in [105,...
Particle getLastInstance(const Particle &ptcl) const
follow a "propagating" particle and return its last instance
HTXS::Stage1_2_Fine::Category getStage1_2_Fine_Category(const HTXS::HiggsProdMode prodMode, const Particle &higgs, const Jets &jets, const Particle &V) const
Stage-1.2_Fine categorization.
bool remove_least_mij(std::vector< FourMomentum > &v_p4, std::vector< int > &v_pid) const
removes SFOS lepton pairs with invariant mass < 5GeV removes only the pair with the least invariant m...
bool quarkDecay(const Particle &p) const
Return true is particle decays to quarks.
bool hasParent(HepMC::ConstGenParticlePtr ptcl, int pdgID) const
Checks whether the input particle has a parent with a given PDGID.
int vbfTopology_Stage1_3_Fine(const Jets &jets, const Particle &higgs) const
VBF topology selection for Stage1_3 Includes additional deltaphijj binning.
bool passm34(double m) const
Pass sub-leading lepton-pair mass window [12, 115] GeV.
int getBin(double x, const std::vector< double > &bins) const
Return bin index of x given the provided bin edges. 0=first bin, -1=underflow bin.
void setHiggsDecayMode(HTXS::HiggsDecayMode decayMode)
Sets the Higgs production mode.
bool hasChild(HepMC::ConstGenParticlePtr ptcl, int pdgID) const
Checks whether the input particle has a child with a given PDGID.
HTXS::Stage1::Category getStage1Category(const HTXS::HiggsProdMode prodMode, const Particle &higgs, const Jets &jets, const Particle &V) const
Stage-1 categorization.
HTXS::Stage1_3_Fine::Category getStage1_3_Fine_Category(const HTXS::HiggsProdMode prodMode, const Particle &higgs, const Jets &jets, const Particle &V, const bool isTHW) const
Stage-1.3 Fine categorization.
HTXS::Stage0::DecayCategory getStage0DecayCategory(const Particle &higgs, const Particles &decayparticles, std::vector< float > &decay_observables, int &cuts_passed) const
Stage-0 Higgs decay categorization.
HTXS::Stage0::Category getStage0Category(const HTXS::HiggsProdMode prodMode, const Particle &higgs, const Particle &V) const
Stage-0 HTXS categorization.
bool ChLeptonDecay(const Particle &p) const
Return true if particle decays to charged leptons.
bool passm12(double m) const
Pass leading lepton-pair mass window [50, 106] GeV.
HTXS::Stage1_2::Category getStage1_2_Category(const HTXS::HiggsProdMode prodMode, const Particle &higgs, const Jets &jets, const Particle &V) const
Stage-1.2 categorization.
std::array< std::atomic< size_t >, HTXS::NUM_ERRORCODES > m_errorCount ATLAS_THREAD_SAFE
int vbfTopology(const Jets &jets, const Particle &higgs) const
VBF topolog selection 0 = fail loose selction: m_jj > 400 GeV and Dy_jj > 2.8 1 pass loose,...
bool ZZ4lJpsi_cut(std::vector< FourMomentum > &v_p4, std::vector< int > &v_pid) const
J/psi veto: reject events with a same-flavour opposite-sign lepton pair with invariant mass below 5 G...
unsigned char ZZ4lm12m34_cut(const std::vector< FourMomentum > &v_p4, const std::vector< int > &v_pid, std::vector< int > &lepton_index) const
Apply m12 and m34 mass window cuts for ZZ->4l selection Also insert the leading lepton pair and suble...
The namespace of all packages in PhysicsAnalysis/JetTagging.
Namespace for Stage0 categorization.
Categorization Stage 1.2: Three digit integer of format PF Where P is a digit representing the proces...
Categorization Stage 1.3: Three digit integer of format PF Where P is a digit representing the proces...
Categorization Stage 1: Three digit integer of format PF Where P is a digit representing the process ...
Higgs Template Cross Section namespace.
HiggsDecayMode
Two digit number of format PF P is digit for the physics process and F is 0 for |yH|>2....
ErrorCode
Error code: whether the classification was successful or failed.
@ HS_VTX_IDENTIFICATION
failed to identify hard scatter vertex
@ PRODMODE_DEFINED
production mode not defined
@ SUCCESS
successful classification
@ VH_DECAY_IDENTIFICATION
failed to identify associated vector boson decay products
@ HIGGS_IDENTIFICATION
failed to identify Higgs boson
@ TOP_W_IDENTIFICATION
failed to identify top decay
@ HIGGS_DECAY_IDENTIFICATION
failed to identify Higgs boson decay products
@ MOMENTUM_CONSERVATION
failed momentum conservation
@ NUM_ERRORCODES
number of error codes (keep this unnumbered and last)
@ VH_IDENTIFICATION
failed to identify associated vector boson
HiggsProdMode
Higgs production modes, corresponding to input sample.
ConstGenVertexPtr signal_process_vertex(const GenEvent *e)
Definition GenEvent.h:597
HepMC3::ConstGenParticlePtr ConstGenParticlePtr
Definition GenParticle.h:20
Definition Jets.py:1
void swap(ElementLinkVector< DOBJ > &lhs, ElementLinkVector< DOBJ > &rhs)
#define MSG_WARNING(ARG)
#define MSG_INFO(ARG)
MsgStream & msg
Definition testRead.cxx:32