Rivet Analyses Reference

ATLAS_2017_I1624693

Study of ordered hadron chains at 7 TeV
Experiment: ATLAS (LHC)
Inspire ID: 1624693
Status: VALIDATED
Authors:
  • Sharka Todorova-Nova
  • Christian Gutschow
References:Beams: p+ p+
Beam energies: (3500.0, 3500.0) GeV
Run details:
  • minimum bias, charged tracks with pT>100 MeV, |eta|<2.5

The analysis of the momentum difference between charged hadrons in high-energy proton-proton collisions is performed in order to study coherent particle production. The observed correlation pattern agrees with a model of a helical QCD string fragmenting into a chain of ground-state hadrons. A threshold momentum difference in the production of adjacent pairs of charged hadrons is observed, in agreement with model predictions. The presence of low-mass hadron chains also explains the emergence of charge-combination-dependent two-particle correlations commonly attributed to Bose-Einstein interference. The data sample consists of 190 $\mu\text{b}^{-1}$ of minimum-bias events collected with proton-proton collisions at a center-of-mass energy $\sqrt{s}$ = 7 TeV in the early low-luminosity data taking with the ATLAS detector at the LHC.

Source code: ATLAS_2017_I1624693.cc
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
// -*- C++ -*-
#include "Rivet/Analysis.hh"
#include "Rivet/Projections/FinalState.hh"
#include "Rivet/Projections/ChargedFinalState.hh"

namespace Rivet {

  /// @brief Study of ordered hadron chains at 7 TeV
  class ATLAS_2017_I1624693 : public Analysis {
  public:

    /// Constructor
    RIVET_DEFAULT_ANALYSIS_CTOR(ATLAS_2017_I1624693);

    /// @name Analysis methods
    //@{
    struct usedX {
      
      int locMin;
      int locMax;
      std::vector<std::pair<int,float> > chains; 
      
      // Constructor
      usedX(int min, int max, int ic, float mass) {
        locMin=min;
        locMax=max;
        chains.clear();
        chains.push_back(std::pair<int,float>(ic,mass));
      }
      
      // Constructor
      usedX(int min, int max) {
        locMin=min;
        locMax=max;
        chains.clear();
      }

      void add(int jc, float mass) {
        
        if (chains.size()) {
          std::vector<std::pair<int,float> >::iterator it=chains.begin();
          while ( it!=chains.end() && mass>(*it).second )  ++it;
          chains.insert(it,std::pair<int,float>(jc,mass));
        }
        else {
          chains.push_back(std::pair<int,float>(jc,mass));
        }
      }
    };


    /// Book histograms and initialise projections before the run
    void init() {

      /// @todo Initialise and register projections here
      ChargedFinalState cfs((Cuts::etaIn(-2.5, 2.5) && Cuts::pT >=  0.1*GeV));
      declare(cfs,"CFS");

      // pion mass;
      pim = 0.1396;     

      /// @todo Book histograms here, e.g.:
      book(_DeltaQ , 1, 1, 1);
      book(_Delta3h, 2, 1, 1);
      book(_dalitz , 3, 1, 1);

      // auxiliary
      book(_h_nch, "_nch", 200, -0.5, 199.5);

    }


    /// Perform the per-event analysis
    void analyze(const Event& event) {
      //const double weight = event.weight();
      bool match =false;

      /// @todo Do the event by event analysis here
      const ChargedFinalState& had = applyProjection<ChargedFinalState>(event, "CFS");
      Particles hs=had.particles();
      int nch = hs.size(); 

      if (nch < 3)  return; 

      _h_nch->fill(1.*nch,1.);
     
      for (unsigned int i=0; i < hs.size() - 1; ++i) {
        for (unsigned int j=i+1; j < hs.size(); ++j) {
          double q12 = qq(hs[i],hs[j],match);
          if (match) _DeltaQ->fill(q12,-1.);
          else       _DeltaQ->fill(q12,1.);
        }
      }

      // chain selection
     
      std::vector<float> wchain;
      std::vector< std::vector<unsigned int> > rchains;
      std::vector< std::vector<float> > mchains;
      wchain.clear();  rchains.clear();  mchains.clear();
      for (unsigned int ip1 = 0; ip1< hs.size(); ++ip1 ) {
        wchain.push_back(1.);
        std::vector<unsigned int> cc(1,ip1);
        std::vector<float> mc;

        double qlmin=10000.; int ilmin=-1;
        for (unsigned ip2 = 0; ip2 < hs.size(); ++ip2) {
          if (ip2==ip1) continue;
          double ql = qq(hs[ip1],hs[ip2],match);
          if (!match) continue;    // looking for closest like-sign match      
          if (ql <qlmin) { qlmin=ql; ilmin=ip2;}
        }
        if (ilmin<0) { 
          wchain.back()=0.;
          mc.push_back(-1.);
        }
        else {     // search for unlike-sign match
          cc.push_back(ilmin);
          mc.push_back(qlmin);
          if (int(ip1)>ilmin && rchains[ilmin][1]==ip1) {
            // std::cout <<"exclusive match:"<< std::endl;
            wchain.back()=0.5; wchain[ilmin]=0.5;
          }
          
          double m3min=10000.; int ixmin=-1;
          for (unsigned ip2 = 0; ip2< hs.size(); ++ip2) {
            if (ip2==ip1 || int(ip2)==ilmin )  continue;
            double qx = qq(hs[ip1],hs[ip2],match);
            if (match)  continue;
            double qxl = qq(hs[ip2],hs[ilmin],match);
            double m3 = sqrt(9*pim*pim+qxl*qxl+qlmin*qlmin+qx*qx);
            if (m3 <m3min) { m3min=m3; ixmin=ip2;}
          }

          if (ixmin<0) {
            wchain.back()=0.;
            mc.push_back(-1.);
          }
          else {
            cc.push_back(ixmin);
            mc.push_back(m3min);
          }
        }
        rchains.push_back(cc);
        mchains.push_back(mc);
      }

      // cleanup: association rate for like-sign pairs should not exceed 2
      std::vector<float> assoc(hs.size(),0.);       // cache for association rate
      std::vector<bool> accept(rchains.size(), false);
      // loop over chains and accept lowest masses while watching the association rate
      int inext = 0;
      while ( inext>-1 ) {
        inext = -1; float cMin = 100000.;
        // find non-accepted chain with lowest Q_ls; dissolve chains if association count over 2 
        for (unsigned int ic=0; ic < rchains.size(); ++ic) {
          if (rchains[ic].size() < 2)  continue;
          if (accept[ic])  continue;
          if (mchains[ic][0] < cMin) { cMin = mchains[ic][0]; inext=ic; }
        }
        if (inext>-1 ) {
          unsigned int cloc0 = rchains[inext][0];
          unsigned int cloc1 = rchains[inext][1];
          if ( (assoc[cloc0] + 1. <= 2.)  &&  (assoc[cloc1] + 1. <= 2.) ) { // chain can be accepted
            accept[inext]=true;
            assoc[cloc0]+=1.;
            assoc[cloc1]+=1.;
            if (wchain[inext]==0.5) {  // accept the identical chain, too
              for (unsigned int ic=0; ic<hs.size(); ++ic) {
                if (rchains[ic][0] == cloc1 && rchains[ic][1] == cloc0) {   
                  accept[ic]=true;
                  break;
                }
              }
            }
          }
          else if ( assoc[cloc0]>1 ) { // association count filled up, discard chain
            accept[inext]=true;
            wchain[inext]=0.;
          }
          else {   // dissolve chain and find new association
            unsigned int i1 = rchains[inext][0];
            float mMn = 1000000.;
            int ipn = -1;
            for (unsigned int i2=0; i2<hs.size(); ++i2) {
              if (i1 == i2)  continue;
              double m = qq(hs[i1],hs[i2],match);
              if (!match)  continue;
              if (assoc[i2] > 1.)  continue;
              if (m > 0. && m <mMn ) { mMn = m; ipn = i2;}
            }
            if (ipn >= 0) {
              rchains[inext][1]=ipn;  mchains[inext][0]=mMn;
              // resolve chain weight : by default, it is 1.
              wchain[inext]=1.;
              // check exclusivity of pairing
              for (unsigned int ico=0; ico<hs.size(); ++ico) {
                if (int(rchains[ico][0]) == ipn && rchains[ico][1] == i1) {   // scale the contribution from both chains
                  wchain[ico]=0.5;
                  wchain[inext]=0.5;
                }
              }
              // add 3.member
              // continue with arbitrary match
              int ipnn=-1; float mMnn = 10000.;
              mMn = 1000000.;
              for (unsigned int ij=0; ij < hs.size(); ++ij) {
                rchains[inext].resize(2);
                float q02 = qq(hs[i1],hs[ij],match);
                if (match>0.)  continue;
                float q12 = qq(hs[ipn],hs[ij],match);
                double m3 = sqrt(9*pim*pim+q02*q02+mMn*mMn+q12*q12);
                if (m3>0. && m3 <mMnn ) { mMnn = m3; ipnn = ij; }
              }
              if (ipnn>=0) { rchains[inext].push_back(ipnn); rchains[inext][2]=ipnn; mchains[inext][1]=mMnn; }
              else {accept[inext]=true; wchain[inext]=0.;}
            }
            else { // chain not recovered
              wchain[inext]=0.;
              accept[inext]=true; 
            }
          }
        }
      }  // end loop over chains
 
      // cleanup: association rate for unlike-sign pairs
      // third member verification
      std::vector<bool> accept3(rchains.size(),false);
      // watch unlike-sign combinations used
      std::vector<usedX> used;
      // loop over chains and accept lowest masses while watching the association rate
      inext = 0;
      while ( inext>-1 ) {
        inext = -1; float cMin = 100000.;
        // find non-accepted chain with lowest mass; dissolve chains if association count over 3 
        for (unsigned int ic=0; ic < rchains.size(); ++ic) {
          if (rchains[ic].size() < 3 || !wchain[ic] || !accept[ic])  continue;
          if (accept3[ic])  continue;
          if (mchains[ic][1]<cMin) { cMin = mchains[ic][1]; inext=ic; }
        }
        // check association counts
        if (inext>-1 ) {
          unsigned int cloc0 = rchains[inext][0];
          unsigned int cloc1 = rchains[inext][1];
          unsigned int cloc2 = rchains[inext][2];

          // map use of unlike sign pairs
          int iu0 = -1; float w0=0.;
          for (unsigned int iu=0; iu<used.size(); ++iu) {
            if (fmin(cloc0,cloc2)==used[iu].locMin && fmax(cloc0,cloc2)==used[iu].locMax ) {
              iu0=iu;
              if (used[iu].chains.size() > 0) 
                for (unsigned int iw=0; iw<used[iu].chains.size(); ++iw)  w0+=wchain[used[iu].chains[iw].first];
              //used[iu].add(i1,mch[1]);
              break;
            }
          }
          if (iu0<0) { used.push_back(usedX(fmin(cloc0,cloc2),fmax(cloc0,cloc2)));iu0=used.size()-1; }
          int iu1 = -1; float w1=0.;
          for (unsigned int iu=0; iu<used.size(); ++iu) {
            if (fmin(cloc1,cloc2)==used[iu].locMin && fmax(cloc1,cloc2)==used[iu].locMax) {
              iu1=iu;
              if (used[iu].chains.size()>0) 
                for (unsigned int iw=0; iw<used[iu].chains.size(); iw++) w1 += wchain[used[iu].chains[iw].first];
              //used[iu].add(inext,mch[1]);
              break;
            }
          }
          if (iu1<0) { used.push_back(usedX(fmin(cloc1,cloc2),fmax(cloc1,cloc2))); iu1=used.size()-1; }

          if ( assoc[cloc2] < 3. && w0 < 2. && w1 < 2.) {
            accept3[inext] = true;
            assoc[cloc2] += 1.;
            used[iu0].add(inext, mchains[inext][1]);
            used[iu1].add(inext, mchains[inext][1]);
            if (wchain[inext]==0.5) {  // accept the identical chain, too
              for (unsigned int ic=0; ic< rchains.size(); ++ic) {
                if (rchains[ic][0]==cloc1 && rchains[ic][1] == cloc0) { 
                  accept3[ic]=true;
                  used[iu0].add(ic, mchains[ic][1]);
                  used[iu1].add(ic, mchains[ic][1]);
                  break;
                }
              }
            }
          }
          else { // find new association
            int i1 = rchains[inext][0];
            int i2 = rchains[inext][1];
            float mMn = 1000000.;
            int ipn=-1; int iploc=-1;
            rchains[inext].pop_back();
            for (unsigned int i3 = 0; i3 < hs.size(); ++i3) {
              double q02 = qq(hs[i1],hs[i3],match);
              if (match > 0.)  continue;
              if (assoc[i3] > 3-wchain[inext])  continue;
              // check pair association
              w0=0.; w1=0.;
              for (unsigned int iu=0; iu<used.size(); ++iu) {
                if (fmin(cloc0,i3)==used[iu].locMin && fmax(cloc0,i3)==used[iu].locMax ) {
                  if (used[iu].chains.size() > 0)
                    for (unsigned int iw=0; iw<used[iu].chains.size(); ++iw)  w0 += wchain[used[iu].chains[iw].first];
                }
                if (fmin(cloc1,i3)==used[iu].locMin && fmax(cloc1,i3)==used[iu].locMax ) {
                  if (used[iu].chains.size()>0) 
                    for (unsigned int iw=0; iw<used[iu].chains.size(); ++iw)  w1 += wchain[used[iu].chains[iw].first];
                }
              }
              if (w0+wchain[inext]>2. || w1+wchain[inext]>2.) continue;

              float q12 = qq(hs[i2],hs[i3],match);
              float q01 = qq(hs[i1],hs[i2],match);
              float m = sqrt(9*pim*pim+q02*q02+q01*q01+q12*q12);
              if (m>0. && m <mMn ) { mMn = m; ipn = i3; iploc = i3; }
            }
            if (ipn>=0) { 
              rchains[inext].push_back(ipn); rchains[inext][2]=iploc; mchains[inext][1]=mMn;
            }
            else { // chain not recovered
              wchain[inext]=0.;
            }
          }
        }
      }  // end loop over chains
      // end 3rd member optimization

      for (unsigned int ip=0; ip < wchain.size(); ++ip) {
        if (!wchain[ip])  continue;
        if (rchains[ip].size() < 3)  continue;
        float m3min = mchains[ip][1];
        if (m3min > 0.59)  continue;
        // dalitz plot
        std::pair<float,float> dd = dalitz3(hs[rchains[ip][0]], hs[rchains[ip][1]], hs[rchains[ip][2]]);
        _dalitz->fill(dd.first,dd.second,1.*wchain[ip]);
        // Delta(Q) spectra
        float qlmin = mchains[ip][0]; 
        float qxmin = qq(hs[rchains[ip][0]], hs[rchains[ip][2]], match); 
        float xlmin = qq(hs[rchains[ip][1]], hs[rchains[ip][2]], match); 
        _Delta3h->fill(qxmin, 0.5*wchain[ip]);
        _Delta3h->fill(xlmin, 0.5*wchain[ip]);
        _Delta3h->fill(qlmin, -1.*wchain[ip]);
      }
    }

    /// Normalise histograms etc., after the run
    void finalize() {

      
      // normalize by the number of charged particles
      // counter automatic division by bin size 
      double norm = 0.01 / (_h_nch->xMean()*_h_nch->numEntries());
      _dalitz->scaleW(norm);
      _DeltaQ->scaleW(norm);
      _Delta3h->scaleW(norm);
      
    }

    //@}
    double qq(const Particle& gp1, const Particle& gp2, bool& match) {
      match = gp1.charge() * gp2.charge() > 0;
      FourMomentum p1, p2;
      p1.setPM(gp1.px(), gp1.py(), gp1.pz(), pim);
      p2.setPM(gp2.px(), gp2.py(), gp2.pz(), pim);
      return sqrt(fmax(0., (p1 + p2).mass2() - 4*pim*pim));
    }

    std::pair<float,float> dalitz3(const Particle& gp1, const Particle& gp2, const Particle& gp3) const {
      
      float p1= gp1.pt();
      float p2= gp2.pt();
      float p3= gp3.pt();
      float th1 = gp1.theta();
      float th2 = gp2.theta();
      float th3 = gp3.theta();
      float ph1 = gp1.phi();
      float ph2 = gp2.phi();
      float ph3 = gp3.phi();
      float e1 = sqrt(p1*p1+pim*pim);
      float e2 = sqrt(p2*p2+pim*pim);
      float e3 = sqrt(p3*p3+pim*pim);
      
      float p1x = p1*cos(ph1)*sin(th1);
      float p1y = p1*sin(ph1)*sin(th1);
      float p1z = p1*cos(th1);
      
      float p2x = p2*cos(ph2)*sin(th2);
      float p2y = p2*sin(ph2)*sin(th2);
      float p2z = p2*cos(th2);
      
      float p3x = p3*cos(ph3)*sin(th3);
      float p3y = p3*sin(ph3)*sin(th3);
      float p3z = p3*cos(th3);
      
      float px = p1x+p2x+p3x;
      float py = p1y+p2y+p3y;
      float pz = p1z+p2z+p3z;
      float ap = sqrt(px*px+py*py+pz*pz);
      float e=e1+e2+e3; 
      
      float beta = ap/e;
      float gamma = 1./sqrt(1-beta*beta);
      
      float p1l = (p1x*px+p1y*py+p1z*pz)/ap;
      float p2l = (p2x*px+p2y*py+p2z*pz)/ap;
      float p3l = (p3x*px+p3y*py+p3z*pz)/ap;
      
      float e1_boost = gamma*e1-gamma*beta*p1l;
      float e2_boost = gamma*e2-gamma*beta*p2l;
      float e3_boost = gamma*e3-gamma*beta*p3l;
      
      float Q = sqrt(e*e-ap*ap)-3*pim;
      
      return std::pair<float,float>(sqrt(3.)*(e1_boost-e2_boost)/Q , 3*(e3_boost-pim)/Q-1.);
    }
    
  private:

    // Data members like post-cuts event weight counters go here
    float pim;

  private:

    /// @name Histograms
    Histo1DPtr  _DeltaQ;
    Histo1DPtr _Delta3h;
    Histo1DPtr   _h_nch;
    Histo2DPtr _dalitz;
    //@}
  };

  // This global object acts as a hook for the plugin system
  RIVET_DECLARE_PLUGIN(ATLAS_2017_I1624693);
}