CMS 3D CMS Logo

 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
SETPatternRecognition.cc
Go to the documentation of this file.
1 
12 #include "TMath.h"
13 
14 
15 using namespace edm;
16 using namespace std;
17 
19 : MuonSeedVPatternRecognition(parameterSet.getParameter<ParameterSet>("SETTrajBuilderParameters").getParameter<ParameterSet>("FilterParameters"))
20 {
21  const string metname = "Muon|RecoMuon|SETPatternRecognition";
22  // Parameter set for the Builder
23  ParameterSet trajectoryBuilderParameters = parameterSet.getParameter<ParameterSet>("SETTrajBuilderParameters");
24  // The inward-outward fitter (starts from seed state)
25  ParameterSet filterPSet = trajectoryBuilderParameters.getParameter<ParameterSet>("FilterParameters");
26  maxActiveChambers = filterPSet.getParameter<int>("maxActiveChambers");
27  useRPCs = filterPSet.getParameter<bool>("EnableRPCMeasurement");
28  DTRecSegmentLabel = filterPSet.getParameter<edm::InputTag>("DTRecSegmentLabel");
29  CSCRecSegmentLabel = filterPSet.getParameter<edm::InputTag>("CSCRecSegmentLabel");
30  RPCRecSegmentLabel = filterPSet.getParameter<edm::InputTag>("RPCRecSegmentLabel");
31 
32  outsideChamberErrorScale = filterPSet.getParameter<double>("OutsideChamberErrorScale");
33  minLocalSegmentAngle = filterPSet.getParameter<double>("MinLocalSegmentAngle");
34  //----
38 
39 }
40 
41 //---- it is a "cluster recognition" actually; the pattern recognition is in SETFilter
43  std::vector<MuonRecHitContainer> & segments_clusters)
44 {
45  const string metname = "Muon|RecoMuon|SETMuonSeedSeed";
46 
47  //---- Build collection of all segments
48  MuonRecHitContainer muonRecHits;
49  MuonRecHitContainer muonRecHits_DT2D_hasPhi;
50  MuonRecHitContainer muonRecHits_DT2D_hasZed;
51  MuonRecHitContainer muonRecHits_RPC;
52 
53  // ********************************************;
54  // Get the DT-Segment collection from the Event
55  // ********************************************;
56 
58  event.getByToken(dtToken, dtRecHits);
59  std::vector<DTChamberId> chambers_DT;
60  std::vector<DTChamberId>::const_iterator chIt_DT;
61  for (DTRecSegment4DCollection::const_iterator rechit = dtRecHits->begin(); rechit!=dtRecHits->end();++rechit) {
62  bool insert = true;
63  for(chIt_DT=chambers_DT.begin(); chIt_DT != chambers_DT.end(); ++chIt_DT){
64  if (
65  ((*rechit).chamberId().wheel()) == ((*chIt_DT).wheel()) &&
66  ((*rechit).chamberId().station() == (*chIt_DT).station()) &&
67  ((*rechit).chamberId().sector() == (*chIt_DT).sector())){
68  insert = false;
69  }
70  }
71  if (insert){
72  chambers_DT.push_back((*rechit).chamberId());
73  }
74  if(segmentCleaning((*rechit).geographicalId(),
75  rechit->localPosition(), rechit->localPositionError(),
76  rechit->localDirection(), rechit->localDirectionError(),
77  rechit->chi2(), rechit->degreesOfFreedom())){
78  continue;
79  }
80  if( (rechit->hasZed() && rechit->hasPhi()) ) {
81  muonRecHits.push_back(MuonTransientTrackingRecHit::specificBuild(theService->trackingGeometry()->idToDet((*rechit).geographicalId()),&*rechit));
82  }
83  else if(rechit->hasZed()) {
84  muonRecHits_DT2D_hasZed.push_back(MuonTransientTrackingRecHit::specificBuild(theService->trackingGeometry()->idToDet((*rechit).geographicalId()),&*rechit));
85  }
86  else if(rechit->hasPhi()) { // safeguard
87  muonRecHits_DT2D_hasPhi.push_back(MuonTransientTrackingRecHit::specificBuild(theService->trackingGeometry()->idToDet((*rechit).geographicalId()),&*rechit));
88  }
89  else {
90  //std::cout<<"Warning in "<<metname<<": DT segment which claims to have neither phi nor Z."<<std::endl;
91  }
92  }
93  //std::cout<<"DT done"<<std::endl;
94 
95  // ********************************************;
96  // Get the CSC-Segment collection from the event
97  // ********************************************;
98 
100  event.getByToken(cscToken, cscSegments);
101  std::vector<CSCDetId> chambers_CSC;
102  std::vector<CSCDetId>::const_iterator chIt_CSC;
103  for(CSCSegmentCollection::const_iterator rechit=cscSegments->begin(); rechit != cscSegments->end(); ++rechit) {
104  bool insert = true;
105  for(chIt_CSC=chambers_CSC.begin(); chIt_CSC != chambers_CSC.end(); ++chIt_CSC){
106  if (((*rechit).cscDetId().chamber() == (*chIt_CSC).chamber()) &&
107  ((*rechit).cscDetId().station() == (*chIt_CSC).station()) &&
108  ((*rechit).cscDetId().ring() == (*chIt_CSC).ring()) &&
109  ((*rechit).cscDetId().endcap() == (*chIt_CSC).endcap())){
110  insert = false;
111  }
112  }
113  if (insert){
114  chambers_CSC.push_back((*rechit).cscDetId().chamberId());
115  }
116  if(segmentCleaning((*rechit).geographicalId(),
117  rechit->localPosition(), rechit->localPositionError(),
118  rechit->localDirection(), rechit->localDirectionError(),
119  rechit->chi2(), rechit->degreesOfFreedom())){
120  continue;
121  }
122  muonRecHits.push_back(MuonTransientTrackingRecHit::specificBuild(theService->trackingGeometry()->idToDet((*rechit).geographicalId()),&*rechit));
123  }
124  //std::cout<<"CSC done"<<std::endl;
125 
126  // ********************************************;
127  // Get the RPC-Hit collection from the event
128  // ********************************************;
129 
131  event.getByToken(rpcToken, rpcRecHits);
132  if(useRPCs){
133  for(RPCRecHitCollection::const_iterator rechit=rpcRecHits->begin(); rechit != rpcRecHits->end(); ++rechit) {
134  // RPCs are special
135  const LocalVector localDirection(0.,0.,1.);
136  const LocalError localDirectionError (0.,0.,0.);
137  const double chi2 = 1.;
138  const int ndf = 1;
139  if(segmentCleaning((*rechit).geographicalId(),
140  rechit->localPosition(), rechit->localPositionError(),
141  localDirection, localDirectionError,
142  chi2, ndf)){
143  continue;
144  }
145  muonRecHits_RPC.push_back(MuonTransientTrackingRecHit::specificBuild(theService->trackingGeometry()->idToDet((*rechit).geographicalId()),&*rechit));
146  }
147  }
148  //std::cout<<"RPC done"<<std::endl;
149  //
150  if(int(chambers_DT.size() + chambers_CSC.size()) > maxActiveChambers){
151  // std::cout <<" Too many active chambers : nDT = "<<chambers_DT.size()<<
152  // " nCSC = "<<chambers_CSC.size()<<" Skip them all."<<std::endl;
153  edm::LogWarning("tooManyActiveChambers")<<" Too many active chambers : nDT = "<<chambers_DT.size()
154  <<" nCSC = "<<chambers_CSC.size()<<" Skip them all.";
155  muonRecHits.clear();
156  muonRecHits_DT2D_hasPhi.clear();
157  muonRecHits_DT2D_hasZed.clear();
158  muonRecHits_RPC.clear();
159  }
160  //---- Find "pre-clusters" from all segments; these contain potential muon candidates
161 
162  //---- From all the hits (i.e. segments; sometimes "rechits" is also used with the same meaning;
163  //---- this convention has meaning in the global reconstruction though could be misleading
164  //---- from a local reconstruction point of view; "local rechits" are used in the backward fit only)
165  //---- make clusters of hits; a trajectory could contain hits from one cluster only
166 
167  // the clustering procedure is very similar to the one used in the segment reconstruction
168 
169  bool useDT2D_hasPhi = true;
170  bool useDT2D_hasZed = true;
171  double dXclusBoxMax = 0.60; // phi - can be as large as 15 - 20 degrees for 6 GeV muons
172  double dYclusBoxMax = 0.;
173 
174  // this is the main selection criteria; the value of 0.02 rad seems wide enough to
175  // contain any hit from a passing muon and still narrow enough to remove good part of
176  // possible "junk" hits
177  // (Comment: it may be better to allow maximum difference between any two hits in a trajectory
178  // to be 0.02 or 0.04 or ...; currently the requirement below is imposed on two consecutive hits)
179 
180  dYclusBoxMax = 0.02; // theta // hardoded - remove it!
181 
182  // X and Y are distance variables - we use eta and phi here
183 
184  float dXclus = 0.0;
185  float dXclus_box = 0.0;
186  float dYclus_box = 0.0;
187 
189 
190  std::vector< MuonRecHitContainer > seeds;
191 
192  std::vector<float> running_meanX;
193  std::vector<float> running_meanY;
194 
195  std::vector<float> seed_minX;
196  std::vector<float> seed_maxX;
197  std::vector<float> seed_minY;
198  std::vector<float> seed_maxY;
199 
200  // split rechits into subvectors and return vector of vectors:
201  // Loop over rechits
202  // Create one seed per hit
203  for (MuonRecHitContainer::const_iterator it = muonRecHits.begin(); it != muonRecHits.end(); ++it ) {
204 
205  // try to avoid using 2D DT segments. We will add them later to the
206  // clusters they are most likely to belong to. Might need to add them
207  // to more than just one cluster, if we find them to be consistent with
208  // more than one. This would lead to an implicit sharing of hits between
209  // SA muon candidates.
210 
211  temp.clear();
212 
213  temp.push_back((*it));
214 
215  seeds.push_back(temp);
216 
217  // First added hit in seed defines the mean to which the next hit is compared
218  // for this seed.
219 
220  running_meanX.push_back( (*it)->globalPosition().phi() );
221  running_meanY.push_back( (*it)->globalPosition().theta() );
222 
223  // set min/max X and Y for box containing the hits in the precluster:
224  seed_minX.push_back( (*it)->globalPosition().phi() );
225  seed_maxX.push_back( (*it)->globalPosition().phi() );
226  seed_minY.push_back( (*it)->globalPosition().theta() );
227  seed_maxY.push_back( (*it)->globalPosition().theta() );
228  }
229 
230  // merge clusters that are too close
231  // measure distance between final "running mean"
232  for(unsigned int NNN = 0; NNN < seeds.size(); ++NNN) {
233 
234  for(unsigned int MMM = NNN+1; MMM < seeds.size(); ++MMM) {
235  if(running_meanX[MMM] == 999999. || running_meanX[NNN] == 999999. ) {
236  // LogDebug("CSC") << "CSCSegmentST::clusterHits: Warning: Skipping used seeds, this should happen - inform developers!\n";
237  //std::cout<<"We should never see this line now!!!"<<std::endl;
238  continue; //skip seeds that have been used
239  }
240 
241  // Some complications for using phi as a clustering variable due to wrap-around (-pi = pi)
242  // Define temporary mean, min, and max variables for the cluster which could be merged (NNN)
243  double temp_meanX = running_meanX[NNN];
244  double temp_minX = seed_minX[NNN];
245  double temp_maxX = seed_maxX[NNN];
246 
247  // check if the difference between the two phi values is greater than pi
248  // if so, need to shift temporary values by 2*pi to make a valid comparison
249  dXclus = running_meanX[NNN] - running_meanX[MMM];
250  if (dXclus > TMath::Pi()) {
251  temp_meanX = temp_meanX - 2.*TMath::Pi();
252  temp_minX = temp_minX - 2.*TMath::Pi();
253  temp_maxX = temp_maxX - 2.*TMath::Pi();
254  }
255  if (dXclus < -TMath::Pi()) {
256  temp_meanX = temp_meanX + 2.*TMath::Pi();
257  temp_minX = temp_minX + 2.*TMath::Pi();
258  temp_maxX = temp_maxX + 2.*TMath::Pi();
259  }
260 
261  // // calculate cut criteria for simple running mean distance cut:
262  // // not sure that these values are really used anywhere
263 
264  // calculate minmal distance between precluster boxes containing the hits:
265  // use the temp variables from above for phi of the NNN cluster
266  if ( temp_meanX > running_meanX[MMM] ) dXclus_box = temp_minX - seed_maxX[MMM];
267  else dXclus_box = seed_minX[MMM] - temp_maxX;
268  if ( running_meanY[NNN] > running_meanY[MMM] ) dYclus_box = seed_minY[NNN] - seed_maxY[MMM];
269  else dYclus_box = seed_minY[MMM] - seed_maxY[NNN];
270 
271 
272  if( dXclus_box < dXclusBoxMax && dYclus_box < dYclusBoxMax ) {
273  // merge clusters!
274  // merge by adding seed NNN to seed MMM and erasing seed NNN
275 
276  // calculate running mean for the merged seed:
277  // use the temp variables from above for phi of the NNN cluster
278  running_meanX[MMM] = (temp_meanX*seeds[NNN].size() + running_meanX[MMM]*seeds[MMM].size()) / (seeds[NNN].size()+seeds[MMM].size());
279  running_meanY[MMM] = (running_meanY[NNN]*seeds[NNN].size() + running_meanY[MMM]*seeds[MMM].size()) / (seeds[NNN].size()+seeds[MMM].size());
280 
281  // update min/max X and Y for box containing the hits in the merged cluster:
282  // use the temp variables from above for phi of the NNN cluster
283  if ( temp_minX <= seed_minX[MMM] ) seed_minX[MMM] = temp_minX;
284  if ( temp_maxX > seed_maxX[MMM] ) seed_maxX[MMM] = temp_maxX;
285  if ( seed_minY[NNN] <= seed_minY[MMM] ) seed_minY[MMM] = seed_minY[NNN];
286  if ( seed_maxY[NNN] > seed_maxY[MMM] ) seed_maxY[MMM] = seed_maxY[NNN];
287 
288  // now check to see if the running mean has moved outside of the allowed -pi to pi region
289  // if so, then adjust shift all values up or down by 2 * pi
290  if (running_meanX[MMM] > TMath::Pi()) {
291  running_meanX[MMM] = running_meanX[MMM] - 2.*TMath::Pi();
292  seed_minX[MMM] = seed_minX[MMM] - 2.*TMath::Pi();
293  seed_maxX[MMM] = seed_maxX[MMM] - 2.*TMath::Pi();
294  }
295  if (running_meanX[MMM] < -TMath::Pi()) {
296  running_meanX[MMM] = running_meanX[MMM] + 2.*TMath::Pi();
297  seed_minX[MMM] = seed_minX[MMM] + 2.*TMath::Pi();
298  seed_maxX[MMM] = seed_maxX[MMM] + 2.*TMath::Pi();
299  }
300 
301  // add seed NNN to MMM (lower to larger number)
302  seeds[MMM].insert(seeds[MMM].end(),seeds[NNN].begin(),seeds[NNN].end());
303 
304  // mark seed NNN as used (at the moment just set running mean to 999999.)
305  running_meanX[NNN] = 999999.;
306  running_meanY[NNN] = 999999.;
307  // we have merged a seed (NNN) to the highter seed (MMM) - need to contimue to
308  // next seed (NNN+1)
309  break;
310  }
311 
312  }
313  }
314  bool tooCloseClusters = false;
315  if(seeds.size()>1){
316  std::vector <double> seedTheta(seeds.size());
317  for(unsigned int iSeed = 0;iSeed<seeds.size();++iSeed){
318  seedTheta[iSeed] = seeds[iSeed][0]->globalPosition().theta();
319  if(iSeed){
320  double dTheta = fabs(seedTheta[iSeed] - seedTheta[iSeed-1]);
321  if (dTheta < 0.5){ //? should be something more clever
322  tooCloseClusters = true;
323  break;
324  }
325  }
326  }
327 
328  }
329 
330  // have formed clusters from all hits except for 2D DT segments. Now add the 2D segments to the
331  // compatible clusters. For this we compare the mean cluster postition with the
332  // 2D segment position. We should use the valid coordinate only and use the bad coordinate
333  // as a cross check.
334  for(unsigned int NNN = 0; NNN < seeds.size(); ++NNN) {
335  if(running_meanX[NNN] == 999999.) continue; //skip seeds that have been marked as used up in merging
336 
337  // We have a valid cluster - loop over all 2D segments.
338  if(useDT2D_hasZed) {
339  for (MuonRecHitContainer::const_iterator it2 = muonRecHits_DT2D_hasZed.begin(); it2 != muonRecHits_DT2D_hasZed.end(); ++it2 ) {
340  // check that global theta of 2-D segment lies within cluster box plus or minus allowed slop
341  if (((*it2)->globalPosition().theta() < seed_maxY[NNN] + dYclusBoxMax) && ((*it2)->globalPosition().theta() > seed_minY[NNN] - dYclusBoxMax)) {
342  // check that global phi of 2-D segment (assumed to be center of chamber since no phi hit info)
343  // matches with cluster box plus or minus allowed slop given that the true phi value could be
344  // anywhere within a given chamber (+/- 5 degrees ~ 0.09 radians from center)
345  if(
346  !(
347  (
348  ((*it2)->globalPosition().phi() + 0.09) < (seed_minX[NNN] - dXclusBoxMax)
349  &&
350  ((*it2)->globalPosition().phi() - 0.09) < (seed_minX[NNN] - dXclusBoxMax)
351  )
352  ||
353  (
354  ((*it2)->globalPosition().phi() + 0.09) > (seed_maxX[NNN] + dXclusBoxMax)
355  &&
356  ((*it2)->globalPosition().phi() - 0.09) > (seed_maxX[NNN] + dXclusBoxMax)
357  )
358  )
359  ) { // we have checked that the 2Dsegment is within tight theta boundaries and loose phi boundaries of the current cluster -> add it
360  seeds[NNN].push_back((*it2));
361 
362  }
363  }
364  }
365 
366  }
367 
368  // put DT hasphi loop here
369  if (useDT2D_hasPhi) {
370 
371  for (MuonRecHitContainer::const_iterator it2 = muonRecHits_DT2D_hasPhi.begin(); it2 != muonRecHits_DT2D_hasPhi.end(); ++it2 ) {
372  if (((*it2)->globalPosition().phi() < seed_maxX[NNN] + dXclusBoxMax) && ((*it2)->globalPosition().phi() > seed_minX[NNN] - dXclusBoxMax)) {
373  if(
374  !(
375  (
376  ((*it2)->globalPosition().theta() + 0.3) < (seed_minY[NNN] - dYclusBoxMax)
377  &&
378  ((*it2)->globalPosition().theta() - 0.3) < (seed_minY[NNN] - dYclusBoxMax)
379  )
380  ||
381  (
382  ((*it2)->globalPosition().theta() + 0.3) > (seed_maxY[NNN] + dYclusBoxMax)
383  &&
384  ((*it2)->globalPosition().theta() - 0.3) > (seed_maxY[NNN] + dYclusBoxMax)
385  )
386  )
387  ) { // we have checked that the 2Dsegment is within tight phi boundaries and loose theta boundaries of the current cluster -> add it
388  seeds[NNN].push_back((*it2)); // warning - neeed eta/theta switch here
389 
390  }
391  }
392  }
393  } // DT2D_hastPhi loop
394 
395  // put RPC loop here
396  int secondCh = 0;
397  DetId detId_prev;
398  if(seeds[NNN].size()>1){// actually we should check how many chambers with measurements are present
399  for(unsigned int iRH = 0 ;iRH<seeds[NNN].size() ;++iRH){
400  if( iRH && detId_prev != seeds[NNN][iRH]->hit()->geographicalId()){
401  ++secondCh;
402  break;
403  }
404  detId_prev = seeds[NNN][iRH]->hit()->geographicalId();
405  }
406  }
407 
408  if (useRPCs && !secondCh && !tooCloseClusters) {
409  for (MuonRecHitContainer::const_iterator it2 = muonRecHits_RPC.begin(); it2 != muonRecHits_RPC.end(); ++it2 ) {
410  if (((*it2)->globalPosition().phi() < seed_maxX[NNN] + dXclusBoxMax) && ((*it2)->globalPosition().phi() > seed_minX[NNN] - dXclusBoxMax)) {
411  if(
412  !(
413  (
414  ((*it2)->globalPosition().theta() + 0.3) < (seed_minY[NNN] - dYclusBoxMax)
415  &&
416  ((*it2)->globalPosition().theta() - 0.3) < (seed_minY[NNN] - dYclusBoxMax)
417  )
418  ||
419  (
420  ((*it2)->globalPosition().theta() + 0.3) > (seed_maxY[NNN] + dYclusBoxMax)
421  &&
422  ((*it2)->globalPosition().theta() - 0.3) > (seed_maxY[NNN] + dYclusBoxMax)
423  )
424  )
425  ) { // we have checked that the 2Dsegment is within tight phi boundaries and loose theta boundaries of the current cluster -> add it
426  seeds[NNN].push_back((*it2)); // warning - neeed eta/theta switch here
427 
428  }
429  }
430  }
431  } // RPC loop
432  }
433 
434  // hand over the final seeds to the output
435  // would be more elegant if we could do the above step with
436  // erasing the merged ones, rather than the
437  for(unsigned int NNN = 0; NNN < seeds.size(); ++NNN) {
438  if(running_meanX[NNN] == 999999.) continue; //skip seeds that have been marked as used up in merging
439  //std::cout<<"Next Cluster..."<<std::endl;
440  segments_clusters.push_back(seeds[NNN]);
441  }
442 }
443 
444 
446  const LocalPoint& localPosition, const LocalError& localError,
447  const LocalVector& localDirection, const LocalError& localDirectionError,
448  const double& chi2, const int& ndf){
449  // drop segments which are "bad"
450  bool dropTheSegment = true;
451  const GeomDet* geomDet = theService->trackingGeometry()->idToDet( detId );
452  // only segments whithin the boundaries of the chamber
453  bool insideCh = geomDet->surface().bounds().inside(localPosition, localError,outsideChamberErrorScale);
454 
455  // Don't use segments (nearly) parallel to the chamberi;
456  // the direction vector is normalized (R=1)
457  bool parallelSegment = fabs(localDirection.z())>minLocalSegmentAngle? false: true;
458 
459  if(insideCh && !parallelSegment){
460  dropTheSegment = false;
461  }
462  // use chi2 too? (DT, CSCs, RPCs; 2D, 4D;...)
463 
464 
465  return dropTheSegment;
466 }
EDGetTokenT< ProductType > consumes(edm::InputTag const &tag)
const double Pi
T getParameter(std::string const &) const
edm::EDGetTokenT< DTRecSegment4DCollection > dtToken
edm::EDGetTokenT< RPCRecHitCollection > rpcToken
edm::EDGetTokenT< CSCSegmentCollection > cscToken
virtual bool inside(const Local3DPoint &) const =0
Determine if the point is inside the bounds.
const std::string metname
const Bounds & bounds() const
Definition: Surface.h:120
const Plane & surface() const
The nominal surface of the GeomDet.
Definition: GeomDet.h:40
T z() const
Definition: PV3DBase.h:64
bool segmentCleaning(const DetId &detId, const LocalPoint &localPosition, const LocalError &localError, const LocalVector &localDirection, const LocalError &localDirectionError, const double &chi2, const int &ndf)
#define end
Definition: vmac.h:37
bool insert(Storage &iStorage, ItemType *iItem, const IdTag &iIdTag)
Definition: HCMethods.h:49
How EventSelector::AcceptEvent() decides whether to accept an event for output otherwise it is excluding the probing of A single or multiple positive and the trigger will pass if any such matching triggers are PASS or EXCEPTION[A criterion thatmatches no triggers at all is detected and causes a throw.] A single negative with an expectation of appropriate bit checking in the decision and the trigger will pass if any such matching triggers are FAIL or EXCEPTION A wildcarded negative criterion that matches more than one trigger in the trigger but the state exists so we define the behavior If all triggers are the negative crieriion will lead to accepting the event(this again matches the behavior of"!*"before the partial wildcard feature was incorporated).The per-event"cost"of each negative criterion with multiple relevant triggers is about the same as!*was in the past
MuonServiceProxy * theService
Definition: DetId.h:18
MuonTransientTrackingRecHit::MuonRecHitContainer MuonRecHitContainer
SETPatternRecognition(const edm::ParameterSet &pset, edm::ConsumesCollector &iC)
virtual void produce(const edm::Event &event, const edm::EventSetup &eSetup, std::vector< MuonRecHitContainer > &result)
#define begin
Definition: vmac.h:30
static MuonRecHitPointer specificBuild(const GeomDet *geom, const TrackingRecHit *rh)
tuple size
Write out results.
ParameterSet const & parameterSet(Provenance const &provenance)
Definition: Provenance.cc:11