source: HiSusy/trunk/Pythia8/pythia8170/examples/LHAupAlpgen.h @ 1

Last change on this file since 1 was 1, checked in by zerwas, 11 years ago

first import of structure, PYTHIA8 and DELPHES

File size: 35.1 KB
Line 
1// LHAupAlpgen.h is a part of the PYTHIA event generator.
2// Copyright (C) 2012 Torbjorn Sjostrand.
3// PYTHIA is licenced under the GNU GPL version 2, see COPYING for details.
4// Please respect the MCnet Guidelines, see GUIDELINES for details.
5
6// Author: Richard Corke (richard.corke@thep.lu.se)
7// This file provides the following classes:
8//   AlpgenPar:   a class for parsing ALPGEN parameter files
9//                and reading back out the values
10//   LHAupAlpgen: an LHAup derived class for reading in ALPGEN
11//                format event files
12//   AlpgenHooks: a UserHooks derived class for providing
13//                'Alpgen:*' user options
14// Example usage is shown in main32.cc, and further details
15// can be found in the 'Alpgen and MLM Merging' manual page.
16
17#ifndef LHAUPALPGEN_H
18#define LHAUPALPGEN_H
19
20// Includes and namespace
21#include "Pythia.h"
22using namespace Pythia8;
23
24//==========================================================================
25
26// Preprocessor settings
27
28// Debug flag to print all particles in each event
29//#define LHADEBUG
30// Debug flag to print particles when an e/p imbalance is found
31//#define LHADEBUGRESCALE
32
33//==========================================================================
34
35// AlpgenPar: Class to parse ALPGEN parameter files and make them
36//            available through a simple interface
37
38class AlpgenPar {
39
40public:
41
42  // Constructor
43  AlpgenPar(Info *infoPtrIn = NULL) : infoPtr(infoPtrIn) {}
44
45  // Parse as incoming ALPGEN parameter file (passed as string)
46  bool parse(const string paramStr);
47
48  // Parse an incoming parameter line
49  void extractRunParam(string line);
50
51  // Check if a parameter exists
52  bool haveParam(const string &paramIn) {
53    return (params.find(paramIn) == params.end()) ? false : true; }
54
55  // Get a parameter as a double or integer.
56  // Caller should have already checked existance of the parameter.
57  double getParam(const string &paramIn) {
58    return (haveParam(paramIn)) ? params[paramIn] : 0.; }
59  int    getParamAsInt(const string &paramIn) {
60    return (haveParam(paramIn)) ? int(params[paramIn]) : 0.; }
61
62  // Print parameters read from the '.par' file
63  void printParams();
64
65private:
66
67  // Warn if a parameter is going to be overwriten
68  void warnParamOverwrite(const string &paramIn, double val);
69
70  // Simple string trimmer
71  static string trim(string s);
72
73  // Storage for parameters
74  map < string, double > params;
75
76  // Info pointer if provided
77  Info *infoPtr;
78
79  // Constants
80  static const double ZEROTHRESHOLD;
81
82};
83
84//--------------------------------------------------------------------------
85
86// Main implementation of AlpgenPar class. This may be split out to a
87// separate C++ file if desired, but currently included here for ease
88// of use.
89
90// Constants: could be changed here if desired, but normally should not.
91// These are of technical nature, as described for each.
92
93// A zero threshold value for double comparisons.
94const double AlpgenPar::ZEROTHRESHOLD = 1e-10;
95
96//--------------------------------------------------------------------------
97
98// Warn if e/pT imbalance greater than these values
99// Parse an incoming Alpgen parameter file string
100
101bool AlpgenPar::parse(const string paramStr) {
102
103  // Read par file in blocks:
104  //   0 - process information
105  //   1 - run parameters
106  //   2 - cross sections
107  int block = 0;
108
109  // Loop over incoming lines
110  stringstream paramStream(paramStr);
111  string line;
112  while (getline(paramStream, line)) {
113
114    // Change to 'run parameters' block
115    if        (line.find("run parameters") != string::npos) {
116      block = 1;
117
118    // End of 'run parameters' block
119    } else if (line.find("end parameters") != string::npos) {
120      block = 2;
121
122    // Do not extract anything from block 0 so far
123    } else if (block == 0) {
124
125    // Block 1 or 2: extract parameters
126    } else {
127      extractRunParam(line);
128
129    }
130  } // while (getline(paramStream, line))
131
132  return true;
133}
134
135//--------------------------------------------------------------------------
136
137// Parse an incoming parameter line
138
139void AlpgenPar::extractRunParam(string line) {
140
141  // Extract information to the right of the final '!' character
142  size_t idx = line.rfind("!");
143  if (idx == string::npos) return;
144  string paramName = trim(line.substr(idx + 1));
145  string paramVal  = trim(line.substr(0, idx));
146  istringstream iss(paramVal);
147
148  // Special case: 'hard process code' - single integer input
149  double val;
150  if (paramName == "hard process code") {
151    iss >> val;
152    warnParamOverwrite("hpc", val);
153    params["hpc"] = val;
154
155  // Special case: 'Crosssection +- error (pb)' - two double values
156  } else if (paramName.find("Crosssection") == 0) {
157    double xerrup;
158    iss >> val >> xerrup;
159    warnParamOverwrite("xsecup", val);
160    warnParamOverwrite("xerrup", val);
161    params["xsecup"] = val;
162    params["xerrup"] = xerrup;
163
164  // Special case: 'unwtd events, lum (pb-1)' - integer and double values
165  } else if (paramName.find("unwtd events") == 0) {
166    int nevent;
167    iss >> nevent >> val;
168    warnParamOverwrite("nevent", val);
169    warnParamOverwrite("lum", val);
170    params["nevent"] = nevent;
171    params["lum"]    = val;
172
173  // Special case: 'mc,mb,...' - split on ',' for name and ' ' for values
174  } else if (paramName.find(",") != string::npos) {
175   
176    // Simple tokeniser
177    string        paramNameNow;
178    istringstream issName(paramName);
179    while (getline(issName, paramNameNow, ',')) {
180      iss >> val;
181      warnParamOverwrite(paramNameNow, val);
182      params[paramNameNow] = val;
183    }
184
185  // Default case: assume integer and double on the left
186  } else {
187    int paramIdx;
188    iss >> paramIdx >> val;
189    warnParamOverwrite(paramName, val);
190    params[paramName] = val;
191  }
192}
193
194//--------------------------------------------------------------------------
195
196// Print parameters read from the '.par' file
197
198void AlpgenPar::printParams() {
199
200  // Loop over all stored parameters and print
201  cout << fixed << setprecision(3) << endl
202       << " *-------  Alpgen parameters  -------*" << endl;
203  for (map < string, double >::iterator it = params.begin();
204       it != params.end(); ++it)
205    cout << " |  " << left << setw(13) << it->first
206         << "  |  " << right << setw(13) << it->second
207         << "  |" << endl;
208  cout << " *-----------------------------------*" << endl;
209}
210
211//--------------------------------------------------------------------------
212
213// Warn if a parameter is going to be overwriten
214
215void AlpgenPar::warnParamOverwrite(const string &paramIn, double val) {
216
217  // Check if present and if new value is different
218  if (haveParam(paramIn) &&
219      abs(getParam(paramIn) - val) > ZEROTHRESHOLD) {
220    if (infoPtr) infoPtr->errorMsg("Warning in LHAupAlpgen::"
221        "warnParamOverwrite: overwriting existing parameter", paramIn);
222  }
223}
224
225//--------------------------------------------------------------------------
226
227// Simple string trimmer
228
229string AlpgenPar::trim(string s) {
230
231  // Remove whitespace in incoming string
232  size_t i;
233  if ((i = s.find_last_not_of(" \t\r\n")) != string::npos)
234    s = s.substr(0, i + 1);
235  if ((i = s.find_first_not_of(" \t\r\n")) != string::npos)
236    s = s.substr(i);
237  return s;
238}
239
240//==========================================================================
241
242// LHAupAlpgen: LHAup derived class for reading in ALPGEN format
243//              event files.
244
245class LHAupAlpgen : public LHAup {
246
247public:
248
249  // Constructor and destructor.
250  LHAupAlpgen(const char *baseFNin, Info *infoPtrIn = NULL);
251  ~LHAupAlpgen() { closeFile(isUnw, ifsUnw); }
252
253  // Override fileFound routine from LHAup.
254  bool fileFound() { return (isUnw != NULL); }
255
256  // Override setInit/setEvent routines from LHAup.
257  bool setInit();
258  bool setEvent(int);
259
260  // Print list of particles; mainly intended for debugging
261  void printParticles();
262
263private:
264
265  // Add resonances to incoming event.
266  bool addResonances();
267
268  // Rescale momenta to remove any imbalances.
269  bool rescaleMomenta();
270
271  // Class variables
272  string    baseFN, parFN, unwFN;  // Incoming filenames
273  AlpgenPar alpgenPar;             // Parameter database
274
275  int      lprup;                  // Process code
276  double   ebmupA, ebmupB;         // Beam energies
277  int      ihvy1, ihvy2;           // Heavy flavours for certain processes
278  double   mb;                     // Bottom mass
279
280  ifstream  ifsUnw;                // Input file stream for 'unw' file
281  istream  *isUnw;                 // Input stream for 'unw' file
282
283  vector < LHAParticle > myParticles;  // Local storage for particles
284
285  // Constants
286  static const double ZEROTHRESHOLD, EWARNTHRESHOLD, PTWARNTHRESHOLD,
287                      INCOMINGMIN;
288};
289
290// ----------------------------------------------------------------------
291
292// Main implementation of LHAupAlpgen class. This may be split out to a
293// separate C++ file if desired, but currently included here for ease
294// of use.
295
296// Constants: could be changed here if desired, but normally should not.
297// These are of technical nature, as described for each.
298
299// A zero threshold value for double comparisons.
300const double LHAupAlpgen::ZEROTHRESHOLD   = 1e-10;
301
302// Warn if e/pT imbalance greater than these values
303const double LHAupAlpgen::EWARNTHRESHOLD  = 3e-3;
304const double LHAupAlpgen::PTWARNTHRESHOLD = 1e-3;
305
306// If incoming e/pZ is 0, it is reset to this value
307const double LHAupAlpgen::INCOMINGMIN     = 1e-3;
308
309// ----------------------------------------------------------------------
310
311// Constructor. Opens parameter file and parses then opens event file.
312
313LHAupAlpgen::LHAupAlpgen(const char *baseFNin, Info *infoPtrIn)
314    : baseFN(baseFNin), alpgenPar(infoPtrIn), isUnw(NULL) {
315
316  // Set the info pointer if given
317  if (infoPtrIn) setPtr(infoPtrIn);
318
319  // Read in '_unw.par' file to get parameters
320  ifstream  ifsPar;
321  istream  *isPar = NULL;
322
323  // Try gzip file first then normal file afterwards
324#ifdef GZIPSUPPORT
325  parFN = baseFN + "_unw.par.gz";
326  isPar = openFile(parFN.c_str(), ifsPar);
327  if (!ifsPar.is_open()) closeFile(isPar, ifsPar);
328#endif
329  if (isPar == NULL) {
330    parFN = baseFN + "_unw.par";
331    isPar = openFile(parFN.c_str(), ifsPar);
332    if (!ifsPar.is_open()) {
333      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::LHAupAlpgen: "
334          "cannot open parameter file", parFN);
335      closeFile(isPar, ifsPar);
336      return;
337    }
338  }
339
340  // Read entire contents into string and close file
341  string paramStr((istreambuf_iterator<char>(isPar->rdbuf())),
342                   istreambuf_iterator<char>());
343
344  // Make sure we reached EOF and not other error
345  if (ifsPar.bad()) {
346    if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::LHAupAlpgen: "
347        "cannot read parameter file", parFN);
348    return;
349  }
350  closeFile(isPar, ifsPar);
351
352  // Parse file and set LHEF header
353  alpgenPar.parse(paramStr);
354  if (infoPtr) setInfoHeader("AlpgenPar", paramStr);
355
356  // Open '.unw' events file (with possible gzip support)
357#ifdef GZIPSUPPORT
358  unwFN = baseFN + ".unw.gz";
359  isUnw = openFile(unwFN.c_str(), ifsUnw);
360  if (!ifsUnw.is_open()) closeFile(isUnw, ifsUnw);
361#endif
362  if (isUnw == NULL) {
363    unwFN = baseFN + ".unw";
364    isUnw = openFile(unwFN.c_str(), ifsUnw);
365    if (!ifsUnw.is_open()) {
366      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::LHAupAlpgen: "
367          "cannot open event file", unwFN);
368      closeFile(isUnw, ifsUnw);
369    }
370  }
371}
372
373// ----------------------------------------------------------------------
374
375// setInit is a virtual method that must be finalised here.
376// Sets up beams, strategy and processes.
377
378bool LHAupAlpgen::setInit() {
379
380  // Check that all required parameters are present
381  if (!alpgenPar.haveParam("ih2") || !alpgenPar.haveParam("ebeam")  ||
382      !alpgenPar.haveParam("hpc") || !alpgenPar.haveParam("xsecup") ||
383      !alpgenPar.haveParam("xerrup")) {
384    if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setInit: "
385        "missing input parameters");
386    return false;
387  }
388
389  // Beam IDs
390  int ih2 = alpgenPar.getParamAsInt("ih2");
391  int idbmupA = 2212;
392  int idbmupB = (ih2 == 1) ? 2212 : -2212;
393
394  // Beam energies
395  double ebeam = alpgenPar.getParam("ebeam");
396  ebmupA = ebeam;
397  ebmupB = ebmupA;
398
399  // PDF group and set (at the moment not set)
400  int pdfgupA = 0, pdfsupA = 0;
401  int pdfgupB = 0, pdfsupB = 0;
402
403  // Strategy is for unweighted events and xmaxup not important
404  int    idwtup = 3;
405  double xmaxup = 0.;
406
407  // Get hard process code
408  lprup = alpgenPar.getParamAsInt("hpc");
409
410  // Check for unsupported processes
411  if (lprup == 7 || lprup == 8 || lprup == 13) {
412    if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setInit: "
413        "process not implemented");
414    return false;
415  }
416
417  // Depending on the process code, get heavy flavour information:
418  //    6 = QQbar           + jets
419  //    7 = QQbar + Q'Qbar' + jets
420  //    8 = QQbar + Higgs   + jets
421  //   16 = QQbar + gamma   + jets
422  if (lprup == 6 || lprup == 7 || lprup == 8 || lprup == 16) {
423    if (!alpgenPar.haveParam("ihvy")) {
424      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setInit: "
425          "heavy flavour information not present");
426      return false;
427    }
428    ihvy1 = alpgenPar.getParamAsInt("ihvy");
429
430  } else ihvy1 = -1;
431  if (lprup == 7) {
432    if (!alpgenPar.haveParam("ihvy2")) {
433      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setInit: "
434          "heavy flavour information not present");
435      return false;
436    }
437    ihvy2 = alpgenPar.getParamAsInt("ihvy2");
438  } else ihvy2 = -1;
439  // For single top (process 13), get b mass to set incoming
440  mb = -1.;
441  if (lprup == 13) {
442    if (!alpgenPar.haveParam("mb")) {
443      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setInit: "
444          "heavy flavour information not present");
445      return false;
446    }
447    mb = alpgenPar.getParam("mb");
448  }
449
450  // Set the beams
451  setBeamA(idbmupA, ebmupA, pdfgupA, pdfsupA);
452  setBeamB(idbmupB, ebmupB, pdfgupB, pdfsupB);
453  setStrategy(idwtup);
454
455  // Add the process
456  double xsecup = alpgenPar.getParam("xsecup");
457  double xerrup = alpgenPar.getParam("xerrup");
458  addProcess(lprup, xsecup, xerrup, xmaxup);
459  xSecSumSave = xsecup;
460  xErrSumSave = xerrup;
461
462  // All okay
463  return true; 
464}
465
466// ----------------------------------------------------------------------
467
468// setEvent is a virtual method that must be finalised here.
469// Read in an event from the 'unw' file and setup.
470
471bool LHAupAlpgen::setEvent(int) {
472
473  // Read in the first line of the event
474  int    nEvent, iProc, nParton;
475  double Swgt, Sq;
476  string line;
477  if (!getline(*isUnw, line)) {
478    // Read was bad
479    if (ifsUnw.bad()) {
480      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setEvent: "
481          "could not read events from file");
482      return false;
483    }
484    // End of file reached
485    if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setEvent: "
486        "end of file reached");
487    return false;
488  }
489  istringstream iss1(line);
490  iss1 >> nEvent >> iProc >> nParton >> Swgt >> Sq;
491
492  // Set the process details (ignore alphaQED and alphaQCD parameters)
493  double wgtT = Swgt, scaleT = Sq;
494  setProcess(lprup, wgtT, scaleT);
495
496  // Incoming flavour and x information for later
497  int    id1T, id2T;
498  double x1T, x2T;
499  // Temporary storage for read in parton information
500  int    idT, statusT, mother1T, mother2T, col1T, col2T;
501  double pxT, pyT, pzT, eT, mT;
502  // Leave tau and spin as default values
503  double tauT = 0., spinT = 9.;
504
505  // Store particles locally at first so that resonances can be added
506  myParticles.clear();
507
508  // Now read in partons
509  for (int i = 0; i < nParton; i++) {
510    // Get the next line
511    if (!getline(*isUnw, line)) {
512      if (infoPtr) infoPtr->errorMsg("Error in LHAupAlpgen::setEvent: "
513          "could not read events from file");
514      return false;
515    }
516    istringstream iss2(line);
517
518    // Incoming (flavour, colour, anticolour, pz)
519    if (i < 2) {
520      // Note that mothers will be set automatically by Pythia, and LHA
521      // status -1 is for an incoming parton
522      iss2 >> idT >> col1T >> col2T >> pzT;
523      statusT  = -1;
524      mother1T = mother2T = 0;
525      pxT = pyT = mT = 0.;
526      eT  = abs(pzT);
527
528      // Adjust when zero pz/e
529      if (pzT == 0.) {
530        pzT = (i == 0) ? INCOMINGMIN : -INCOMINGMIN;
531        eT  = INCOMINGMIN;
532      }
533
534    // Outgoing (flavour, colour, anticolour, px, py, pz, mass)
535    } else {
536      // Note that mothers 1 and 2 corresport to the incoming partons,
537      // as set above, and LHA status +1 is for outgoing final state
538      iss2 >> idT >> col1T >> col2T >> pxT >> pyT >> pzT >> mT;
539      statusT  = 1;
540      mother1T = 1;
541      mother2T = 2;
542      eT = sqrt(max(0., pxT*pxT + pyT*pyT + pzT*pzT + mT*mT));
543    }
544
545    // Add particle
546    myParticles.push_back(LHAParticle(
547        idT, statusT, mother1T, mother2T, col1T, col2T,
548        pxT, pyT, pzT, eT, mT, tauT, spinT));
549  }
550
551  // Add resonances if required
552  if (!addResonances()) return false;
553
554  // Rescale momenta if required (must be done after full event
555  // reconstruction in addResonances)
556  if (!rescaleMomenta()) return false;
557
558  // Pass particles on to Pythia
559  for (size_t i = 0; i < myParticles.size(); i++)
560    addParticle(myParticles[i]);
561
562  // Set incoming flavour/x information and done
563  id1T = myParticles[0].idPart;
564  x1T  = myParticles[0].ePart / ebmupA;
565  id2T = myParticles[1].idPart;
566  x2T  = myParticles[1].ePart / ebmupA;
567  setIdX(id1T, id2T, x1T, x2T);
568  setPdf(id1T, id2T, x1T, x2T, 0., 0., 0., false);
569  return true;
570}
571
572// ----------------------------------------------------------------------
573
574// Print list of particles; mainly intended for debugging
575
576void LHAupAlpgen::printParticles() {
577  cout << endl << "---- LHAupAlpgen particle listing begin ----" << endl;
578  cout << scientific << setprecision(6);
579  for (int i = 0; i < int(myParticles.size()); i++) {
580    cout << setw(5)  << i
581         << setw(5)  << myParticles[i].idPart
582         << setw(5)  << myParticles[i].statusPart
583         << setw(15) << myParticles[i].pxPart
584         << setw(15) << myParticles[i].pyPart
585         << setw(15) << myParticles[i].pzPart
586         << setw(15) << myParticles[i].ePart
587         << setw(15) << myParticles[i].mPart
588         << setw(5)  << myParticles[i].mother1Part - 1
589         << setw(5)  << myParticles[i].mother2Part - 1
590         << setw(5)  << myParticles[i].col1Part
591         << setw(5)  << myParticles[i].col2Part
592         << endl;
593  }
594  cout << "----  LHAupAlpgen particle listing end  ----" << endl;
595}
596
597// ----------------------------------------------------------------------
598
599// Routine to add resonances to an incoming event based on the
600// hard process code (now stored in lprup).
601
602bool LHAupAlpgen::addResonances() {
603
604  // Temporary storage for resonance information
605  int    idT, statusT, mother1T, mother2T, col1T, col2T;
606  double pxT, pyT, pzT, eT, mT;
607  // Leave tau and spin as default values
608  double tauT = 0., spinT = 9.;
609
610  // Alpgen process dependent parts. Processes:
611  //    1 = W        + QQbar         + jets
612  //    2 = Z/gamma* + QQbar         + jets
613  //    3 = W                        + jets
614  //    4 = Z/gamma*                 + jets
615  //   10 = W        + c             + jets
616  //   14 = W        + gamma         + jets
617  //   15 = W        + QQbar + gamma + jets
618  // When QQbar = ttbar, tops are not decayed in these processes.
619  // Explicitly reconstruct W/Z resonances; assumption is that the
620  // decay products are the last two particles.
621  if (lprup <= 4 || lprup == 10 || lprup == 14 || lprup == 15) {
622    // Decay products are the last two entries
623    int i1 = myParticles.size() - 1, i2 = i1 - 1;
624
625    // Follow 'alplib/alpsho.f' procedure to get ID
626    if (myParticles[i1].idPart + myParticles[i2].idPart == 0)
627      idT = 0;
628    else
629      idT = - (myParticles[i1].idPart % 2) - (myParticles[i2].idPart % 2);
630    idT = (idT > 0) ? 24 : (idT < 0) ? -24 : 23;
631
632    // Check that we get the expected resonance type; Z/gamma*
633    if (lprup == 2 || lprup == 4) {
634      if (idT != 23) {
635        if (infoPtr) infoPtr->errorMsg("Error in "
636            "LHAupAlpgen::addResonances: wrong resonance type in event");
637        return false;
638      }
639
640    // W's
641    } else {
642      if (abs(idT) != 24) {
643        if (infoPtr) infoPtr->errorMsg("Error in "
644            "LHAupAlpgen::addResonances: wrong resonance type in event");
645        return false;
646      }
647    }
648
649    // Remaining input
650    statusT  = 2;
651    mother1T = 1;
652    mother2T = 2;
653    col1T = col2T = 0;
654    pxT = myParticles[i1].pxPart + myParticles[i2].pxPart;
655    pyT = myParticles[i1].pyPart + myParticles[i2].pyPart;
656    pzT = myParticles[i1].pzPart + myParticles[i2].pzPart;
657    eT  = myParticles[i1].ePart  + myParticles[i2].ePart;
658    mT  = sqrt(eT*eT - pxT*pxT - pyT*pyT - pzT*pzT);
659    myParticles.push_back(LHAParticle(
660        idT, statusT, mother1T, mother2T, col1T, col2T,
661        pxT, pyT, pzT, eT, mT, tauT, spinT));
662
663    // Update decay product mothers (note array size as if from 1)
664    myParticles[i1].mother1Part = myParticles[i2].mother1Part =
665        myParticles.size();
666    myParticles[i1].mother2Part = myParticles[i2].mother2Part = 0;
667
668  // Processes:
669  //    5 = nW + mZ + j gamma + lH + jets
670  //    6 = QQbar         + jets    (QQbar = ttbar)
671  //    8 = QQbar + Higgs + jets    (QQbar = ttbar)
672  //   13 = top   + q               (topprc = 1)
673  //   13 = top   + b               (topprc = 2)
674  //   13 = top   + W     + jets    (topprc = 3)
675  //   13 = top   + W     + b       (topprc = 4)
676  //   16 = QQbar + gamma + jets    (QQbar = ttbar)
677  //
678  // When tops are present, they are decayed to Wb (both the W and b
679  // are not given), with this W also decaying (decay products given).
680  // The top is marked intermediate, the (intermediate) W is
681  // reconstructed from its decay products, and the decay product mothers
682  // updated. The final-state b is reconstructed from (top - W).
683  //
684  // W/Z resonances are given, as well as their decay products. The
685  // W/Z is marked intermediate, and the decay product mothers updated.
686  //
687  // It is always assumed that decay products are at the end.
688  // For processes 5 and 13, it is also assumed that the decay products
689  // are in the same order as the resonances.
690  // For processes 6, 8 and 16, the possibility of the decay products
691  // being out-of-order is also taken into account.
692  } else if ( ((lprup == 6 || lprup == 8 || lprup == 16) && ihvy1 == 6) ||
693              lprup == 5 || lprup == 13) {
694
695    // Go backwards through the particles looking for W/Z/top
696    int idx = myParticles.size() - 1;
697    for (int i = myParticles.size() - 1; i > -1; i--) {
698
699      // W or Z
700      if (myParticles[i].idPart == 23 ||
701          abs(myParticles[i].idPart) == 24) {
702
703        // Check that decay products and resonance match up
704        int flav;
705        if (myParticles[idx].idPart + myParticles[idx - 1].idPart == 0)
706          flav = 0;
707        else
708          flav = - (myParticles[idx].idPart % 2)
709                 - (myParticles[idx - 1].idPart % 2);
710        flav = (flav > 0) ? 24 : (flav < 0) ? -24 : 23;
711        if (flav != myParticles[i].idPart) {
712          if (infoPtr)
713            infoPtr->errorMsg("Error in LHAupAlpgen::addResonance: "
714                "resonance does not match decay products");
715          return false;
716        }
717
718        // Update status/mothers
719        myParticles[i].statusPart      = 2;
720        myParticles[idx  ].mother1Part = i + 1;
721        myParticles[idx--].mother2Part = 0;
722        myParticles[idx  ].mother1Part = i + 1;
723        myParticles[idx--].mother2Part = 0;
724
725      // Top
726      } else if (abs(myParticles[i].idPart) == 6) {
727
728        // Check that decay products and resonance match up
729        int flav;
730        if (myParticles[idx].idPart + myParticles[idx - 1].idPart == 0)
731          flav = 0;
732        else
733          flav = - (myParticles[idx].idPart % 2)
734                 - (myParticles[idx - 1].idPart % 2);
735        flav = (flav > 0) ? 24 : (flav < 0) ? -24 : 23;
736
737        bool outOfOrder = false, wrongFlavour = false;;
738        if ( abs(flav) != 24 ||
739             (flav ==  24 && myParticles[i].idPart !=  6) ||
740             (flav == -24 && myParticles[i].idPart != -6) ) {
741
742          // Processes 5 and 13, order should always be correct
743          if (lprup == 5 || lprup == 13) {
744            wrongFlavour = true;
745
746          // Processes 6, 8 and 16, can have out of order decay products
747          } else {
748
749            // Go back two decay products and retry
750            idx -= 2;
751            if (myParticles[idx].idPart + myParticles[idx - 1].idPart == 0)
752              flav = 0;
753            else
754              flav = - (myParticles[idx].idPart % 2)
755                     - (myParticles[idx - 1].idPart % 2);
756            flav = (flav > 0) ? 24 : (flav < 0) ? -24 : 23;
757
758            // If still the wrong flavour then error
759            if ( abs(flav) != 24 ||
760                 (flav ==  24 && myParticles[i].idPart !=  6) ||
761                 (flav == -24 && myParticles[i].idPart != -6) )
762              wrongFlavour = true;
763            else outOfOrder = true;
764          }
765
766          // Error if wrong flavour
767          if (wrongFlavour) {
768            if (infoPtr)
769              infoPtr->errorMsg("Error in LHAupAlpgen::addResonance: "
770                  "resonance does not match decay products");
771            return false;
772          }
773        }
774
775        // Mark t/tbar as now intermediate
776        myParticles[i].statusPart = 2;
777
778        // New intermediate W+/W-
779        idT      = flav;
780        statusT  = 2;
781        mother1T = i + 1;
782        mother2T = 0;
783        col1T = col2T = 0;
784        pxT = myParticles[idx].pxPart + myParticles[idx - 1].pxPart;
785        pyT = myParticles[idx].pyPart + myParticles[idx - 1].pyPart;
786        pzT = myParticles[idx].pzPart + myParticles[idx - 1].pzPart;
787        eT  = myParticles[idx].ePart  + myParticles[idx - 1].ePart;
788        mT  = sqrt(eT*eT - pxT*pxT - pyT*pyT - pzT*pzT);
789        myParticles.push_back(LHAParticle(
790            idT, statusT, mother1T, mother2T, col1T, col2T,
791            pxT, pyT, pzT, eT, mT, tauT, spinT));
792
793        // Update the decay product mothers
794        myParticles[idx  ].mother1Part = myParticles.size();
795        myParticles[idx--].mother2Part = 0;
796        myParticles[idx  ].mother1Part = myParticles.size();
797        myParticles[idx--].mother2Part = 0;
798
799        // New final-state b/bbar
800        idT     = (flav == 24) ? 5 : -5;
801        statusT = 1;
802        // Colour from top
803        col1T   = myParticles[i].col1Part;
804        col2T   = myParticles[i].col2Part;
805        // Momentum from (t/tbar - W+/W-)
806        pxT     = myParticles[i].pxPart - myParticles.back().pxPart;
807        pyT     = myParticles[i].pyPart - myParticles.back().pyPart;
808        pzT     = myParticles[i].pzPart - myParticles.back().pzPart;
809        eT      = myParticles[i].ePart  - myParticles.back().ePart;
810        mT      = sqrt(eT*eT - pxT*pxT - pyT*pyT - pzT*pzT);
811        myParticles.push_back(LHAParticle(
812            idT, statusT, mother1T, mother2T, col1T, col2T,
813            pxT, pyT, pzT, eT, mT, tauT, spinT));
814
815        // If decay products were out of order, reset idx to point
816        // at correct decay products
817        if (outOfOrder) idx += 4;
818
819      } // if (abs(myParticles[i].idPart) == 6)
820    } // for (i)
821
822
823  // Processes:
824  //    7 = QQbar + Q'Qbar' + jets (tops are not decayed)
825  //    9 =                   jets
826  //   11 = gamma           + jets
827  //   12 = Higgs           + jets
828  } else if (lprup == 7 || lprup == 9 || lprup == 11 || lprup == 12) {
829    // Nothing to do for these processes
830  }
831
832  // For single top, set incoming b mass if necessary
833  if (lprup == 13) for (int i = 0; i < 2; i++)
834    if (abs(myParticles[i].idPart) == 5) {
835      myParticles[i].mPart = mb;
836      myParticles[i].ePart = sqrt(pow2(myParticles[i].pzPart) + pow2(mb));
837    }
838
839  // Debug output and done.
840#ifdef LHADEBUG
841  printParticles();
842#endif
843  return true;
844}
845
846// ----------------------------------------------------------------------
847
848// Routine to rescale momenta to remove any imbalances. The routine
849// assumes that any imbalances are due to decimal output/rounding
850// effects, and are therefore small.
851//
852// First any px/py imbalances are fixed by adjusting all outgoing
853// particles px/py and also updating their energy so mass is fixed.
854// Because incoming pT is zero, changes should be limited to ~0.001.
855//
856// Second, any pz/e imbalances are fixed by scaling the incoming beams
857// (again, no changes to masses required). Because incoming pz/e is not
858// zero, effects can be slightly larger ~0.002/0.003.
859
860bool LHAupAlpgen::rescaleMomenta() {
861
862  // Total momenta in/out
863  int  nOut = 0;
864  Vec4 pIn, pOut;
865  for (int i = 0; i < int(myParticles.size()); i++) {
866    Vec4 pNow = Vec4(myParticles[i].pxPart, myParticles[i].pyPart,
867                     myParticles[i].pzPart, myParticles[i].ePart);
868    if (i < 2) pIn += pNow;
869    else if (myParticles[i].statusPart == 1) {
870      nOut++;
871      pOut += pNow;
872    }
873  }
874
875  // pT out to match pT in. Split any imbalances over all outgoing
876  // particles, and scale energies also to keep m^2 fixed.
877  if (abs(pOut.pT() - pIn.pT()) > ZEROTHRESHOLD) {
878    // Differences in px/py
879    double pxDiff = (pOut.px() - pIn.px()) / nOut,
880           pyDiff = (pOut.py() - pIn.py()) / nOut;
881
882    // Warn if resulting changes above warning threshold
883    if (pxDiff > PTWARNTHRESHOLD || pyDiff > PTWARNTHRESHOLD) {
884      if (infoPtr) infoPtr->errorMsg("Warning in LHAupAlpgen::setEvent: "
885          "large pT imbalance in incoming event");
886
887      // Debug
888#ifdef LHADEBUGRESCALE
889      printParticles();
890      cout << "pxDiff = " << pxDiff << ", pyDiff = " << pyDiff << endl;
891#endif
892    }
893
894    // Adjust all final-state outgoing
895    pOut.reset();
896    for (int i = 2; i < int(myParticles.size()); i++) {
897      if (myParticles[i].statusPart != 1) continue;
898      myParticles[i].pxPart -= pxDiff;
899      myParticles[i].pyPart -= pyDiff;
900      myParticles[i].ePart   = sqrt(max(0., pow2(myParticles[i].pxPart) +
901          pow2(myParticles[i].pyPart) + pow2(myParticles[i].pzPart) +
902          pow2(myParticles[i].mPart)));
903      pOut += Vec4(myParticles[i].pxPart, myParticles[i].pyPart,
904                   myParticles[i].pzPart, myParticles[i].ePart);
905    }
906  }
907
908  // Differences in E/pZ and scaling factors
909  double de = (pOut.e()  - pIn.e());
910  double dp = (pOut.pz() - pIn.pz());
911  double a  = 1 + (de + dp) / 2. / myParticles[0].ePart;
912  double b  = 1 + (de - dp) / 2. / myParticles[1].ePart;
913
914  // Warn if resulting energy changes above warning threshold.
915  // Change in pz less than or equal to change in energy (incoming b
916  // quark can have mass at this stage for process 13). Note that for
917  // very small incoming momenta, the relative adjustment may be large,
918  // but still small in absolute terms.
919  if (abs(a - 1.) * myParticles[0].ePart > EWARNTHRESHOLD ||
920      abs(b - 1.) * myParticles[1].ePart > EWARNTHRESHOLD) {
921    if (infoPtr) infoPtr->errorMsg("Warning in LHAupAlpgen::setEvent: "
922        "large rescaling factor");
923
924    // Debug output
925#ifdef LHADEBUGRESCALE
926    printParticles();
927    cout << "de = " << de << ", dp = " << dp
928         << ", a = " << a << ", b = " << b << endl
929         << "Absolute energy change for incoming 0 = "
930         << abs(a - 1.) * myParticles[0].ePart << endl
931         << "Absolute energy change for incoming 1 = "
932         << abs(b - 1.) * myParticles[1].ePart << endl;
933#endif
934  }
935  myParticles[0].ePart  *= a;
936  myParticles[0].pzPart *= a;
937  myParticles[1].ePart  *= b;
938  myParticles[1].pzPart *= b;
939
940  // Recalculate resonance four vectors
941  for (int i = 0; i < int(myParticles.size()); i++) {
942    if (myParticles[i].statusPart != 2) continue;
943
944    // Only mothers stored in LHA, so go through all
945    Vec4 resVec;
946    for (int j = 0; j < int(myParticles.size()); j++) {
947      if (myParticles[j].mother1Part - 1 != i) continue;
948      resVec += Vec4(myParticles[j].pxPart, myParticles[j].pyPart,
949                     myParticles[j].pzPart, myParticles[j].ePart);
950    }
951
952    myParticles[i].pxPart = resVec.px();
953    myParticles[i].pyPart = resVec.py();
954    myParticles[i].pzPart = resVec.pz();
955    myParticles[i].ePart  = resVec.e();
956  }
957
958  return true;
959}
960
961//==========================================================================
962
963// AlpgenHooks: provides Alpgen file reading options.
964// Note that it is defined with virtual inheritance, so that it can
965// be combined with other UserHooks classes, see e.g. main32.cc.
966
967class AlpgenHooks : virtual public UserHooks {
968
969public:
970
971  // Constructor and destructor
972  AlpgenHooks(Pythia &pythia);
973  ~AlpgenHooks() { if (LHAagPtr) delete LHAagPtr; }
974
975  // Override initAfterBeams routine from UserHooks
976  bool initAfterBeams();
977
978private:
979
980  // LHAupAlpgen pointer
981  LHAupAlpgen *LHAagPtr;
982};
983
984// ----------------------------------------------------------------------
985
986// Main implementation of AlpgenHooks class. This may be split out to a
987// separate C++ file if desired, but currently included here for ease
988// of use.
989
990// ----------------------------------------------------------------------
991
992// Constructor: provides the 'Alpgen:file' option by directly
993//              changing the Pythia 'Beams' settings
994
995AlpgenHooks::AlpgenHooks(Pythia &pythia) : LHAagPtr(NULL) {
996
997  // If LHAupAlpgen needed, construct and pass to Pythia
998  string agFile = pythia.settings.word("Alpgen:file");
999  if (agFile != "void") {
1000    LHAagPtr = new LHAupAlpgen(agFile.c_str(), &pythia.info);
1001    pythia.settings.mode("Beams:frameType", 5);
1002    pythia.setLHAupPtr(LHAagPtr);
1003  }
1004}
1005
1006// ----------------------------------------------------------------------
1007
1008// Initialisation routine which is called by pythia.init().
1009// This happens after the local pointers have been assigned and after
1010// Pythia has processed the Beam information (and therefore LHEF header
1011// information has been read in), but before any other internal
1012// initialisation. Provides the remaining 'Alpgen:*' options.
1013
1014bool AlpgenHooks::initAfterBeams() {
1015
1016  // Read in ALPGEN specific configuration variables
1017  bool setMasses = settingsPtr->flag("Alpgen:setMasses");
1018  bool setNjet   = settingsPtr->flag("Alpgen:setNjet");
1019  bool setMLM    = settingsPtr->flag("Alpgen:setMLM");
1020
1021  // If ALPGEN parameters are present, then parse in AlpgenPar object
1022  AlpgenPar par(infoPtr);
1023  string parStr = infoPtr->header("AlpgenPar");
1024  if (!parStr.empty()) {
1025    par.parse(parStr);
1026    par.printParams();
1027  }
1028
1029  // Set masses if requested
1030  if (setMasses) {
1031    if (par.haveParam("mc")) particleDataPtr->m0(4,  par.getParam("mc"));
1032    if (par.haveParam("mb")) particleDataPtr->m0(5,  par.getParam("mb"));
1033    if (par.haveParam("mt")) particleDataPtr->m0(6,  par.getParam("mt"));
1034    if (par.haveParam("mz")) particleDataPtr->m0(23, par.getParam("mz"));
1035    if (par.haveParam("mw")) particleDataPtr->m0(24, par.getParam("mw"));
1036    if (par.haveParam("mh")) particleDataPtr->m0(25, par.getParam("mh"));
1037  }
1038
1039  // Set MLM:nJets if requested
1040  if (setNjet) {
1041    if (par.haveParam("njets"))
1042      settingsPtr->mode("MLM:nJet", par.getParamAsInt("njets"));
1043    else
1044      infoPtr->errorMsg("Warning in AlpgenHooks:init: "
1045          "no ALPGEN nJet parameter found");
1046  }
1047
1048  // Set MLM merging parameters if requested
1049  if (setMLM) {
1050    if (par.haveParam("ptjmin") && par.haveParam("drjmin") &&
1051        par.haveParam("etajmax")) {
1052      double ptjmin = par.getParam("ptjmin");
1053      ptjmin = max(ptjmin + 5., 1.2 * ptjmin);
1054      settingsPtr->parm("MLM:eTjetMin",   ptjmin);
1055      settingsPtr->parm("MLM:coneRadius", par.getParam("drjmin"));
1056      settingsPtr->parm("MLM:etaJetMax",  par.getParam("etajmax"));
1057
1058    // Warn if setMLM requested, but parameters not present
1059    } else {
1060      infoPtr->errorMsg("Warning in AlpgenHooks:init: "
1061          "no ALPGEN merging parameters found");
1062    }
1063  }
1064
1065  // Initialisation complete.
1066  return true;
1067}
1068
1069//==========================================================================
1070
1071#endif // LHAUPALPGEN_H
Note: See TracBrowser for help on using the repository browser.