ATLAS Offline Software
Loading...
Searching...
No Matches
PseudoJetContainer.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3*/
4
5// PseudoJetContainer.cxx
6
10#include <map>
11#include "xAODJet/Jet.h"
12#include "fastjet/PseudoJet.hh"
13#include "JetRec/LineFormatter.h" // helper class for debug printing
14#include <sstream>
15#include <ios>
16#include <iostream>
17#include <exception>
18
19using fastjet::PseudoJet;
20
22 checkInvariants("PseudoJetContainer()");
23}
24
25PseudoJetContainer::PseudoJetContainer(std::unique_ptr<const IConstituentExtractor> c,
26 const std::vector<PseudoJet> & vecPJ,
27 bool debug){
28
29 m_debug = debug;
30
31 if (vecPJ.empty()){
32 m_emptyExtractors.insert(std::move(c));
33 return;
34 }
35
36 if (m_debug){checkInConstituents(vecPJ, "constuctor");}
37 m_allConstituents = vecPJ;
38
39 // the limits of the Extractor index range correposnd to the
40 // numbering of the EDM objects in th extractors.
41 m_extractorRanges.emplace_back(0,
42 vecPJ.back().user_index(),
43 std::move(c));
44 if (m_debug){checkInvariants("PseudoJetContainer(vcPJ, c)");}
45}
46
47bool
49 const std::vector<PseudoJet>& inConstits) const
50{
51
52 // check container integrity if m_debug == true
53 if (m_debug){checkInvariants("extractConstituents()");}
54
55 // set up mapping to acquire the EDM indices by Extractor
56 std::map<const IConstituentExtractor*, std::vector<int>> sorter;
57
58 // add an entry into the sorter for each extractor.
59 // this way each extractor will be called, possibly with
60 // an empty index list - this will happen if no pseudojet correponding
61 // to the extractor is received. But an empty list is used
62 // by the extractors to fill zeros into the jet.
63 for(const auto& er : m_extractorRanges){
64 sorter.emplace(er.m_e.get(), std::vector<int>{});
65 }
66
67 // see header file for explanation of empty extractors.
68 for(const auto& e : m_emptyExtractors){
69 sorter.emplace(e.get(), std::vector<int>{});
70 }
71
72 // check whether inputs are lgal if m_debug == true
73 if (m_debug){checkInConstituents(inConstits, "extractConsituents()");}
74
75 for (const auto& pj : inConstits){
76
77 int pj_ind = pj.user_index();
78
79 // fastjet clustering may add in pseudojets with the default (-1) index.
80 // ignore these.
81 if (pj_ind == -1) {continue;}
82
83 // The index of the pseudojet and the low limit
84 // the extractorRange table (which acts as an offset), identifies which
85 // extractorRanges entry to use, and the EDM object that corrsponds to the
86 // pseudojet.
87
88 auto e = std::find_if(m_extractorRanges.begin(),
90 [pj_ind](const ExtractorRange& r){
91 return pj_ind >= r.m_lo and pj_ind <= r.m_hi;});
92
93 if(e == m_extractorRanges.end()){return false;}
94
95 sorter[(*e).m_e.get()].push_back(pj_ind - (*e).m_lo);
96 }
97
98 // send the jet to the each extractor with a vector of EDM
99 // indices for that extractor
100 for(const auto& s : sorter){s.first -> addToJet(jet, s.second);}
101 return true;
102}
103
104
105// fill xAOD jet with constit&ghosts extracted from final PSeudoJet
107 const PseudoJet& finalPJ) const {
108
109 std::vector<PseudoJet> inConstituents = finalPJ.constituents();
110 return extractConstituents(jet, inConstituents);
111}
112
113// fill xAOD jet with correct by-vertex consituents and ghosts
115 std::vector<PseudoJet> allInConstituents = finalPJ.constituents();
116 std::vector<PseudoJet> byVertexConstituents;
117 for (PseudoJet& constituent: allInConstituents){
118 if (constituent.has_user_info<jet::VertexIndexedConstituentUserInfo>()){
119 const jet::VertexIndexedConstituentUserInfo& userInfo = constituent.user_info<jet::VertexIndexedConstituentUserInfo>();
120 const xAOD::Vertex* originVertex = userInfo.vertex();
121
122 // if constituent has relevant user info, only accept it if it matches the vertex in question
123 if (originVertex == vertex){
124 byVertexConstituents.emplace_back(constituent);
125 }
126 }
127 else{
128 // this must be a ghost constituent, since it does not have userinfo
129 byVertexConstituents.emplace_back(constituent);
130 }
131 }
132 return extractConstituents(jet, byVertexConstituents);
133}
134
135const std::vector<PseudoJet>* PseudoJetContainer::casVectorPseudoJet() const {
136 if (m_debug){checkInvariants("asVectorPseudoJet()");}
137 return &m_allConstituents;
138}
139
140std::vector<PseudoJet>* PseudoJetContainer::casVectorPseudoJet() {
141 if (m_debug){checkInvariants("asVectorPseudoJet()");}
142 return &m_allConstituents;
143}
144
146 // Adds the extractors and pseudo jets for other to this container.
147
148 // add the other extractor and its range to the ExtractorRanges table
149 int offset = m_extractorRanges.empty() ? 0 : m_extractorRanges.back().m_hi+1;
150
151 std::transform(other->m_extractorRanges.begin(),
152 other->m_extractorRanges.end(),
153 std::back_inserter(m_extractorRanges),
154 [offset](const ExtractorRange& e){return e.bump(offset);});
155
156 // copy over the pseudojets for the extractor being processed,
157 // and set the user index to match th extractorRange limits.
158
159
160 std::transform(other->m_allConstituents.begin(),
161 other->m_allConstituents.end(),
162 std::back_inserter(m_allConstituents),
163 [offset](fastjet::PseudoJet pj){
164 pj.set_user_index(pj.user_index() + offset);return pj;}
165 );
166
167 for(const auto &e : other->m_emptyExtractors){m_emptyExtractors.emplace(e->clone());}
168
169 if (m_debug){checkInvariants("append()");}
170}
171
172std::string PseudoJetContainer::toString(int level, int extLevel) const {
173 std::ostringstream oss{"PseudoJetContainer at: ", std::ios::ate};
174 oss << this
175 << "\ndump levels: "<< level << " " << extLevel << '\n'
176 << " ExtractorRanges ranges: [" << m_extractorRanges.size() <<"] " ;
177 for(const auto& er : m_extractorRanges){
178 oss << "(" << er.m_lo << "," <<er.m_hi << "), ";
179 }
180
181 oss << " empty extractors: " << m_emptyExtractors.size();
182 oss << " Pseudojets: " << m_allConstituents.size();
183
184 if (level > 0){
185 oss << "\n Extractor dump: \n";
186 for(const auto& er : m_extractorRanges){
187 oss << "Extractor at [" ;
188 oss << er.m_e.get();
189 oss << "]\n";
190 oss << er.m_e->toString(extLevel) << '\n';
191 }
192 }
193
194 if(level > 1){ oss << dumpPseudoJets();}
195
196 return oss.str();
197}
198
200 std::ostringstream oss{"PseudoJetContainer at: ", std::ios::ate};
201 oss <<"\n PseudoJet energies\n";
202 for(const auto& p : m_allConstituents){
203 oss << "pseudojet user ind " << p.user_index()
204 << " E " << p.e() << " " << p.eta() << '\n';
205 }
206
207 return oss.str();
208}
209
210
211bool PseudoJetContainer::checkInvariants(const std::string& from) const {
212
213 if(!m_debug){return true;}
214
215 std::ostringstream
216 oss("PseudoJetContainer checkInvariants called from: ",
217 std::ios::ate);
218 oss << from << " ";
219
220 if (m_extractorRanges.empty()){
221 if(! m_allConstituents.empty()){
222 oss << "No extractors, but pseudojets present";
223 return bad_invariants_exit(oss);
224 }
225 return true;
226 }
227
228 // have at least one extractor
229 // m_lo == m_hi possible if an empty EDM container has been read in.
230 for(const auto& ex : m_extractorRanges){
231 if (ex.m_lo < 0 or ex.m_lo > ex.m_hi){
232 oss << "bad extractor limits: " << ex.m_lo << " " << ex.m_hi;
233 return bad_invariants_exit(oss);
234 }
235 }
236
237 // have at least one extractor with non-zero limits
238 auto npj = m_allConstituents.size();
239 auto uExtCount = (m_extractorRanges.back()).m_hi;
240 if (uExtCount < 0){
241 oss << " Illegal extractor range limit " << uExtCount;
242 return bad_invariants_exit(oss);
243 }
244
245 std::size_t uuExtCount = uExtCount;
246
247 //The user index for a pseudojet is an index into the EDM objet
248 // array. The comparison here is between th size of the PseudoJet
249 // array and the index of th EDM objects. When each EDM object
250 // gives rise to a PsedoJet, these numbers differ by 1.
251 // Not all EDM objcts give rise to a PseudoJet, so an inequality
252 // is tested here.
253 if (npj > uuExtCount + 1){
254 oss << " Mismatch between pseudo jet count/upper Extractor limit "
255 << npj << "/" << uuExtCount;
256 return bad_invariants_exit(oss);
257 }
258
259 // have pseudoJets
260
261 auto firstInd = m_allConstituents[0].user_index();
262 if (firstInd != 0){
263 oss << " first pseudojet does not have index 0 " << firstInd;
264 return bad_invariants_exit(oss);
265 }
266
267 auto nent = m_extractorRanges.size();
268
269 for (std::size_t i = 0; i < nent-1; ++i){
270 auto cur = m_extractorRanges[i];
271 auto next = m_extractorRanges[i+1];
272 if (cur.m_hi != next.m_lo - 1){
273 oss << "Hi limit for extractor " << i
274 << " != low limit for extractor " << i+1;
275 return bad_invariants_exit(oss);
276 }
277 }
278
279 if(!checkPseudoJetVector(m_allConstituents, "checkInvariants()")) {
280 return bad_invariants_exit(oss);
281 }
282
283 for(const auto& pj : m_allConstituents){
284 try{
285 pj.e();
286 } catch(...){
287 oss << "PseudoJet.e() fails of PJ in m_allConstituents";
288 return bad_invariants_exit(oss);
289 }
290 }
291
292 for(const ExtractorRange& er : m_extractorRanges){
293 if (!((er.m_e)->checkIntegrity())){
294 oss << "IConstituentExtractor fails integrity test";
295 return bad_invariants_exit(oss);
296 }
297 }
298
299 oss << "No of PseudoJets: "<< m_allConstituents.size()
300 << " No of Extractors " << m_extractorRanges.size() <<" all OK";
301
302 return true;
303}
304
305bool PseudoJetContainer::checkPseudoJetVector(const std::vector<PseudoJet>& v,
306 const std::string& from) const{
307
308 // Pseudojets in th vector v contain the indices into the
309 // the EDM object vector held in the Extractors. If there is more than
310 // one extractor, an extractor dependent offset is added to index.
311 // The indices should be ordered, and start from a value >= 0.
312
313 if(!m_debug){return true;}
314
315 std::ostringstream
316 oss("PseudoJetContainer checkPseudoJetVector called from: ",
317 std::ios::ate);
318 oss << from << " ";
319
320 if(v.empty()){
321 oss << "Empty pseudojet vector " ;
322 return true; // empty not a problem
323 }
324
325 oss <<"PseudoJetVector energy dump:\n";
326 std::vector<float> energies;
327 energies.reserve(v.size());
328 std::transform(v.begin(),
329 v.end(),
330 std::back_inserter(energies),
331 [](const PseudoJet& p){return p.e();});
332
333 LineFormatter formatter(10); // dump the energies, 10 values/line.
334 oss << formatter(energies);
335
336
337 bool ok{true};
338 if (v[0].user_index() < 0){
339 ok = false;
340 oss << "First pj has index < 0: " << v[0].user_index();
341 }
342
343 std::vector<int> indices(v.size());
344 std::transform(v.begin(),
345 v.end(),
346 std::back_inserter(indices),
347 [](const PseudoJet & p){return p.user_index();});
348
349 if(!std::is_sorted(indices.begin(), indices.end())){
350 oss << "Pseudojets out of order ";
351 ok = false;
352 }
353
354 if(ok){oss << v.size() << " checked ok";}
355
356 return ok;
357}
358
359bool
360PseudoJetContainer::checkInConstituents(const std::vector<PseudoJet>& v,
361 const std::string& from) const {
362
363 if(!m_debug){return true;}
364
365 std::ostringstream
366 oss("PseudoJetContainer checkInConstituents called from: ",
367 std::ios::ate);
368 oss << from << " ";
369 oss << v.size() << " entries\n";
370
371 std::vector<double> energies;
372 energies.reserve(v.size());
373 std::transform(v.begin(),
374 v.end(),
375 std::back_inserter(energies),
376 [](const PseudoJet & p){return p.e();});
377
378
379 LineFormatter formatter(10); // dump the energies, 10 values/line.
380 oss << formatter(energies);
381
382 for(const auto& pj: v){
383 if(pj.user_index() == -1){oss << "PJ index -1, E() " << pj.E() << '\n';}
384 }
385
386 std::vector<int> indices;
387 indices.reserve(v.size());
388 std::transform(v.begin(),
389 v.end(),
390 std::back_inserter(indices),
391 [](const PseudoJet & p){return p.user_index();});
392
393 oss <<"PseudoJet indices\n";
394 oss << formatter(indices);
395
396
397 std::map<int, int> counter;
398 if(!m_extractorRanges.empty()) {
399 int lo = m_extractorRanges[0].m_lo;
400 int hi = m_extractorRanges.back().m_hi;
401
402 for (auto ind : indices){
403 // ind = -1 pseudojets can be produced by fastjet.
404 if (ind == -1){continue;}
405 if (ind < lo or ind > hi){++(counter[ind]);}
406 }
407 }
408
409 bool bad = !counter.empty();
410
411 if(bad){
412 oss <<"Out of range values[counts]: ";
413
414 for(auto ent: counter){oss << ent.first << "/" << ent.second << '\n';}
415 oss << "Extractor ranges: \n";
416 for(const auto& er: m_extractorRanges){
417 oss << er.m_lo << " " << er.m_hi << '\n';
418 }
419
421 }
422
423
424 return true;
425}
426
427bool
428PseudoJetContainer::bad_invariants_exit(const std::ostringstream& oss) const {
429
430 auto errMsg = oss.str();
431
432 if (m_debug) {
433 std::cerr << errMsg << '\n';
434 throw std::runtime_error(errMsg);
435 }
436
437 return false;
438}
439
440std::size_t PseudoJetContainer::size() const {
441 return m_allConstituents.size();
442}
443
444bool PseudoJetContainer::debug() const{ return m_debug;}
446std::ostream& operator << (std::ostream& os, const PseudoJetContainer& c){
447 os << c.toString(0);
448 return os;
449}
std::ostream & operator<<(std::ostream &os, const PseudoJetContainer &c)
std::set< std::unique_ptr< const IConstituentExtractor > > m_emptyExtractors
bool extractByVertexConstituents(xAOD::Jet &jet, const PseudoJet &finalPJ, const xAOD::Vertex *vertex) const
std::vector< PseudoJet > m_allConstituents
bool checkInConstituents(const std::vector< PseudoJet > &, const std::string &) const
bool checkInvariants(const std::string &) const
const std::vector< PseudoJet > * casVectorPseudoJet() const
std::vector< ExtractorRange > m_extractorRanges
std::size_t size() const
void append(const PseudoJetContainer *)
fastjet::PseudoJet PseudoJet
bool bad_invariants_exit(const std::ostringstream &) const
std::string dumpPseudoJets() const
std::string toString(int level, int extLevel=0) const
bool checkPseudoJetVector(const std::vector< PseudoJet > &, const std::string &) const
bool extractConstituents(xAOD::Jet &, const std::vector< PseudoJet > &) const
const xAOD::Vertex * vertex() const
Returns the associated vertex if this constit is a track. Else returns null. *‍/.
int r
Definition globals.cxx:22
Jet_v1 Jet
Definition of the current "jet version".
Vertex_v1 Vertex
Define the latest version of the vertex class.