CMS 3D CMS Logo

HitParentTest.cc
Go to the documentation of this file.
3 
8 
12 
16 
24 
25 #include <TH1F.h>
26 
27 #include <boost/format.hpp>
28 #include <algorithm>
29 #include <memory>
30 #include <iostream>
31 #include <fstream>
32 #include <vector>
33 #include <map>
34 #include <string>
35 
36 class HitParentTest : public edm::one::EDAnalyzer<edm::one::WatchRuns, edm::one::SharedResources> {
37 public:
39  ~HitParentTest() override {}
40  static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
41 
42 protected:
43  void beginJob() override;
44  void beginRun(edm::Run const&, edm::EventSetup const&) override {}
45  void analyze(const edm::Event& e, const edm::EventSetup& c) override;
46  void endRun(edm::Run const&, edm::EventSetup const&) override {}
47  void endJob() override;
48 
49 private:
51  void analyzeHits(const std::vector<PCaloHit>&, int type);
52  void analyzeAPDHits(const std::vector<PCaloHit>&, int depth);
53 
54  bool simTrackPresent(int id);
55  math::XYZTLorentzVectorD getOldestParentVertex(edm::SimTrackContainer::const_iterator track);
56  edm::SimTrackContainer::const_iterator parentSimTrack(edm::SimTrackContainer::const_iterator thisTrkItr);
57  bool validSimTrack(unsigned int simTkId, edm::SimTrackContainer::const_iterator thisTrkItr);
58 
59 private:
67 
69  unsigned int total_num_apd_hits_seen[2];
70  unsigned int num_apd_hits_no_parent[2];
71 
74  unsigned int num_apd_hits_no_simtrack[2];
75 
78 
81 
84  std::map<int, unsigned> particle_type_count;
85 
87  bool histos;
88  unsigned int totalHits[7], noParent[7];
89  unsigned int noSimTrack[7], noGenParticle[7];
90  TH1F *hitType[7], *hitRho[7], *hitZ[7];
91 };
92 
94  usesResource(TFileService::kSharedResource);
95 
96  sourceLabel = ps.getUntrackedParameter<std::string>("SourceLabel", "VtxSmeared");
97  g4Label = ps.getUntrackedParameter<std::string>("ModuleLabel", "g4SimHits");
98  hitLabEB = ps.getUntrackedParameter<std::string>("EBCollection", "EcalHitsEB");
99  hitLabEE = ps.getUntrackedParameter<std::string>("EECollection", "EcalHitsEE");
100  hitLabES = ps.getUntrackedParameter<std::string>("ESCollection", "EcalHitsES");
101  hitLabHC = ps.getUntrackedParameter<std::string>("HCCollection", "HcalHits");
102  edm::LogVerbatim("HitParentTest") << "Module Label: " << g4Label << " Hits: " << hitLabEB << ", " << hitLabEE
103  << ", " << hitLabES << ", " << hitLabHC;
104 
105  tok_eb_ = consumes<edm::PCaloHitContainer>(edm::InputTag(g4Label, hitLabEB));
106  tok_ee_ = consumes<edm::PCaloHitContainer>(edm::InputTag(g4Label, hitLabEE));
107  tok_es_ = consumes<edm::PCaloHitContainer>(edm::InputTag(g4Label, hitLabES));
108  tok_hc_ = consumes<edm::PCaloHitContainer>(edm::InputTag(g4Label, hitLabHC));
109  tok_tk_ = consumes<edm::SimTrackContainer>(edm::InputTag(g4Label));
110  tok_vtx_ = consumes<edm::SimVertexContainer>(edm::InputTag(g4Label));
111 
112  for (unsigned int i = 0; i < 2; ++i) {
117  }
118  std::string dets[7] = {"EB", "EE", "ES", "HB", "HE", "HO", "HF"};
119  for (unsigned int i = 0; i < 7; ++i) {
120  detector[i] = dets[i];
121  totalHits[i] = 0;
122  noParent[i] = 0;
123  noSimTrack[i] = 0;
124  noGenParticle[i] = 0;
125  }
126 }
127 
130  desc.addUntracked<std::string>("SourceLabel", "generatorSmeared");
131  desc.addUntracked<std::string>("ModuleLabel", "g4SimHits");
132  desc.addUntracked<std::string>("EBCollection", "EcalHitsEB");
133  desc.addUntracked<std::string>("EECollection", "EcalHitsEE");
134  desc.addUntracked<std::string>("ESCollection", "EcalHitsES");
135  desc.addUntracked<std::string>("HCCollection", "HcalHits");
136  descriptions.add("HitParentTest", desc);
137 }
138 
141  if (!tfile.isAvailable()) {
142  edm::LogVerbatim("HitParentTest") << "TFileService unavailable: no histograms";
143  histos = false;
144  } else {
145  char name[20], title[100];
146  histos = true;
147  for (unsigned int i = 0; i < 7; ++i) {
148  sprintf(name, "HitType%d", i);
149  sprintf(title, "Hit types for %s", detector[i].c_str());
150  hitType[i] = tfile->make<TH1F>(name, title, 10, 0., 10.);
151  hitType[i]->GetXaxis()->SetTitle(title);
152  hitType[i]->GetYaxis()->SetTitle("Hits");
153  sprintf(name, "RhoVertex%d", i);
154  sprintf(title, "#rho of the vertex for %s Hits", detector[i].c_str());
155  hitRho[i] = tfile->make<TH1F>(name, title, 10000, 0., 100.);
156  hitRho[i]->GetXaxis()->SetTitle(title);
157  hitRho[i]->GetYaxis()->SetTitle("Hits");
158  sprintf(name, "ZVertex%d", i);
159  sprintf(title, "z of the vertex for %s Hits", detector[i].c_str());
160  hitZ[i] = tfile->make<TH1F>(name, title, 2000, -100., 100.);
161  hitZ[i]->GetXaxis()->SetTitle(title);
162  hitZ[i]->GetYaxis()->SetTitle("Hits");
163  }
164  }
165 }
166 
168  edm::LogVerbatim("HitParentTest") << "HitParentTes::Run = " << e.id().run() << " Event = " << e.id().event();
169 
170  // get PCaloHits for ecal barrel
172  e.getByToken(tok_eb_, caloHitEB);
173 
174  // get PCaloHits for ecal endcap
176  e.getByToken(tok_ee_, caloHitEE);
177 
178  // get PCaloHits for preshower
180  e.getByToken(tok_es_, caloHitES);
181 
182  // get PCaloHits for hcal
184  e.getByToken(tok_hc_, caloHitHC);
185 
186  // get sim tracks
188 
189  // get sim vertices
191 
192  edm::LogVerbatim("HitParentTest") << "HitParentTest: hits valid[EB]: " << caloHitEB.isValid()
193  << " valid[EE]: " << caloHitEE.isValid() << " valid[ES]: " << caloHitES.isValid()
194  << " valid[HC]: " << caloHitHC.isValid();
195 
196  if (caloHitEB.isValid()) {
197  for (int depth = 1; depth <= 2; ++depth)
198  analyzeAPDHits(*caloHitEB, depth);
199  analyzeHits(*caloHitEB, 0);
200  }
201  if (caloHitEE.isValid())
202  analyzeHits(*caloHitEE, 1);
203  if (caloHitES.isValid())
204  analyzeHits(*caloHitES, 2);
205  if (caloHitHC.isValid())
206  analyzeHits(*caloHitHC, 3);
207 }
208 
211 protected:
212  const std::map<int, unsigned>& particle_type_count;
213 
214 public:
215  HitParentTestComparison(const std::map<int, unsigned>& _particle_type_count)
216  : particle_type_count(_particle_type_count) {}
217 
218  bool operator()(int pid1, int pid2) const { return particle_type_count.at(pid1) > particle_type_count.at(pid2); }
219 };
220 
222  edm::LogVerbatim("HitParentTest") << "HitParentTest::Total number of APD hits seen: "
224  for (unsigned int depth = 0; depth < 2; ++depth) {
225  edm::LogVerbatim("HitParentTest") << "APD Hits in depth = " << depth + 1
226  << " Total = " << total_num_apd_hits_seen[depth];
227  edm::LogVerbatim("HitParentTest") << "summary of errors:\n"
228  << " number of APD hits with zero geant track id: "
229  << num_apd_hits_no_parent[depth] << "\n"
230  << "number of APD hits for which the parent simtrack was not found "
231  << "in the simtrack collection: " << num_apd_hits_no_simtrack[depth] << "\n"
232  << "number of APD hits for which no generator particle was "
233  << "found: " << num_apd_hits_no_gen_particle[depth] << "\n";
234  }
235 
236  for (unsigned int det = 0; det < 7; ++det) {
237  edm::LogVerbatim("HitParentTest") << "Total number of hits seen in " << detector[det] << ": " << totalHits[det];
238  edm::LogVerbatim("HitParentTest") << "summary of errors:\n"
239  << "number of hits with zero geant track id: " << noParent[det] << "\n"
240  << "number of hits for which the parent simtrack was not found in "
241  << "the simtrack collection: " << noSimTrack[det] << "\n"
242  << "number of hits for which no generator particle was found: "
243  << noGenParticle[det] << "\n";
244  }
245 
246  // sort in decreasing order of occurence
247  std::vector<int> sorted_pids;
248  for (std::map<int, unsigned>::const_iterator it = particle_type_count.begin(); it != particle_type_count.end(); ++it)
249  sorted_pids.push_back(it->first);
250 
251  // now sort the pids
252 
253  std::sort(sorted_pids.begin(), sorted_pids.end(), HitParentTestComparison(particle_type_count));
254 
255  edm::LogVerbatim("HitParentTest") << "HitParentTest::Frequency particle types through the APD "
256  << "(pid/frequency):";
257  for (unsigned i = 0; i < sorted_pids.size(); ++i) {
258  int pid = sorted_pids[i];
259  edm::LogVerbatim("HitParentTest") << " pid " << boost::format("%6d") % pid << ": count "
260  << boost::format("%6d") % particle_type_count[pid];
261  }
262 }
263 
264 void HitParentTest::analyzeHits(const std::vector<PCaloHit>& hits, int type) {
265  for (std::vector<PCaloHit>::const_iterator hit_it = hits.begin(); hit_it != hits.end(); ++hit_it) {
266  int id(type), flag(0);
267  if (type == 3) {
268  HcalDetId id_ = HcalDetId(hit_it->id());
269  int subdet = id_.subdet();
270  if (subdet == static_cast<int>(HcalEndcap))
271  id = type + 1;
272  else if (subdet == static_cast<int>(HcalOuter))
273  id = type + 2;
274  else if (subdet == static_cast<int>(HcalForward))
275  id = type + 3;
276  }
277  ++totalHits[id];
278 
279  // get the geant track id
280  int hit_geant_track_id = hit_it->geantTrackId();
281 
282  if (hit_geant_track_id <= 0) {
283  ++noParent[id];
284  flag = 1;
285  } else {
286  bool found = false;
287  flag = 2;
288  // check whether this id is actually there in the list of simtracks
289  for (edm::SimTrackContainer::const_iterator simTrkItr = SimTk->begin(); simTrkItr != SimTk->end() && !found;
290  ++simTrkItr) {
291  if (hit_geant_track_id == (int)(simTrkItr->trackId())) {
292  found = true;
293  flag = 3;
294  bool match = validSimTrack(hit_geant_track_id, simTrkItr);
295 
296  edm::LogVerbatim("HitParentTest")
297  << "[" << detector[type] << "] Match = " << match << " hit_geant_track_id=" << hit_geant_track_id
298  << " particle id=" << simTrkItr->type();
299 
300  if (!match) {
301  edm::LogVerbatim("HitParentTest") << "NO MATCH FOUND !";
302  ++noGenParticle[id];
303  }
304 
305  // beam pipe...
306  int pid = simTrkItr->type();
307  math::XYZTLorentzVectorD oldest_parent_vertex = getOldestParentVertex(simTrkItr);
308 
309  edm::SimTrackContainer::const_iterator oldest_parent_track = parentSimTrack(simTrkItr);
310 
311  edm::LogVerbatim("HitParentTest")
312  << "[" << detector[type] << "] Hit pid = " << pid << " hit track id = " << hit_geant_track_id
313  << " Oldest Parent's Vertex: " << oldest_parent_vertex << " rho = " << oldest_parent_vertex.Rho()
314  << " Oldest Parent's pid: " << oldest_parent_track->type()
315  << " Oldest Parent's track id: " << oldest_parent_track->trackId()
316  << "\nHit vertex index: " << simTrkItr->vertIndex() << " (tot #vertices: " << SimVtx->size() << ")"
317  << "\nHit vertex parent track: " << (*SimVtx)[simTrkItr->vertIndex()].parentIndex()
318  << " present=" << simTrackPresent((*SimVtx)[simTrkItr->vertIndex()].parentIndex());
319  if (histos) {
320  hitRho[id]->Fill(oldest_parent_vertex.Rho());
321  hitZ[id]->Fill(oldest_parent_vertex.Z());
322  }
323  } // a match was found
324  } // geant track id found in simtracks
325 
326  if (!found)
327  ++noSimTrack[id];
328  } // hits with a valid SimTrack Id
329  if (histos)
330  hitType[id]->Fill((double)(flag));
331  } // loop over all calohits (of the given depth)
332 }
333 
334 void HitParentTest::analyzeAPDHits(const std::vector<PCaloHit>& hits, int depth) {
335  for (std::vector<PCaloHit>::const_iterator hit_it = hits.begin(); hit_it != hits.end(); ++hit_it) {
336  if (hit_it->depth() == depth) {
337  ++total_num_apd_hits_seen[depth - 1];
338 
339  // get the geant track id
340  int hit_geant_track_id = hit_it->geantTrackId();
341 
342  if (hit_geant_track_id <= 0) {
343  ++num_apd_hits_no_parent[depth - 1];
344  } else {
345  bool found = false;
346  // check whether this id is actually there in the list of simtracks
347  for (edm::SimTrackContainer::const_iterator simTrkItr = SimTk->begin(); simTrkItr != SimTk->end() && !found;
348  ++simTrkItr) {
349  if (hit_geant_track_id == (int)(simTrkItr->trackId())) {
350  found = true;
351  bool match = validSimTrack(hit_geant_track_id, simTrkItr);
352 
353  edm::LogVerbatim("HitParentTest")
354  << "APDHIT Match = " << match << " hit_geant_track_id = " << hit_geant_track_id
355  << " particle id=" << simTrkItr->type();
356 
357  if (!match) {
358  edm::LogVerbatim("HitParentTest") << "NO MATCH FOUND !";
359  ++num_apd_hits_no_gen_particle[depth - 1];
360  }
361 
362  int apd_pid = simTrkItr->type();
363  std::map<int, unsigned>::iterator count_it = particle_type_count.find(apd_pid);
364  if (count_it == particle_type_count.end())
365  // first occurence of this particle pid
366  particle_type_count[apd_pid] = 1;
367  else
368  ++count_it->second;
369 
370  //--------------------
371  // check where the oldest parent has its vertex. Should be close to the
372  // beam pipe...
373  math::XYZTLorentzVectorD oldest_parent_vertex = getOldestParentVertex(simTrkItr);
374 
375  edm::SimTrackContainer::const_iterator oldest_parent_track = parentSimTrack(simTrkItr);
376 
377  edm::LogVerbatim("HitParentTest")
378  << "APD hit pid = " << apd_pid << " APD hit track id = " << hit_geant_track_id
379  << " depth = " << hit_it->depth() << " OLDEST PARENT'S VERTEX: " << oldest_parent_vertex
380  << " rho = " << oldest_parent_vertex.Rho() << " OLDEST PARENT'S PID: " << oldest_parent_track->type()
381  << " OLDEST PARENT'S track id: " << oldest_parent_track->trackId() << "\n"
382  << "APD hit vertex index: " << simTrkItr->vertIndex() << " (tot #vertices: " << SimVtx->size() << ")"
383  << "\n"
384  << "APD hit vertex parent track: " << (*SimVtx)[simTrkItr->vertIndex()].parentIndex()
385  << " present=" << simTrackPresent((*SimVtx)[simTrkItr->vertIndex()].parentIndex());
386 
387  } // a match was found
388  } // geant track id found in simtracks
389 
390  if (!found)
391  ++num_apd_hits_no_simtrack[depth - 1];
392  } // hits with a valid SimTrack Id
393  } // right depth index
394  } // loop over all calohits (of the given depth)
395 }
396 
398  for (edm::SimTrackContainer::const_iterator simTrkItr = SimTk->begin(); simTrkItr != SimTk->end(); ++simTrkItr) {
399  if ((int)(simTrkItr->trackId()) == id)
400  return true;
401  }
402  return false;
403 }
404 
405 math::XYZTLorentzVectorD HitParentTest::getOldestParentVertex(edm::SimTrackContainer::const_iterator track) {
406  static const math::XYZTLorentzVectorD invalid_vertex(
407  10000, 10000, 10000, 10000); // default value if no valid vertex found
408 
409  edm::SimTrackContainer::const_iterator oldest_parent_track = parentSimTrack(track);
410 
411  int vertex_index = oldest_parent_track->vertIndex();
412 
413  // sanity checks
414  if (vertex_index < 0 || vertex_index >= (int)(SimVtx->size()))
415  return invalid_vertex;
416 
417  return (*SimVtx)[vertex_index].position();
418 }
419 
420 edm::SimTrackContainer::const_iterator HitParentTest::parentSimTrack(edm::SimTrackContainer::const_iterator thisTrkItr) {
421  edm::SimTrackContainer::const_iterator itr = SimTk->end();
422 
423  int vertIndex = thisTrkItr->vertIndex();
424  int type = thisTrkItr->type();
425  int charge = (int)thisTrkItr->charge();
426  edm::LogVerbatim("HitParentTest") << "SimTrackParent:: " << thisTrkItr->trackId() << " Vertex " << vertIndex
427  << " Type " << type << " Charge " << charge;
428 
429  if (vertIndex == -1)
430  return thisTrkItr;
431  else if (vertIndex >= (int)SimVtx->size())
432  return itr;
433 
434  edm::SimVertexContainer::const_iterator simVtxItr = SimVtx->begin();
435  for (int iv = 0; iv < vertIndex; iv++)
436  simVtxItr++;
437  int parent = simVtxItr->parentIndex();
438 
439  if (parent < 0 && simVtxItr != SimVtx->begin()) {
440  const math::XYZTLorentzVectorD pos1 = simVtxItr->position();
441  for (simVtxItr = SimVtx->begin(); simVtxItr != SimVtx->end(); ++simVtxItr) {
442  if (simVtxItr->parentIndex() > 0) {
443  const math::XYZTLorentzVectorD pos2 = pos1 - simVtxItr->position();
444  double dist = pos2.P();
445  if (dist < 0.001) {
446  parent = simVtxItr->parentIndex();
447  break;
448  }
449  }
450  }
451  }
452  for (edm::SimTrackContainer::const_iterator simTrkItr = SimTk->begin(); simTrkItr != SimTk->end(); simTrkItr++) {
453  if ((int)simTrkItr->trackId() == parent && simTrkItr != thisTrkItr)
454  return parentSimTrack(simTrkItr);
455  }
456 
457  return thisTrkItr;
458 }
459 
460 bool HitParentTest::validSimTrack(unsigned int simTkId, edm::SimTrackContainer::const_iterator thisTrkItr) {
461  edm::LogVerbatim("HitParentTest") << "Inside validSimTrack: trackId " << thisTrkItr->trackId() << " vtxIndex "
462  << thisTrkItr->vertIndex() << " to be matched to " << simTkId;
463 
464  //This track originates from simTkId
465  if (thisTrkItr->trackId() == simTkId)
466  return true;
467 
468  //Otherwise trace back the history using SimTracks and SimVertices
469  int vertIndex = thisTrkItr->vertIndex();
470  if (vertIndex == -1 || vertIndex >= (int)SimVtx->size())
471  return false;
472  edm::SimVertexContainer::const_iterator simVtxItr = SimVtx->begin();
473  for (int iv = 0; iv < vertIndex; iv++)
474  simVtxItr++;
475  int parent = simVtxItr->parentIndex();
476  edm::LogVerbatim("HitParentTest") << "validSimTrack:: parent index " << parent << " ";
477  if (parent < 0 && simVtxItr != SimVtx->begin()) {
478  const math::XYZTLorentzVectorD pos1 = simVtxItr->position();
479  for (simVtxItr = SimVtx->begin(); simVtxItr != SimVtx->end(); ++simVtxItr) {
480  if (simVtxItr->parentIndex() > 0) {
481  const math::XYZTLorentzVectorD pos2 = pos1 - simVtxItr->position();
482  double dist = pos2.P();
483  if (dist < 0.001) {
484  parent = simVtxItr->parentIndex();
485  break;
486  }
487  }
488  }
489  }
490  edm::LogVerbatim("HitParentTest") << "HitParentTes::final index " << parent;
491  for (edm::SimTrackContainer::const_iterator simTrkItr = SimTk->begin(); simTrkItr != SimTk->end(); simTrkItr++) {
492  if ((int)simTrkItr->trackId() == parent && simTrkItr != thisTrkItr)
493  return validSimTrack(simTkId, simTrkItr);
494  }
495 
496  return false;
497 }
498 
499 //define this as a plug-in
std::string hitLabEB
RunNumber_t run() const
Definition: EventID.h:38
static const std::string kSharedResource
Definition: TFileService.h:76
type
Definition: HCALResponse.h:21
EventNumber_t event() const
Definition: EventID.h:40
edm::EDGetTokenT< edm::SimTrackContainer > tok_tk_
T getUntrackedParameter(std::string const &, T const &) const
void beginJob() override
std::string hitLabEE
TH1F * hitRho[7]
ROOT::Math::LorentzVector< ROOT::Math::PxPyPzE4D< double > > XYZTLorentzVectorD
Lorentz vector with cylindrical internal representation using pseudorapidity.
Definition: LorentzVector.h:14
HcalSubdetector subdet() const
get the subdetector
Definition: HcalDetId.h:138
ParameterDescriptionBase * addUntracked(U const &iLabel, T const &value)
edm::EDGetTokenT< edm::SimVertexContainer > tok_vtx_
bool getByToken(EDGetToken token, Handle< PROD > &result) const
Definition: Event.h:525
unsigned int noGenParticle[7]
bool operator()(int pid1, int pid2) const
unsigned int num_apd_hits_no_simtrack[2]
const std::map< int, unsigned > & particle_type_count
edm::SimTrackContainer::const_iterator parentSimTrack(edm::SimTrackContainer::const_iterator thisTrkItr)
unsigned int noSimTrack[7]
T * make(const Args &...args) const
make new ROOT object
Definition: TFileService.h:64
std::string detector[7]
unsigned int noParent[7]
edm::Handle< edm::SimVertexContainer > SimVtx
unsigned int num_apd_hits_no_gen_particle[2]
void analyze(const edm::Event &e, const edm::EventSetup &c) override
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
edm::EDGetTokenT< edm::PCaloHitContainer > tok_ee_
edm::Handle< edm::SimTrackContainer > SimTk
TH1F * hitZ[7]
void endRun(edm::Run const &, edm::EventSetup const &) override
std::string g4Label
bool isAvailable() const
Definition: Service.h:40
std::map< int, unsigned > particle_type_count
HitParentTest(const edm::ParameterSet &ps)
edm::EDGetTokenT< edm::PCaloHitContainer > tok_eb_
bool isValid() const
Definition: HandleBase.h:70
bool validSimTrack(unsigned int simTkId, edm::SimTrackContainer::const_iterator thisTrkItr)
unsigned int num_apd_hits_no_parent[2]
std::string sourceLabel
math::XYZTLorentzVectorD getOldestParentVertex(edm::SimTrackContainer::const_iterator track)
HitParentTestComparison(const std::map< int, unsigned > &_particle_type_count)
edm::EDGetTokenT< edm::PCaloHitContainer > tok_hc_
void beginRun(edm::Run const &, edm::EventSetup const &) override
void analyzeHits(const std::vector< PCaloHit > &, int type)
bool simTrackPresent(int id)
void add(std::string const &label, ParameterSetDescription const &psetDescription)
unsigned int total_num_apd_hits_seen[2]
edm::EventID id() const
Definition: EventBase.h:59
#define begin
Definition: vmac.h:32
TH1F * hitType[7]
std::string hitLabES
void analyzeAPDHits(const std::vector< PCaloHit > &, int depth)
unsigned int totalHits[7]
std::pair< typename Association::data_type::first_type, double > match(Reference key, Association association, bool bestMatchByMaxValue)
Generic matching function.
Definition: Utils.h:10
edm::EDGetTokenT< edm::PCaloHitContainer > tok_es_
void endJob() override
~HitParentTest() override
std::string hitLabHC
Definition: Run.h:45
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)