Page MenuHomeHEPForge

No OneTemporary

Index: trunk/src/SartreInclusiveDiffraction.cpp
===================================================================
--- trunk/src/SartreInclusiveDiffraction.cpp (revision 550)
+++ trunk/src/SartreInclusiveDiffraction.cpp (revision 551)
@@ -1,988 +1,987 @@
//==============================================================================
// SartreInclusiveDiffraction.cpp
//
// Copyright (C) 2024 Tobias Toll and Thomas Ullrich
//
// This file is part of Sartre.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation.
// This program is distributed in the hope that it will be useful,
// but without any warranty; without even the implied warranty of
// merchantability or fitness for a particular purpose. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Tobias Toll
// Last update:
// $Date: 2024-06-03 11:27:05 -0400 (Mon, 03 Jun 2024) $
// $Author: ullrich $
//==============================================================================
//
// Note:
// When not using runcards, the user must first create an instance of
// SartreInclusiveDiffraction and then get the settings via one of:
// SartreInclusiveDiffraction::runSettings()
// EventGeneratorSettings::instance()
// Once init() is called settings cannot be changed any more.
//==============================================================================
#include "Version.h"
#include "Kinematics.h"
#include "Constants.h"
#include "SartreInclusiveDiffraction.h"
#include "Math/Functor.h"
#include "ModeFinderFunctor.h"
#include "Math/BrentMinimizer1D.h"
#include "Math/IntegratorMultiDim.h"
#include "Math/GSLMinimizer.h"
#include "TUnuranMultiContDist.h"
#include <string>
#include <limits>
#include <cmath>
#include <iomanip>
#include "TH2D.h"
#include "TFile.h"
#include "TF3.h"
#include "InclusiveDiffractiveCrossSectionsFromTables.h"
#include "InclusiveDiffractiveCrossSections.h"
#include "DglapEvolution.h"
using namespace std;
#define PR(x) cout << #x << " = " << (x) << endl;
SartreInclusiveDiffraction::SartreInclusiveDiffraction()
{
mSettings = EventGeneratorSettings::instance();
mRandom = mSettings->randomGenerator();
mIsInitialized = false;
mCurrentEvent = 0;
mNucleus = 0;
mUpcNucleus= 0;
// mSettings = 0;
mPDF_Functor = 0;
mPDF = 0;
mEventCounter = 0;
mTriesCounter = 0;
mTotalCrossSection = 0;
mTotalCrossSection_qqg = 0;
mTotalCrossSection_qq = 0;
mCrossSection = 0;
mUnuran = 0;
mEvents = 0;
mTries = 0;
mS = 0;
mA = 0;
}
SartreInclusiveDiffraction::~SartreInclusiveDiffraction()
{
delete mNucleus;
delete mUpcNucleus;
delete mPDF_Functor;
delete mPDF;
delete mCrossSection;
delete mUnuran;
delete mCurrentEvent;
}
bool SartreInclusiveDiffraction::init(const char* runcard)
{
mStartTime = time(0);
bool ok;
//
// Reset member variables.
// Note that one instance of Sartre should be able to get
// initialized multiple times.
//
mEvents = 0;
mTries = 0;
mTotalCrossSection = 0;
mTotalCrossSection_qqg = 0;
mTotalCrossSection_qq = 0;
//
// Print header
//
string ctstr(ctime(&mStartTime));
ctstr.erase(ctstr.size()-1, 1);
cout << "/========================================================================\\" << endl;
cout << "| |" << endl;
cout << "| Sartre, Version " << setw(54) << left << VERSION << right << '|' << endl;
cout << "| |" << endl;
cout << "| An event generator for inclusive diffraction |" << endl;
cout << "| in ep and eA collisions based on the dipole model. |" << endl;
cout << "| |" << endl;
cout << "| Copyright (C) 2010-2024 Tobias Toll and Thomas Ullrich |" << endl;
cout << "| |" << endl;
cout << "| This program is free software: you can redistribute it and/or modify |" << endl;
cout << "| it under the terms of the GNU General Public License as published by |" << endl;
cout << "| the Free Software Foundation, either version 3 of the License, or |" << endl;
cout << "| any later version. |" << endl;
cout << "| |" << endl;
cout << "| Code compiled on " << setw(12) << left << __DATE__;
cout << setw(41) << left << __TIME__ << right << '|' << endl;
cout << "| Run started at " << setw(55) << left << ctstr.c_str() << right << '|' << endl;
cout << "\\========================================================================/" << endl;
mSettings = EventGeneratorSettings::instance(); // EventGeneratorSettings is a singleton
mSettings->setInclusiveDiffractionMode(true);
//
// Read runcard if available
//
if (runcard) {
if (!mSettings->readSettingsFromFile(runcard)) {
cout << "Error, reading runcard '" << runcard << "'. File doesn't exist or is not readable." << endl;
exit(1);
}
else
cout << "Runcard is '" << runcard << "'." << endl;
}
else
cout << "No runcard provided." << endl;
mUseCrossSectionTables=mSettings->useInclusiveCrossSectionTables();
TableGeneratorSettings* tgsettings=TableGeneratorSettings::instance();
tgsettings->setDipoleModelType(mSettings->dipoleModelType());
tgsettings->setDipoleModelParameterSet(mSettings->dipoleModelParameterSet());
// Set up DGLAP evolution for integrals:
if(!mUseCrossSectionTables){
DglapEvolution &dglap = DglapEvolution::instance();
dglap.generateLookupTable(1000, 1000); dglap.useLookupTable(true);
}
//
// Set beam particles and center of mass energy
//
mElectronBeam = mSettings->eBeam();
mHadronBeam = mSettings->hBeam();
mS = Kinematics::s(mElectronBeam, mHadronBeam);
mA = mSettings->A();
bool allowBreakup = mSettings->enableNuclearBreakup();
if (mA == 1) allowBreakup = false;
if (allowBreakup) {
if (!getenv("SARTRE_DIR")) {
cout << "Error, environment variable 'SARTRE_DIR' is not defined. It is required\n"
"to locate tables needed for the generation if nuclear breakups." << endl;
exit(1);
}
}
if (mNucleus) delete mNucleus;
mNucleus = new FrangibleNucleus(mA, allowBreakup);
string upcNucleusName;
cout << "Sartre is running in inclusive diffraction mode" << endl;
cout << "Hadron beam species: " << mNucleus->name() << " (" << mA << ")" << endl;
cout << "Hadron beam: " << mHadronBeam << endl;
cout << "Electron beam: " << mElectronBeam << endl;
//
// Get details about the processes and models
//
mDipoleModelType = mSettings->dipoleModelType();
mDipoleModelParameterSet = mSettings->dipoleModelParameterSet();
cout << "Dipole model: " << mSettings->dipoleModelName().c_str() << endl;
cout << "Dipole model parameter set: " << mSettings->dipoleModelParameterSetName().c_str() << endl;
cout << "Process is ";
if (mA > 1)
cout << "e + " << mNucleus->name() << " -> e' + " << mNucleus->name() << "' + X"<< endl;
else
cout << "e + p -> e' + p' + X"<< endl;
//
// Print-out seed for reference
//
cout << "Random generator seed: " << mSettings->seed() << endl;
//
// Load in the tables containing the amplitude moments
//
if (!getenv("SARTRE_DIR")) {
cout << "Error, required environment variable 'SARTRE_DIR' is not defined." << endl;
exit(1);
}
//
// Kinematic limits and generator range
//
// There are 3 ranges we have to deal with
// 1. the kinematic range requested by the user
// if given.
// The user can only control Q2 and W but not t.
// For UPC that's xpom.
// 2. the range of the table(s)
// 3. the kinematically allowed range
//
// Of course (3) is more complex than a simple cube/square.
// However, we deal with the detailed shape of the kinematic
// range later using Kinematics::valid() when we generate the
// individual events.
// For setting up UNU.RAN we have to get the cubic/square
// envelope that satifies (1)-(3).
// Note, that they are correlated which makes the order
// in which we do things a bit tricky.
//
//
// Step 2:
// Kinematic limits might overrule boundaries from step 1
//
// let:
// 0: beta
// 1: Q2
// 2: W2
// 3: z
//
// Setup cross-section functor
// It is this functor that is used by all other functors,
// functions, and wrappers when dealing with cross-sections.
//
if (mCrossSection) delete mCrossSection;
if(!mUseCrossSectionTables){
mCrossSection = new InclusiveDiffractiveCrossSectionsIntegrals;
}
else{
mCrossSection = new InclusiveDiffractionCrossSectionsFromTables;
}
//
// Here we need to put in Q2 > mu02.
// The calculation goes haywire for Q2 becoming too small
//
double mu02 = mCrossSection->dipoleModel()->getParameters()->mu02();
// double mf = Settings::quarkMass(0);
double mf=tt_quarkMass[0];
//Now for the diffractive mass:
double kineMX2min = 4*mf*mf;//max(mu02, 4*mf*mf);
// This gives the limits for W2 and for inelasticity y:
double kineW2min = kineMX2min+protonMass2;
double kineYmin = Kinematics::ymin(mS, kineMX2min);
// ...and y gives the limit for Q2, unless it's smaller than the cut-off mu02:
double kineQ2min = Kinematics::Q2min(kineYmin);
kineQ2min = max(kineQ2min, mu02);
//The maximum momenta are given by s:
double kineQ2max = Kinematics::Q2max(mS);
double kineW2max = mS-kineQ2min-protonMass2;
//
// Now for all the fractions, making sure they make sense
//
double kineBetamax = kineQ2max/(kineQ2max+kineMX2min);
double kineBetamin = Kinematics::betamin(kineQ2min, mS, sqrt(kineW2min), 0);
double kineMX2max = (1.-kineBetamin)/kineBetamin*kineQ2max;
kineMX2max=min(kineMX2max, kineW2max-protonMass2);
double kineZmin=(1-sqrt(1-4*mf*mf/kineMX2max))/2.;
double kineZmax=1-kineZmin;
mLowerLimit[0] = kineBetamin;
mUpperLimit[0] = kineBetamax;
mLowerLimit[1] = kineQ2min;
mUpperLimit[1] = kineQ2max;
mLowerLimit[2] = kineW2min;
mUpperLimit[2] = kineW2max;
mLowerLimit[3] = kineZmin;
mUpperLimit[3] = kineZmax;
// Step 3:
// Deal with user provided limits.
// User settings are ignored (switched off) if min >= max.
// Only allow user limits for Q2, W2, and beta,
// not z.
//
bool W2orQ2changed=false;
if (mSettings->Wmin() < mSettings->Wmax()) { // W2 first
if (mSettings->W2min() < mLowerLimit[2]) {
cout << "Warning, requested lower limit of W (" << mSettings->Wmin() << ") "
<< "is smaller than limit given by lookup tables and/or kinematic range (" << sqrt(mLowerLimit[2]) << "). ";
cout << "Limit has no effect." << endl;
}
else {
mLowerLimit[2] = mSettings->W2min();
W2orQ2changed=true;
}
if (mSettings->W2max() > mUpperLimit[2]) {
cout << "Warning, requested upper limit of W (" << mSettings->Wmax() << ") "
<< "exceeds limit given by lookup tables and/or kinematic range (" << sqrt(mUpperLimit[2]) << "). ";
cout << "Limit has no effect." << endl;
}
else {
mUpperLimit[2] = mSettings->W2max();
W2orQ2changed=true;
}
}
if (mSettings->Q2min() < mSettings->Q2max()) { // Q2
if (mSettings->Q2min() < mLowerLimit[1]) {
cout << "Warning, requested lower limit of Q2 (" << mSettings->Q2min() << ") "
<< "is smaller than limit given by lookup tables and/or kinematic range (" << mLowerLimit[1] << "). ";
cout << "Limit has no effect." << endl;
}
else {
mLowerLimit[1] = mSettings->Q2min();
W2orQ2changed=true;
}
if (mSettings->Q2max() > mUpperLimit[1]) {
cout << "Warning, requested upper limit of Q2 (" << mSettings->Q2max() << ") "
<< "exceeds limit given by lookup tables and/or kinematic range (" << mUpperLimit[1] << "). ";
cout << "Limit has no effect." << endl;
}
else {
mUpperLimit[1] = mSettings->Q2max();
W2orQ2changed=true;
}
}
if (W2orQ2changed){//mLowerLimit beta, Q2, W2, z
//kineBetamax = kineQ2max/(kineQ2max+kineMX2min);
mUpperLimit[0] = mUpperLimit[1]/(mUpperLimit[1]+kineMX2min);
//kineBetamin = Kinematics::betamin(kineQ2min, mS, sqrt(kineW2min), 0);
mLowerLimit[0] = Kinematics::betamin(mLowerLimit[1], mS, sqrt(mLowerLimit[2]), 0);
// kineMX2max = (1.-kineBetamin)/kineBetamin*kineQ2max;
// kineMX2max=min(kineMX2max, kineW2max-protonMass2);
kineMX2max = (1.-mLowerLimit[0])/mLowerLimit[0]*mUpperLimit[1];
kineMX2max=min(kineMX2max, mUpperLimit[2]-protonMass2);
// kineZmin=(1-sqrt(1-4*mf*mf/kineMX2max))/2.;
// kineZmax=1-kineZmin;
mLowerLimit[3]=(1-sqrt(1-4*mf*mf/kineMX2max))/2.;
mUpperLimit[3]=1-mLowerLimit[3];
}
if (mSettings->betamin() < mSettings->betamax()) { // beta
if (mSettings->betamin() < mLowerLimit[0]) {
cout << "Warning, requested lower limit of beta (" << mSettings->betamin() << ") "
<< "is smaller than limit given by lookup tables and/or kinematic range (" << mLowerLimit[0] << "). ";
cout << "Limit has no effect." << endl;
}
else {
mLowerLimit[0] = mSettings->betamin();
}
if (mSettings->betamax() > mUpperLimit[0]) {
cout << "Warning, requested upper limit of Q2 (" << mSettings->betamax() << ") "
<< "exceeds limit given by lookup tables and/or kinematic range (" << mUpperLimit[0] << "). ";
cout << "Limit has no effect." << endl;
}
else {
mUpperLimit[0] = mSettings->betamax();
}
}
//
// Check if any phase space is left
//
if (mLowerLimit[0] >= mUpperLimit[0]) {
cout << "Invalid range in beta: beta=[" << mLowerLimit[0] << ", " << mUpperLimit[0] << "]." << endl;
exit(1);
}
if (mLowerLimit[1] >= mUpperLimit[1]) {
cout << "Invalid range in Q2: Q2=[";
cout << mLowerLimit[1] << ", " << mUpperLimit[1] << "]." << endl;
exit(1);
}
if (mLowerLimit[2] >= mUpperLimit[2]) {
cout << "Invalid range in W: W=[" << sqrt(mLowerLimit[2]) << ", " << sqrt(mUpperLimit[2]) << "]." << endl;
exit(1);
}
if (mLowerLimit[3] >= mUpperLimit[3]) {
cout << "Invalid range in z: z=[" << sqrt(mLowerLimit[3]) << ", " << sqrt(mUpperLimit[3]) << "]." << endl;
exit(1);
}
//
// Print-out limits (all verbose levels)
//
if (true){//}(mSettings->verbose()) {
cout << "Sartre was thrown into the world, restricted by kinematics and user inputs alike:" << endl;
cout << setw(10) << " beta=[" << mLowerLimit[0] << ", " << mUpperLimit[0] << "]" << endl;
cout << setw(10) << "Q2=[" << mLowerLimit[1] << ", " << mUpperLimit[1] << "]" << endl;
cout << setw(10) << " W=[" << sqrt(mLowerLimit[2]) << ", " << sqrt(mUpperLimit[2]) << "]" << endl;
cout << setw(10) << " z=[" << mLowerLimit[3] << ", " << mUpperLimit[3] << "]" << endl;
cout << setw(10) << " MX2=[" << kineMX2min << ", " << kineMX2max << "]" << endl;
cout << setw(10) << " y=[" << kineYmin << ", 1" << "]" << endl;
}
//
// UNU.RAN needs the domain (boundaries) and the mode.
// The domain is already defined, here we find the mode, which is tricky.
// The max. cross-section is clearly at the domain boundary in Q2=Q2min.
// The position in W2 and beta is not obvious. The mode should be at z=0.5.
// The approach here is to use the BrentMinimizer1D that
// performs first a scan a then a Brent fit.
//
mCrossSection->setCheckKinematics(false);
double theMode[4];
//
// Find the mode.
// Assume that the mode is at W2=W2max, Q2=Q2min, z=0.5
// Start with beta=0.5 => MX2=Q2
// It is kinemtically easier to use MX2 instead of beta here.
//
if (mSettings->verbose()) cout << "Finding mode of pdf:" << endl;
theMode[1] = mLowerLimit[1]; // Q2
theMode[2] = mUpperLimit[2]; // W2
theMode[3] = 0.5; // z
double MX2min=mLowerLimit[1]*(1-mUpperLimit[0])/mUpperLimit[0];
MX2min=max(kineMX2min, MX2min);
double MX2max=mLowerLimit[1]*(1-mLowerLimit[0])/mLowerLimit[0];
MX2max=min(kineMX2max, MX2max);
InclusiveDiffractionModeFinderFunctor modeFunctor(mCrossSection, theMode[1], theMode[2], theMode[3], MX2min, MX2max);
ROOT::Math::BrentMinimizer1D minimizer;
minimizer.SetFunction(modeFunctor, MX2min, MX2max);
minimizer.SetNpx(static_cast<int>(mUpperLimit[0]-mLowerLimit[0]));
ok = minimizer.Minimize(100000, 0, 1.e-8);
if (! ok) {
cout << "Error, failed to find mode of pdf." << endl;
exit(1);
}
theMode[0] = minimizer.XMinimum(); // Mx2
double MX2=theMode[0];
theMode[0]=theMode[1]/(theMode[1]+MX2); //Mx2->beta
double crossSectionAtMode = (*mCrossSection)(MX2, theMode[1], theMode[2], theMode[3]);
if (mSettings->verbose()) {
cout << "\tlocation: beta=" << theMode[0] << ", Q2=" << theMode[1] << ", W=" << sqrt(theMode[2]) << ", z=" << theMode[3];
cout << "; value: " << crossSectionAtMode << endl;
}
mCrossSection->setCheckKinematics(true);
// domain and mode for Q2 -> log(Q2)
mLowerLimit[1] = log(mLowerLimit[1]);
mUpperLimit[1] = log(mUpperLimit[1]);
theMode[1] = log(theMode[1]);
if (mPDF_Functor) delete mPDF_Functor;
if (mPDF) delete mPDF;
mPDF_Functor = new ROOT::Math::Functor(mCrossSection, &InclusiveDiffractiveCrossSections::unuranPDF, 4);
mPDF = new TUnuranMultiContDist(*mPDF_Functor, true); // last arg = pdf in log or not
mPDF->SetDomain(mLowerLimit, mUpperLimit);
mPDF->SetMode(theMode);
if (mUnuran) delete mUnuran;
mUnuran = new TUnuran;
mCrossSection->setCheckKinematics(false); // avoid numeric glitch in Init()
mUnuran->Init(*mPDF, "method=hitro");
mCrossSection->setCheckKinematics(true);
mUnuran->SetSeed(mSettings->seed());
//
// Burn in generator
//
double xrandom[4];
for (int i=0; i<100; i++) {
mUnuran->SampleMulti(xrandom);
// cout<<"QQ: beta="<<xrandom[0];
// cout<<" Q2="<<exp(xrandom[1]);
// cout<<" W="<<sqrt(xrandom[2]);
// cout<<" z="<<xrandom[3]<<endl;
}
//
// Set up QQG unu.ran:
//
//The mode is simpler for QQG, as it is at minimum beta
//The limits are the same, except for z which is a different quantity in QQG.
mLowerLimit_qqg[0]=mLowerLimit[0]; //beta
mLowerLimit_qqg[1]=mLowerLimit[1]; //log(Q2)
mLowerLimit_qqg[2]=mLowerLimit[2]; //W2
//z>beta*(1+4*mf*mf/Q2);
mLowerLimit_qqg[3]=mLowerLimit[0] * (1+4*mf*mf/exp(mUpperLimit[1]))+1e-10;
mUpperLimit_qqg[0]=mUpperLimit[0]; //beta
mUpperLimit_qqg[1]=mUpperLimit[1]; //log(Q2)
mUpperLimit_qqg[2]=mUpperLimit[2]; //W2
mUpperLimit_qqg[3]=mUpperLimit[3]; //z
double theMode_qqg[4];
theMode_qqg[0]=mLowerLimit_qqg[0]; //beta
theMode_qqg[1]=mLowerLimit_qqg[1]; //log(Q2)
theMode_qqg[2]=mUpperLimit_qqg[2]; //W2
theMode_qqg[3]=theMode_qqg[0]*(1+4*mf*mf/exp(theMode_qqg[1]))+1e-10; //z
if (mPDF_Functor_qqg) delete mPDF_Functor_qqg;
if (mPDF_qqg) delete mPDF_qqg;
mPDF_Functor_qqg = new ROOT::Math::Functor(mCrossSection, &InclusiveDiffractiveCrossSections::unuranPDF_qqg, 4);
mPDF_qqg = new TUnuranMultiContDist(*mPDF_Functor_qqg, true); // last arg = pdf in log or not
//Rescale the domain in z (and fix it in the pdf)
mPDF_qqg->SetDomain(mLowerLimit_qqg, mUpperLimit_qqg);
mPDF_qqg->SetMode(theMode_qqg);
mCrossSection->setCheckKinematics(false);
if (mUnuran_qqg) delete mUnuran_qqg;
mUnuran_qqg = new TUnuran;
mUnuran_qqg->Init(*mPDF_qqg, "method=hitro");
mUnuran_qqg->SetSeed(mSettings->seed());
// mCrossSection->setCheckKinematics(true);
//
// Burn in QQG generator
//
for (int i=0; i<100; i++) {
mUnuran_qqg->SampleMulti(xrandom);
// cout<<"QQG: beta="<<xrandom[0];
// cout<<" Q2="<<exp(xrandom[1]);
// cout<<" W="<<sqrt(xrandom[2]);
// cout<<" z="<<xrandom[3]<<endl;
}
mEventCounter = 0;
mTriesCounter = 0;
mIsInitialized = true;
cout << "Sartre for InclusiveDiffraction is initialized." << endl << endl;
//
// Calculate total cross section in given
// kinematic limits. This will be used to
// decide which type of event to generate,
// q-qbar or q-qbar-g
//
cout<<"Calculating cross sections over the given kinematic range..."<<endl;
cout<<"(This may take while, use your radical freedom and do other things.)"<<endl;
mCrossSection->setFockState(QQ);
mTotalCrossSection=0;
mTotalCrossSection_qq=7.4543e+08; //totalCrossSection();
cout<<" The QQ cross section is: "<<mTotalCrossSection_qq<<" nb. (1/2)"<<endl;
mCrossSection->setFockState(QQG);
mTotalCrossSection=0;
mTotalCrossSection_qqg=3.79917e+08; //totalCrossSection();
cout<<"The QQG cross section is: "<<mTotalCrossSection_qqg<<" nb. (2/2)"<<endl;
cout<<"Total Cross Section: "<<mTotalCrossSection_qqg+mTotalCrossSection_qq<<" nb"<<endl;
mTotalCrossSection=mTotalCrossSection_qqg+mTotalCrossSection_qq;
return true;
}
double SartreInclusiveDiffraction::cross_section_wrapper(double* xx, double* par){
double arg[4]={xx[0], xx[1], xx[2], par[0]};
double result=mCrossSection->unuranPDF(arg);
return -result;
}
bool SartreInclusiveDiffraction::init(const string& str) // overloaded version of init()
{
if (str.empty())
return init();
else
return init(str.c_str());
}
vector<pair<double,double> > SartreInclusiveDiffraction::kinematicLimits()
{
vector<pair<double,double> > array;
array.push_back(make_pair(mLowerLimit[0], mUpperLimit[0])); // t
array.push_back(make_pair(exp(mLowerLimit[1]), exp(mUpperLimit[1]))); // Q2 or xpom
if (!mSettings->UPC())
array.push_back(make_pair(sqrt(mLowerLimit[2]), sqrt(mUpperLimit[2]))); // W
return array;
}
Event* SartreInclusiveDiffraction::generateEvent()
{
if (!mIsInitialized) {
cout << "SartreInclusiveDiffraction::generateEvent(): Error, Sartre is not initialized yet." << endl;
cout << " Call init() before trying to generate events." << endl;
return 0;
}
//
// Decide which Fock-state to generate:
//
bool isQQ=false;
if(mRandom->Uniform(mTotalCrossSection) <= mTotalCrossSection_qq)
isQQ=true;
//
// Generate one event
//
double xrandom[4];
while (true) {
mTriesCounter++;
delete mCurrentEvent;
mCurrentEvent = new Event;
//
// xrandom[i]
// i=0: beta
// i=1: Q2
// i=2: W2
// i=3: z
//
if(isQQ)
mUnuran->SampleMulti(xrandom);
else
mUnuran_qqg->SampleMulti(xrandom);
xrandom[1] = exp(xrandom[1]); // log(Q2) -> Q2
double MX2=xrandom[1]*(1-xrandom[0])/xrandom[0];
bool isValidEvent;
isValidEvent = Kinematics::valid( mS, xrandom[0], xrandom[1], xrandom[2], xrandom[3], sqrt(MX2), true, (mSettings->verboseLevel() > 1));
if (!isValidEvent) {
if (mSettings->verboseLevel() > 2)
cout << "SartreInclusiveDiffraction::generateEvent(): event rejected, not within kinematic limits" << endl;
continue;
}
//
// Fill beam particles in Event structure
// Kinematics for eA is reported as 'per nucleon'
//
mCurrentEvent->eventNumber = mEventCounter;
mCurrentEvent->beta = xrandom[0]; // beta
mCurrentEvent->Q2 = xrandom[1]; // Q2
mCurrentEvent->x = Kinematics::x(xrandom[1], xrandom[2]); // x
mCurrentEvent->xpom=mCurrentEvent->x/xrandom[0]; //xpom=x/beta
mCurrentEvent->y = Kinematics::y(xrandom[1], mCurrentEvent->x, mS); // y
mCurrentEvent->s = mS; // s
mCurrentEvent->W = sqrt(xrandom[2]);
mCurrentEvent->z = xrandom[3];
mCurrentEvent->MX = sqrt(MX2);
if(isQQ)
mCurrentEvent->polarization = mCrossSection->polarizationOfLastCall();
else
mCurrentEvent->polarization = transverse;
// mCurrentEvent->diffractiveMode = mCrossSection->diffractiveModeOfLastCall();
mCurrentEvent->quarkSpecies = mCrossSection->quarkSpeciesOfLastCall();
if(isQQ)
mCurrentEvent->crossSectionRatioLT=mCrossSection->crossSectionRatioLTOfLastCall();
else
mCurrentEvent->crossSectionRatioLT=0;
//
// For generating final state azimuthal decorelation
// we need to calculate Omega(b, r).
// For this we need to generate b and r values.
//
// Start with b (written by Bhakta Naik and TT):
// From the thickness function (a leading twist approximation)
//
double bmin = 0;
double bmax = 3.5 * mCrossSection->dipoleModel()->nucleus()->radius();
TF1* fbDistribution = new TF1("fbDistribution",this ,&SartreInclusiveDiffraction::bDistribution, bmin, bmax, 0);
double bGenerated = fbDistribution->GetRandom()*hbarc; // fm
//
// Now generate r
//
double mf = mCrossSection->quarkMassOfLastCall();
double rmin=1e-3;
double rmax=3.5 * mCrossSection->dipoleModel()->nucleus()->radius();
double rGenerated=0;
if(isQQ)
{
TF1* frDistribution_qq;
if(mCurrentEvent->polarization == transverse )
{
frDistribution_qq = new TF1("frDistribution_qq", this, &SartreInclusiveDiffraction::rDistribution_qqT, rmin, rmax, 6);
}
else
{
frDistribution_qq = new TF1("frDistribution_qq", this, &SartreInclusiveDiffraction::rDistribution_qqL, rmin, rmax, 6);
}
frDistribution_qq->SetParameters(mCurrentEvent->beta, mCurrentEvent->xpom, mCurrentEvent->z, mCurrentEvent->Q2, bGenerated, mf);
rGenerated = frDistribution_qq->GetRandom();
delete frDistribution_qq;
}
else{ //QQG
// Create a TF2 object.
// The range for 'r' is from 0 to max_r,
// and for 'kappa' is from 0 to Q2.
// The parameter array for TF2 is defined as:
// {Q2, xpom, z, b_rand_value}
//
TF2* frDistribution_qqg = new TF2("frDistribution_qqg", this,&SartreInclusiveDiffraction::rDistribution_qqg, rmin, rmax, 0, mCurrentEvent->Q2, 4);
frDistribution_qqg->SetParameters(mCurrentEvent->xpom, mCurrentEvent->z, bGenerated, sqrt(mCurrentEvent->Q2));
double kGenerated;
frDistribution_qqg->GetRandom2(rGenerated, kGenerated);
delete frDistribution_qqg;
}
//Calculate Omega:
double dsigmadb2=0;
if (mA == 1) {
dsigmadb2=mCrossSection->dipoleModel()->dsigmadb2ep(rGenerated, bGenerated, mCurrentEvent->xpom);
}
else {
dsigmadb2=mCrossSection->dipoleModel()->coherentDsigmadb2(rGenerated, bGenerated, mCurrentEvent->xpom);
}
mCurrentEvent->Omega=-2.*log(1-dsigmadb2/2.);
// Calculate the saturation scale following
// H. Kowalski, T. Lappi and R. Venugopalan, Phys. Rev. Lett. 100 (2008) 022303 [arXiv:0705.3047 [hep-ph]]
double rS;
for(rS=0.1; rS<20; rS+=0.001){
double dsigmadb2=0;
if (mA == 1) {
dsigmadb2=mCrossSection->dipoleModel()->dsigmadb2ep(rS, bGenerated, mCurrentEvent->xpom);
}
else {
dsigmadb2=mCrossSection->dipoleModel()->coherentDsigmadb2(rS, bGenerated, mCurrentEvent->xpom);
}
if(dsigmadb2 >= 0.44239843) break;
}
mCurrentEvent->QS=1./rS*hbarc; //GeV
- double QS=1./rS*hbarc;
Particle eIn, hIn;
eIn.index = 0;
eIn.pdgId = 11; // e-
eIn.status = 1;
eIn.p = mElectronBeam;
hIn.index = 1;
hIn.pdgId = mNucleus->pdgID();
hIn.status = 1;
hIn.p = mHadronBeam;
mCurrentEvent->particles.push_back(eIn);
mCurrentEvent->particles.push_back(hIn);
//
// Generate the final state particles
//
bool ok;
if (isQQ) {
ok = mFinalStateGenerator.generate(mA, mCurrentEvent, QQ);
}
else {
ok = mFinalStateGenerator.generate(mA, mCurrentEvent, QQG);
}
if (!ok) {
if (mSettings->verboseLevel() > 1) cout << "SartreInclusiveDiffraction::generateEvent(): failed to generate final state" << endl;
continue;
}
break;
}
mEventCounter++;
//
// Nuclear breakup
//
// If the event is incoherent the final state generator does produce a
// 'virtual' proton with m > m_p which is used in Nucleus to calculate
// the excitation energy and the boost.
//
int indexOfScatteredHadron = 6;
bool allowBreakup = mSettings->enableNuclearBreakup();
if (mA == 1) allowBreakup = false;
if (mNucleus) mNucleus->resetBreakup(); // clear previous event in any case
if (allowBreakup && mCurrentEvent->diffractiveMode == incoherent && mNucleus) {
int nFragments = mNucleus->breakup(mCurrentEvent->particles[indexOfScatteredHadron].p);
//
// Merge the list of products into the event list.
// We loose some information here. The user can always go back to
// the nucleus and check the decay products for more details.
// In the original list the energy is per nuclei, here we transform it
// to per nucleon to stay consistent with Sartre conventions.
//
const vector<BreakupProduct>& products = mNucleus->breakupProducts();
for (int i=0; i<nFragments; i++) {
Particle fragment;
fragment.index = mCurrentEvent->particles.size();
fragment.pdgId = products[i].pdgId;
fragment.status = 1;
fragment.p = products[i].p*(1/static_cast<double>(products[i].A));
fragment.parents.push_back(indexOfScatteredHadron);
mCurrentEvent->particles.push_back(fragment);
}
}
return mCurrentEvent;
}
double SartreInclusiveDiffraction::totalCrossSection()
{
if (mTotalCrossSection == 0) {
//
// Limits of integration in beta, Q2, W2, z
//
double xmin[4];
double xmax[4];
copy(mLowerLimit, mLowerLimit+4, xmin);
copy(mUpperLimit, mUpperLimit+4, xmax);
//
// At this point mLowerLimit[1] and mUpperLimit[1]
// are in log(Q2)
//
xmin[1] = exp(xmin[1]); // log Q2 limit
xmax[1] = exp(xmax[1]); // log Q2 limit
mTotalCrossSection = calculateTotalCrossSection(xmin, xmax);
}
return mTotalCrossSection;
}
double SartreInclusiveDiffraction::totalCrossSection(double lower[4], double upper[4]) // beta, Q2, W, z
{
lower[2] *= lower[2]; upper[2] *= upper[2]; // W -> W2
double result = calculateTotalCrossSection(lower, upper);
return result;
}
double SartreInclusiveDiffraction::integrationVolume(){
//
// Limits of integration in beta, Q2, W2, z
//
double xmin[4];
double xmax[4];
copy(mLowerLimit, mLowerLimit+4, xmin);
copy(mUpperLimit, mUpperLimit+4, xmax);
//
// At this point mLowerLimit[1] and mUpperLimit[1]
// are in log(Q2)
//
xmin[1] = exp(xmin[1]); // log Q2 limit
xmax[1] = exp(xmax[1]); // log Q2 limit
double volume = (xmax[0]-xmin[0]) * (xmax[1]-xmin[1]) * (xmax[2]-xmin[2]) * (xmax[3]-xmin[3]);
return volume; //GeV4
}
EventGeneratorSettings* SartreInclusiveDiffraction::runSettings()
{
return EventGeneratorSettings::instance();
}
double SartreInclusiveDiffraction::crossSectionsClassWrapper(const double* var){
return (*mCrossSection)(var);
}
double SartreInclusiveDiffraction::calculateTotalCrossSection(double lower[4], double upper[4])
{
double result = 0;
if (!mIsInitialized) {
cout << "SartreInclusiveDiffraction::calculateTotalCrossSection(): Error, Sartre is not initialized yet." << endl;
cout << " Call init() before trying to generate events." << endl;
return result;
}
//
// Calculate integral using adaptive numerical method
//
// Options: ADAPTIVE, kVEGAS, kPLAIN, kMISER
// no abs tolerance given -> relative only
const double precision = 5e-4;
// unsigned int numberofcalls = 1000000;
unsigned int numberofcalls = 0;//1e5;
ROOT::Math::Functor wfL(this, &SartreInclusiveDiffraction::crossSectionsClassWrapper, 4);
ROOT::Math::IntegratorMultiDim ig(ROOT::Math::IntegrationMultiDim::kADAPTIVE, 0, precision, numberofcalls);
ig.SetFunction(wfL);
result = ig.Integral(lower, upper);
//
// If it fails we switch to a MC integration which is usually more robust
// although not as accurate. This should happen very rarely if at all.
//
if (result <= numeric_limits<float>::epsilon()) {
cout << "SartreInclusiveDiffraction::calculateTotalCrossSection(): warning, adaptive integration failed - switching to VEGAS method." << endl;
ROOT::Math::IntegratorMultiDim igAlt(ROOT::Math::IntegrationMultiDim::kVEGAS);
igAlt.SetFunction(wfL);
igAlt.SetRelTolerance(precision);
igAlt.SetAbsTolerance(0);
result = igAlt.Integral(lower, upper);
}
return result;
}
double SartreInclusiveDiffraction::bDistribution(double* var, double*)
{
double b = *var;
double result=0;
if(mA==1){
double BG = mCrossSection->dipoleModel()->getParameters()->BG();
double arg = b*b/(2*BG);
arg /= hbarc2;
result = 1/(2*M_PI*BG) * exp(-arg);
}
else{
result = mNucleus->T(b);
}
return result;
}
// Three functions for randomly choosing a value of r:
double SartreInclusiveDiffraction::rDistribution_qqT(double* var, double* par)
{
double result = mCrossSection->uiAmplitude_1(var, par);
if(isnan(result)) return 0;
return result * result;
}
double SartreInclusiveDiffraction::rDistribution_qqL(double* var, double* par)
{
double result = mCrossSection->uiAmplitude_0(var, par);
if(isnan(result)) return 0;
return result * result;
}
double SartreInclusiveDiffraction::rDistribution_qqg(double* var, double* par)
{
double r = var[0]; //fm
double k = var[1]; //GeV
if(r>100.) return 0; //IntegralUp sometimes tries too large values that break the dipole model
double xpom=par[0];
double z=par[1];
double b=par[2];
double Q2 = par[3];
double para[4] = {xpom, z, k , b};
double result = mCrossSection->uiAmplitude_qqg(&r, para);
return pow(k, 4) * log(Q2 / (k * k)) * result * result ;
}
const FrangibleNucleus* SartreInclusiveDiffraction::nucleus() const {return mNucleus;}
void SartreInclusiveDiffraction::listStatus(ostream& os) const
{
os << "Event summary: " << mEventCounter<< " events generated, " << mTriesCounter << " tried" << endl;
time_t delta = runTime();
os << "Total time used: " << delta/60 << " min " << delta - 60*(delta/60) << " sec" << endl;
}
time_t SartreInclusiveDiffraction::runTime() const
{
time_t now = time(0);
return now-mStartTime;
}
//==============================================================================
//
// Utility functions and operators (helpers)
//
//==============================================================================
ostream& operator<<(ostream& os, const TLorentzVector& v)
{
os << v.Px() << '\t' << v.Py() << '\t' << v.Pz() << '\t' << v.E() << '\t';
double m2 = v*v;
if (m2 < 0)
os << '(' << -sqrt(-m2) << ')';
else
os << '(' << sqrt(m2) << ')';
return os;
}
Index: trunk/src/InclusiveFinalStateGenerator.cpp
===================================================================
--- trunk/src/InclusiveFinalStateGenerator.cpp (revision 550)
+++ trunk/src/InclusiveFinalStateGenerator.cpp (revision 551)
@@ -1,1117 +1,815 @@
//==============================================================================
// InclusiveFinalStateGenerator.cpp
//
// Copyright (C) 2024 Tobias Toll and Thomas Ullrich
//
// This file is part of Sartre.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation.
// This program is distributed in the hope that it will be useful,
// but without any warranty; without even the implied warranty of
// merchantability or fitness for a particular purpose. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Tobias Toll
// Last update:
// $Date: 2024-06-03 11:27:05 -0400 (Mon, 03 Jun 2024) $
// $Author: ullrich $
//==============================================================================
#include "InclusiveFinalStateGenerator.h"
#include "EventGeneratorSettings.h"
#include "Kinematics.h"
#include "Event.h"
#include "Math/BrentRootFinder.h"
#include "Math/GSLRootFinder.h"
#include "Math/RootFinderAlgorithms.h"
#include "Math/IFunction.h"
#include "Math/SpecFuncMathCore.h"
#include <cmath>
#include <algorithm>
#include <limits>
#include <iomanip>
#include "Math/WrappedTF1.h"
#include "Math/GaussIntegrator.h"
#include "TF1.h"
#include "Enumerations.h"
#include "Constants.h"
#define PR(x) cout << #x << " = " << (x) << endl;
//-------------------------------------------------------------------------------
//
// Implementation of ExclusiveFinalStateGenerator
//
//-------------------------------------------------------------------------------
InclusiveFinalStateGenerator::InclusiveFinalStateGenerator()
{
//
// Setup Pythia8 which is need to hadronize the dipole (and higher Fock states)
// We use a dirty hack here to suppress PYTHIA output by setting the cout failbit
//
cout.setstate(std::ios::failbit);
mPythia = new Pythia8::Pythia("", false); // 2nd arg implies no banner
mPythiaEvent = &(mPythia->event);
mPdt = &(mPythia->particleData);
mPythia->readString("ProcessLevel:all = off"); // needed
mPythia->readString("Check:event = off");
bool ok = mPythia->init();
cout.clear();
if (!ok) {
cout << "Error initializing Pythia8. Stop here." << endl;
exit(1);
}
else {
cout << "Pythia8 initialized (" << PYTHIA_VERSION_INTEGER << ")" << endl;
}
}
InclusiveFinalStateGenerator::~InclusiveFinalStateGenerator()
{
// mPythia->stat();
delete mPythia;
}
-double InclusiveFinalStateGenerator::uiGamma_N(double* var, double* par) const
-{
- double t=var[0];
- double s=par[0];
- double result = pow(t, s-1)*exp(-t);
- return result;
-}
-
-double InclusiveFinalStateGenerator::gamma_N(unsigned int i, double val)
-{
- TF1 fGamma_N("fGamma_N", this, &InclusiveFinalStateGenerator::uiGamma_N, -10, 10, 1);
- fGamma_N.SetParameter(0, i);
- ROOT::Math::WrappedTF1 wfGamma_N(fGamma_N);
- ROOT::Math::GaussIntegrator giGamma_N;
- giGamma_N.SetFunction(wfGamma_N);
- giGamma_N.SetAbsTolerance(0.);
- giGamma_N.SetRelTolerance(1e-5);
-
- int sign=1;
- double low=0, up=val;
- if(val<0){
- low=val;
- up=0;
- sign=-1;
- }
- double Tfact=1;
- for(int j=i-1; j>0; j--){
- Tfact*=j;
- }
- return sign*giGamma_N.Integral(low, up)/Tfact;
-}
-
-bool InclusiveFinalStateGenerator::decayPseudoParticle(TLorentzVector In, TLorentzVector& Out1, TLorentzVector& Out2, double m_1, double m_2, double pt=0){
+bool InclusiveFinalStateGenerator::decayPseudoParticle(TLorentzVector In, TLorentzVector& Out1, TLorentzVector& Out2, double m_1, double m_2, double z, bool isPplus=true){
//
- // Calculate the momenta in the In particle's restframe
+ // Calculates the outgoing 4-momenta Out1 and Out2
+ // with masses m_1 and m_2, when particle In
+ // with four-momentum P and mass M decays.
+ // Particle Out1 has P+ momentum fraction z of In+.
+ // (P+ from proton side, P- from electron)
+ // For P- fractions exchange z->(1-z)
+ //
+ // Formula:
+ // pt2=(\vec p_{\perp 1}-z\vec P_\perp)^2 =
+ // z(1-z)M^2-(1-z)m_1^2-zm_2^2
+ // px=zPx+pt*cos(phi)
+ // py=zPy+pt*sin(phi)
+ // phi is a random angle
//
EventGeneratorSettings *settings = EventGeneratorSettings::instance();
TRandom3 *rndm = settings->randomGenerator();
-
- TVector3 boost=TVector3(In.Px()/In.E(), In.Py()/In.E(), In.Pz()/In.E());
+// if(!isPplus) z=1-z;
double M2=In.M2();
+ double Px=In.Px();
+ double Py=In.Py();
+
double m2_1=m_1*m_1;
double m2_2=m_2*m_2;
- //In the rest-frame:
- double p2=2*m2_1*m2_2 + 2*(m2_1+m2_2)*M2 - m2_1*m2_1 - m2_2*m2_2 - M2*M2;
- p2/=-4*M2;
- if(p2<0) return false;
- double p=sqrt(p2);
- double E_1, E_2, px, py, pz;
- if(pt==0){ //no user preference
- //Generate a direction for decay
- double phi=rndm->Uniform(2*M_PI);
- double cosTheta=rndm->Uniform(2)-1;
- double theta=acos(cosTheta);
-
- px=p*cos(phi)*sin(theta);
- py=p*sin(phi)*sin(theta);
- pz=p*cosTheta;
-
- E_1=sqrt(m2_1+p2);
- E_2=sqrt(m2_2+p2);
+
+ //We want E, px, py, pz:
+ double E, px, py, pz;
+ double pt2;
+ if(isPplus)
+ pt2=z*(1-z)*M2-(1-z)*m2_1-z*m2_2;
+ else
+ pt2=z*(1-z)*M2-z*m2_1-(1-z)*m2_2;
+ if(pt2<0) return false;
+ double phi=rndm->Uniform(2*M_PI);
+ px=z*Px+sqrt(pt2)*cos(phi);
+ py=z*Py+sqrt(pt2)*sin(phi);
+ double p1_plus, p1_minus;
+ if(isPplus){
+ p1_plus=z*(In.E()+In.Pz());
+ p1_minus=(px*px+py*py+m2_1)/p1_plus;
}
- else{ //user defined pt
- double phi=rndm->Uniform(2*M_PI);
- px=pt*cos(phi);
- py=pt*sin(phi);
- pz=sqrt(p2-px*px-py*py);
-
- E_1=sqrt(m2_1+p2);
- E_2=sqrt(m2_2+p2);
+ else{
+ p1_minus=z*(In.E()-In.Pz());
+ p1_plus=(px*px+py*py+m2_1)/p1_minus;
}
- Out1=TLorentzVector(px, py, pz, E_1);
- Out2=TLorentzVector(-px, -py, -pz, E_2);
-
- Out1.Boost(boost);
- Out2.Boost(boost);
+ E=(p1_plus+p1_minus)/2;
+ pz=(p1_plus-p1_minus)/2;
+ Out1=TLorentzVector(px, py, pz, E);
+ Out2=In-Out1;
return true;
}
bool InclusiveFinalStateGenerator::generate(int A, Event *event, FockState fockstate)
{
//
// Get generator settings and the random generator
//
EventGeneratorSettings *settings = EventGeneratorSettings::instance();
TRandom3 *rndm = settings->randomGenerator();
//
// The beam particles must be present in the event list
//
int ePos = -1;
int hPos = -1;
bool parentsOK = true;
if (event->particles.size() == 2) {
if (abs(event->particles[0].pdgId) == 11) {
ePos = 0;
hPos = 1;
}
else if (abs(event->particles[1].pdgId) == 11) {
ePos = 1;
hPos = 0;
}
else
parentsOK = false;
}
else
parentsOK = false;
if (!parentsOK) {
cout << "ExclusiveFinalStateGenerator::generate(): error, no beam particles in event list." << endl;
return false;
}
//
// Store arguments locally
// (Some could also be obtained from the event structure)
//
mA = A;
double xpom=event->xpom;
double beta=event->beta;
mT = Kinematics::tmax(xpom);//event->t;
if (mT > 0) mT = -mT; // ensure t<0
mQ2 = event->Q2;
mY = event->y;
mElectronBeam = event->particles[ePos].p;
mHadronBeam = event->particles[hPos].p;
mMX = event->MX;
mS = Kinematics::s(mElectronBeam, mHadronBeam);
double MX=event->MX;
double MX2=MX*MX;
//
// Constants
//
double const twopi = 2*M_PI;
double const hMass2 = mHadronBeam.M2();
mMY2 = hMass2;
//
// Re-engineer scattered electron
//
// e'=(E', pt', pz') -> 3 unknowns
//
// Three equations:
// 1: me*me=E'*E'-pt'*pt'-pz'*pz'
// 2: Q2=-(e-e')^2=-2*me*me + 2*(E*E'-pz*pz')
// 3: W2=(P+e-e')^2=mp2+2*me2+2*(Ep*E-Pz*pz)-2*(Ep*E'-Pz*pz')-2*(E*E'-pz*pz')
//
double Ee=mElectronBeam.E();
double Pe=mElectronBeam.Pz();
double Ep=mHadronBeam.E();
double Pp=mHadronBeam.Pz();
double W=event->W;
double W2=W*W;
double Q2=event->Q2;
// Take masses from the beams in case they are not actually electrons or protons
double me2=mElectronBeam.M2();
double mp2=mHadronBeam.M2();
//
// What we want for each particle:
//
double E, pz, pt, px, py, phi;
//
// Equations 2 and 3 yield:
//
E = Pe*(W2-mp2-2*Ee*Ep) + (Pp+Pe)*Q2 + 2*Pe*Pe*Pp + 2*me2*Pp;
E /= 2*(Ee*Pp-Ep*Pe);
pz = Ee*(W2-mp2) + (Ep+Ee)*Q2 + 2*Ee*Pe*Pp + 2*Ep*me2 - 2*Ee*Ee*Ep;
pz /= 2*(Ee*Pp-Ep*Pe);
//
// Equation 1:
//
pt = sqrt(E*E-pz*pz-me2);
phi = rndm->Uniform(twopi);
TLorentzVector theScatteredElectron(pt*sin(phi), pt*cos(phi), pz, E);
//
// Re-engineer virtual photon
//
// gamma=E-E'
E=mElectronBeam.E()-theScatteredElectron.E();
pz=mElectronBeam.Pz()-theScatteredElectron.Pz();
px=mElectronBeam.Px()-theScatteredElectron.Px();
py=mElectronBeam.Py()-theScatteredElectron.Py();
TLorentzVector theVirtualPhoton = TLorentzVector(px, py, pz, E);
//
// Re-engineer scattered proton/dissociated proton
//
double Pplus=mHadronBeam.E()+mHadronBeam.Pz();
double PplusPrime=(1-xpom)*Pplus;
double PminusPrime=(mMY2-mT)/PplusPrime;
E=(PplusPrime+PminusPrime)/2;
pz=(PplusPrime-PminusPrime)/2;
double pt_squared = E*E-pz*pz-mMY2;
if (pt_squared < 0) {
if (settings->verbose() && settings->verboseLevel() > 2) {
cout<< "InclusiveFinalStateGenerator::generate(): No phase-space for scattered proton pt." << endl;
cout<< " Abort event generation." << endl;
}
return false;
}
pt = sqrt(pt_squared);
px = pt*cos(phi);
py = pt*sin(phi);
TLorentzVector theScatteredProton(px, py, pz, E);
//
// Use scattered proton to set up four momentum of pomeron:
//
TLorentzVector thePomeron=mHadronBeam-theScatteredProton;
//
// Finally the diffractive final state,
// treat at first as a pseudo particle
//
TLorentzVector theXparticle((mHadronBeam + mElectronBeam) - (theScatteredElectron + theScatteredProton));
//
// The quark and anti quark
//
TLorentzVector theQuark;
TLorentzVector theAntiQuark;
TLorentzVector theGluon; //for QQG
double z=event->z;
double pxq=0., pyq=0., pxqbar=0., pyqbar=0.;
double mf = Settings::quarkMass(event->quarkSpecies);
if(fockstate==QQ){
//
- // Decay the X-particle using the correct pt
+ // Decay the X-particle
//
- double pt2=z*(1-z)*Q2-mf*mf;
- if (4*(mf*mf+pt2) > MX2) {
+ if(!decayPseudoParticle(theXparticle, theQuark, theAntiQuark, mf, mf, z, false)){
if (settings->verboseLevel() > 2) {
- cout << "InclusiveFinalStateGenerator::generate(): 4*(mf*mf+pt2) > MX2, for quark flavour "<<event->quarkSpecies << endl;
- cout << " Abort event generation" << endl;
+ cout<<"InclusiveFinalStateGenerator::generate: Failed to decay X state, QQ."<<endl;
}
return false;
}
- if(!decayPseudoParticle(theXparticle, theQuark, theAntiQuark, mf, mf, sqrt(pt2))){
- if (settings->verboseLevel() > 2) {
- cout << "InclusiveFinalStateGenerator::generate(): No phase-space for quark pt" << endl;
- cout << " Abort event generation" << endl;
- }
- return false;
- }
- if(z<0.5 && theQuark.Pz()<theAntiQuark.Pz()){
- TLorentzVector tmp=theQuark;
- theQuark=theAntiQuark;
- theAntiQuark=tmp;
- }
}
else if(fockstate==QQG){
- //The knowns:
double Mqq2=(z/beta-1)*Q2;
if(Mqq2<4*mf*mf){
if (settings->verboseLevel() > 2) {
cout << "InclusiveFinalStateGenerator::generate(): 4*mf*mf > Mqq2" << endl;
cout << " Abort event generation" << endl;
}
return false;
}
//
// Given by definitions of z and beta from 2206.13161v1 fig. 12.
- // Here pomeron comes from + direction, so we reverse the axis. (xi=beta/z)
- // They make the assumption that the photon does not
- // carry any +momentum, therefore all is momentum fractions of
- // the pomeron +momentum. However, for us, the photon does carry
- // a little bit of +momentum. Therefore we make momentum fractions
- // of theXparticle instead.
- //
- double PXPlus=theXparticle.E()+theXparticle.Pz();
- double PgPlus=(1-z)*PXPlus; //gluon
- double PqPlus=beta*PXPlus; //quark
- double PqbarPlus=(z-beta)*PXPlus; //antiquark
-
+ // Here pomeron comes from + direction, so we reverse the axis.
+ // (xi=beta/z)
//
- // First solve for qqbar as a pseudo particle.
- // Notation: 1:Photon, 2:Pomeron, 3:qqbar, 4:gluon
- // The unknowns pt3, pt4, P3Minus, P4Minus
- // Knowns: P1, P2, P3Plus, P4Plus
- // Once we know the angle between X and g all else solves.
- // However, finding this angle is tricky.
- // We use a numerical bisection method.
- //
- double phi_totg;
- bool found=false;
- // First find the brackets with a rough scan:
- double valold=evaluate(0., theXparticle, z, Mqq2, MX2, PgPlus, 0.);
- double valnew=0;
- double phi_low, phi_high;
- double stepsize=2*M_PI/100.;
- for(double phi=0; phi<2*M_PI; phi+=stepsize){
- valnew=evaluate(phi, theXparticle, z, Mqq2, MX2, PgPlus, 0.);
- if(valold*valnew==0){
- phi_totg=phi;
- cout<<"Found it!"<<endl;
- found=true;
- }
- if(valold*valnew<0){
- phi_high=phi;
- phi_low=phi-stepsize;
- break;
- }
- valold=valnew;
- }
- if(!found){
- if(!bisection(phi_high, phi_low, theXparticle, z, Mqq2, MX2, PgPlus, 1e-5, phi_totg, 0.)){
- // If the bisection fails we do a scan for best value.
- // It could mean that there is no valid solution...
- valold=fabs(evaluate(0., theXparticle, z, Mqq2, MX2, PgPlus, 0.));
- valnew=0;
- double eps=1e-4;
- double stepsize=2*M_PI/100.;
- while(true){
- phi+=stepsize;
- if(phi>=2*M_PI){
- cout<<"no value found"<<endl;
- return false;
- }
- valnew=fabs(evaluate(phi, theXparticle, z, Mqq2, MX2, PgPlus, 0.));
- if(valnew>valold){
- stepsize=-stepsize/10.;
- }
- if(fabs(stepsize)<eps){
- phi_totg=phi;
- break;
- }
- valold=valnew;
- }
- }
- }
- double pt=theXparticle.Pt();
- double b=2*(1-z)*cos(phi_totg)*pt;
- double c=(1-z)*(1-z)*pt*pt+(1-z)*Mqq2-z*(1-z)*MX2;
- double ptg=-b/2*(1-sqrt(1-4*c/b/b));
- double phi_tot=acos(theXparticle.Px()/pt);
- double PgMinus=ptg*ptg/PgPlus;
-
- //Fill the gluon 4-vector:
- E=(PgPlus+PgMinus)/2;
- pz=(PgPlus-PgMinus)/2;
- px=ptg*cos(phi_tot+phi_totg);
- py=ptg*sin(phi_tot+phi_totg);
- theGluon=TLorentzVector(px, py, pz, E);
-
- //Decay the pseudo particle into qqbar,
- //Call the pseudo gluon Y
- //TODO: Check that we get the correct P+ momenta, may have to use the same method again as above...
- //My original method is not working. For now, therefore,
- //just decay the pseudo gluon into the qq-bar without
- //caring about the correct momentum fractions:
- TLorentzVector thePseudoGluon = theXparticle - theGluon;
- decayPseudoParticle(thePseudoGluon, theQuark, theAntiQuark, mf, mf);
-
-// TLorentzVector thePseudoGluon = theXparticle - theGluon;
-// double xi=beta/z;
-// // Angle between q and Y:
-// double phi_qY;
-// found=false;
-// // First find the brackets with a rough scan:
-// valold=evaluate(0., thePseudoGluon, xi, mf*mf, Mqq2, PqPlus, mf*mf);
-// valnew=0;
-// stepsize=2*M_PI/100.;
-// for(double phi=0; phi<2*M_PI; phi+=stepsize){
-// valnew=evaluate(phi, thePseudoGluon, xi, mf*mf, Mqq2, PqPlus, mf*mf);
-// if(valold*valnew==0){
-// phi_qY=phi;
-// cout<<"Found it!"<<endl;
-// found=true;
-// }
-// if(valold*valnew<0){
-// phi_high=phi;
-// phi_low=phi-stepsize;
-// break;
-// }
-// valold=valnew;
-// }
-// if(!found){
-// if(!bisection(phi_high, phi_low, theXparticle, z, Mqq2, MX2, PqPlus, 1e-5, phi_qY, mf*mf)){
-// cout<<"bisection 2 failed"<<endl;
-// // If the bisection fails we do a scan for best value.
-// // It could mean that there is no valid solution...
-// valold=fabs(evaluate(0., thePseudoGluon, xi, mf*mf, Mqq2, PqPlus, mf*mf));
-// valnew=0;
-// double eps=1e-4;
-// double stepsize=2*M_PI/10.;
-// phi=0;
-// double valmin=valold;
-// double phimin=0;
-// double upperBracket=2*M_PI;
-// while(true){
-// phi+=stepsize;
-// valnew=fabs(evaluate(phi, thePseudoGluon, xi, mf*mf, Mqq2, PqPlus, mf*mf));
-// if(valnew<valmin){
-// valmin=valnew;
-// phimin=phi;
-// }
-// if(phi>upperBracket){
-// phi=phimin-2*stepsize;
-// upperBracket=phimin+stepsize;
-// stepsize/=10.;
-// valmin=valnew;
-// }
-// PR(valnew);
-// PR(phi);
-// if(fabs(stepsize)<eps){
-// phi_qY=phi;
-// break;
-// }
-// valold=valnew;
-// }
-// PR(valnew/mf/mf);
-// }
-// }
-// pt=thePseudoGluon.Pt();
-// b=2*(1-xi)*cos(phi_qY)*pt;
-// c=(1-xi)*(1-xi)*pt*pt+mf*mf-xi*(1-xi)*Mqq2;
-// double ptq=-b/2*(1-sqrt(1-4*c/b/b));
-// phi_tot=acos(thePseudoGluon.Px()/pt);
-// double PqMinus=ptq*ptq/PqPlus;
-//
-// //Fill the quark 4-vector:
-// E=(PqPlus+PqMinus)/2;
-// pz=(PqPlus-PqMinus)/2;
-// px=ptq*cos(phi_tot+phi_qY);
-// py=ptq*sin(phi_tot+phi_qY);
-// theQuark=TLorentzVector(px, py, pz, E);
-// theAntiQuark=thePseudoGluon-theQuark;
-
-// double k2=(thePomeron-theGluon).M2();
-// double pt2=xi*(1-xi)*(-k2)-mf*mf;
-// if(!decayPseudoParticle(thePseudoGluon, theQuark, theAntiQuark, mf, mf, sqrt(pt2))){
-// if (settings->verboseLevel() > 2) {
-// cout << "InclusiveFinalStateGenerator::generate(): No phase-space for quark pt" << endl;
-// cout << " Abort event generation" << endl;
-// }
-// return false;
-// }
-/*
-
- phi = rndm->Uniform(twopi);
- pxq=sqrt(pt2)*sin(phi);
- pyq=sqrt(pt2)*cos(phi);
- pxqbar=-pxq;
- pyqbar=-pyq;
- if (4*(mf*mf+pt2) > Mqq2) {
- if (settings->verboseLevel() > 1) {
- cout << "InclusiveFinalStateGenerator::generate(): 4*(mf*mf+pt2) > Mqq2" << endl;
- cout << " Abort event generation" << endl;
+ TLorentzVector thePseudoGluon;
+ if(!decayPseudoParticle(theXparticle, theGluon, thePseudoGluon, 0., sqrt(Mqq2), 1-z)){
+ if (settings->verboseLevel() > 2) {
+ cout<<"InclusiveFinalStateGenerator::generate: Failed to decay X state."<<endl;
}
return false;
}
//
- // Calculate the quark momenta in the X-particle's restframe
+ // Decay the pseudo particle into qqbar
//
- TVector3 boost=TVector3(thePseudoGluon.Px()/thePseudoGluon.E(), thePseudoGluon.Py()/thePseudoGluon.E(), thePseudoGluon.Pz()/thePseudoGluon.E());
- int sign=1.;
- if(xi<0.5) sign=-1.;
- theQuark=TLorentzVector(pxq, pyq, sign*sqrt(Mqq2/4-mf*mf-pt2), sqrt(Mqq2)/2);
- theAntiQuark=TLorentzVector(pxqbar, pyqbar, -sign*sqrt(Mqq2/4-mf*mf-pt2), sqrt(Mqq2)/2);
- theQuark.Boost(boost);
- theAntiQuark.Boost(boost);
-// PR(PpomPlus);
- PR(PgPlus);
- PR((theGluon.E()+theGluon.Pz()));
- PR(PqPlus);
- PR(theQuark.E()+theQuark.Pz());
- PR(PqbarPlus);
- PR(theAntiQuark.E()+theAntiQuark.Pz());
- */
- }
+ double xi=beta/z;
+ if(!decayPseudoParticle(thePseudoGluon, theQuark, theAntiQuark, mf, mf, xi)){
+ if (settings->verboseLevel() > 2) {
+ cout<<"InclusiveFinalStateGenerator::generate: Failed to decay pseudo-gluon."<<endl;
+ }
+ return false;
+ }
+ } //QQG
else{
cout<<"InclusiveFinalStateGenerator::generate: unknown fockstate: "<<fockstate<<" ending program."<<endl;
exit(0);
}
// Generate decorrelation between the qqbar system.
// This we do by deconvoluting the pomeron as 2T gluons
// where T is the twist that we are at. These gluons
// are assumed to have momenta distributed as a Gaussian
// with width Qs around the momentum of the pomeron
// in transverse space.
//
// We will take a random walk while keeping both particles
// on shell, as well as (P1+P1)^2=MX2
//
// 1. First find which twist we are operating at,
//
int Twist=0;
double Omega=event->Omega;
double eps=1e-3;
double sum=0;
for(int n=1; n<100; n++){
int nfactorial=1;
for(int i=1; i<=n; i++){
nfactorial*=i;
}
sum+=pow(-1, n-1)*2/nfactorial*pow(Omega/2, n);
double trueVal=2*(1-exp(-Omega/2));
if(abs(sum-trueVal)<eps){
Twist=n;
break;
}
}
- /*
- Twist=0;
- for(int i=0; i<100; i++){
- double gammaN=gamma_N(i+1, -Omega/2);
- if(fabs(2*exp(-Omega/2)*gammaN)<eps){
- Twist=i+1;
- break;
- }
- }
- PR(Twist);
- exit(1);
- */
//
// 2. Now generate 2*Twist number of gluons:
//
double Qs=event->QS;
vector<double> pom_px, pom_py, pom_pz;
double pom_px_tot=thePomeron.Px();
double pom_py_tot=thePomeron.Py();
double pom_pz_tot=thePomeron.Pz();
for(int i=0; i<2*Twist-1; i++){ //-1 since the last gluon conserves total momentum
//
// Generate momenta from a Gaussian with width Qs
//
double p_x=rndm->Gaus(0., Qs);
double p_y=rndm->Gaus(0., Qs);
double p_z=rndm->Gaus(0., Qs);
//
// Distribute the individual gluon momenta around
// the pomeron momentum (the pomeron is the combined
// particle from all the exchange gluons
//
pom_px.push_back(thePomeron.Px()/(2*Twist)+p_x);
pom_py.push_back(thePomeron.Py()/(2*Twist)+p_y);
pom_pz.push_back(thePomeron.Pz()/(2*Twist)+p_z);
pom_px_tot+=p_x;
pom_py_tot+=p_y;
pom_pz_tot+=p_z;
}
//conserve the total pomeron momentum:
pom_px.push_back(thePomeron.Px()-pom_px_tot);
pom_py.push_back(thePomeron.Py()-pom_py_tot);
pom_pz.push_back(thePomeron.Pz()-pom_pz_tot);
//
// 3. Distribute the recoils to:
// the quark and antiquark (QQ) or
// the quark, antiquarkgluon and gluon (QQG).
// Start with QQ
//
double Eq=0, Eqbar=0, pzq=0, pzqbar=0;
if(fockstate==QQ){
Eq=theQuark.E();
Eqbar=theAntiQuark.E();
pzq=theQuark.Pz();
pzqbar=theAntiQuark.Pz();
pxq=theQuark.Px();
pyq=theQuark.Py();
pxqbar=theAntiQuark.Px();
pyqbar=theAntiQuark.Py();
for(unsigned int i=0; i<pom_px.size(); i++){
//
//First calculate the relative velocity between q, qbar and gluon
//
double pomP=sqrt(pom_px[i]*pom_px[i] + pom_py[i]*pom_py[i] + pom_pz[i]*pom_pz[i]);
double qP=sqrt(pxq*pxq + pyq*pyq + pzq*pzq);
double qbarP=sqrt(pxqbar*pxqbar + pyqbar*pyqbar + pzqbar*pzqbar);
double deltaVq=sqrt(1+qP*qP/Eq/Eq - 2*(pom_px[i]*pxq + pom_py[i]*pyq + pom_pz[i]*pzq) /Eq/pomP);
double deltaVqbar=sqrt(1+qbarP*qbarP/Eqbar/Eqbar - 2*(pom_px[i]*pxqbar + pom_py[i]*pyqbar + pom_pz[i]*pzqbar) / Eqbar/pomP);
//The momentum kick is inversely proportional to relative velocity
double fraction=0.5;
if(deltaVq+deltaVqbar!=0)
fraction=deltaVqbar/(deltaVq+deltaVqbar);
//Update momenta
pxq+=fraction*pom_px[i];
pyq+=fraction*pom_py[i];
pzq+=fraction*pom_pz[i];
pxqbar+=(1-fraction)*pom_px[i];
pyqbar+=(1-fraction)*pom_py[i];
pzqbar+=(1-fraction)*pom_pz[i];
//Scale the transverse momenta in order to preserve MX2,
//First find the scaling factor lambda
double lower_bracket=0, upper_bracket=100;
double lambda=computeScalingFactor(pxq, pyq, pzq, pxqbar, pyqbar, pzqbar, mf, mf, sqrt(MX2), lower_bracket, upper_bracket);
if(lambda<=lower_bracket+1e-5 || lambda>=upper_bracket-1e-5) {
//failed to find the root
if (settings->verboseLevel() > 2) {
cout<<"InclusiveFinalStateGenerator::generate: Failed to scatter the quarks"<<endl;
}
return false;
}
if(lambda==(lower_bracket+upper_bracket)/2)
PR(lambda);
pxq*=lambda;
pyq*=lambda;
pzq*=lambda;
pxqbar*=lambda;
pyqbar*=lambda;
pzqbar*=lambda;
//Put on shell:
Eq=sqrt(pxq*pxq+pyq*pyq+pzq*pzq+mf*mf);
Eqbar=sqrt(pxqbar*pxqbar+pyqbar*pyqbar+pzqbar*pzqbar+mf*mf);
}
//
//Update the 4-vectors:
//
//the Quark:
theQuark=TLorentzVector(pxq, pyq, pzq, Eq);
//the Anti-Quark
theAntiQuark=TLorentzVector(pxqbar, pyqbar, pzqbar, Eqbar);
}
else if(fockstate==QQG){
Eq=theQuark.E();
pzq=theQuark.Pz();
pxq=theQuark.Px();
pyq=theQuark.Py();
Eqbar=theAntiQuark.E();
pzqbar=theAntiQuark.Pz();
pxqbar=theAntiQuark.Px();
pyqbar=theAntiQuark.Py();
double Eg=theGluon.E();
double pzg=theGluon.Pz();
double pxg=theGluon.Px();
double pyg=theGluon.Py();
for(unsigned int i=0; i<pom_px.size(); i++){
//
//First calculate the relative velocity between q, qbar and gluon
//
double pomP=sqrt(pom_px[i]*pom_px[i] + pom_py[i]*pom_py[i] + pom_pz[i]*pom_pz[i]);
double qP=sqrt(pxq*pxq + pyq*pyq + pzq*pzq);
double qbarP=sqrt(pxqbar*pxqbar + pyqbar*pyqbar + pzqbar*pzqbar);
double gP=sqrt(pxg*pxg + pyg*pyg + pzg*pzg);
double deltaVg=sqrt(1+gP*gP/Eg/Eg - 2*(pom_px[i]*pxg + pom_py[i]*pyg + pom_pz[i]*pzg) /Eg/pomP);
//Figure out which dipole the gluon is interacting with,
// g-q or q-qbar:
double p2_gq=(pxq+pxg)*(pxq+pxg) + (pyq+pyg)*(pyq+pyg) + (pzq+pzg)*(pzq+pzg);
double p2_gqbar=(pxqbar+pxg)*(pxqbar+pxg) + (pyqbar+pyg)*(pyqbar+pyg) + (pzqbar+pzg)*(pzqbar+pzg);
//Interaction probability propto dipole size:
bool isGQbar = p2_gq/(p2_gq+p2_gqbar) > rndm->Uniform(1);
if(isGQbar){
//Interacting with the g-qbar dipole:
double deltaVqbar=sqrt(1+qbarP*qbarP/Eqbar/Eqbar - 2*(pom_px[i]*pxqbar + pom_py[i]*pyqbar + pom_pz[i]*pzqbar) / Eqbar/pomP);
//The momentum kick is inversely proportional to relative velocity
double fraction=0.5;
if(deltaVg+deltaVqbar!=0)
fraction=deltaVg/(deltaVg+deltaVqbar);
//Update momenta
pxg+=fraction*pom_px[i];
pyg+=fraction*pom_py[i];
pzg+=fraction*pom_pz[i];
pxqbar+=(1-fraction)*pom_px[i];
pyqbar+=(1-fraction)*pom_py[i];
pzqbar+=(1-fraction)*pom_pz[i];
}
else{
//Interacting with the g-q dipole:
double deltaVq=sqrt(1+qP*qP/Eq/Eq - 2*(pom_px[i]*pxq + pom_py[i]*pyq + pom_pz[i]*pzq) /Eq/pomP);
//The momentum kick is inversely proportional to relative velocity
double fraction=0.5;
if(deltaVg+deltaVq!=0)
fraction=deltaVq/(deltaVq+deltaVg);
//Update momenta
pxg+=fraction*pom_px[i];
pyg+=fraction*pom_py[i];
pzg+=fraction*pom_pz[i];
pxq+=(1-fraction)*pom_px[i];
pyq+=(1-fraction)*pom_py[i];
pzq+=(1-fraction)*pom_pz[i];
}
//Scale the transverse momenta in order to preserve MX2 and Mqq2,
//First find the scaling factor lambda
// start by scaling the quarks:
double Mqq2=(z/beta-1)*Q2;
double lower_bracket=0, upper_bracket=100;
double lambda=computeScalingFactor(pxq, pyq, pzq, pxqbar, pyqbar, pzqbar, mf, mf, sqrt(Mqq2), lower_bracket, upper_bracket);
if(lambda<=lower_bracket+1e-5 || lambda>=upper_bracket-1e-5) {
if (settings->verboseLevel() > 2) {
cout<<"InclusiveFinalStateGenerator::generate: Failed to scatter the gluons"<<endl;
}
return false;
}
pxq*=lambda;
pyq*=lambda;
pzq*=lambda;
pxqbar*=lambda;
pyqbar*=lambda;
pzqbar*=lambda;
//Put on shell:
Eq=sqrt(pxq*pxq+pyq*pyq+pzq*pzq+mf*mf);
Eqbar=sqrt(pxqbar*pxqbar+pyqbar*pyqbar+pzqbar*pzqbar+mf*mf);
//
// Create the pseudo gluon based on the current quarks
//
theQuark=TLorentzVector(pxq, pyq, pzq, Eq);
theAntiQuark=TLorentzVector(pxqbar, pyqbar, pzqbar, Eqbar);
TLorentzVector thePseudoGluon=theQuark+theAntiQuark;
double pxpg=thePseudoGluon.Px();
double pypg=thePseudoGluon.Py();
double pzpg=thePseudoGluon.Pz();
double Mpg=thePseudoGluon.M();
// Calculate momentum fractions for later:
double pxFraction=pxq/pxpg;
double pyFraction=pyq/pypg;
double pzFraction=pzq/pzpg;
lower_bracket=0; upper_bracket=100;
lambda=computeScalingFactor(pxpg, pypg, pzpg, pxg, pyg, pzg, Mpg, 0, sqrt(MX2), lower_bracket, upper_bracket);
if(lambda<=lower_bracket+1e-5 || lambda>=upper_bracket-1e-5) {
if (settings->verboseLevel() > 2) {
cout<<"InclusiveFinalStateGenerator::generate: Failed to scatter the quarks"<<endl;
}
return false;
}
if(lambda==(lower_bracket+upper_bracket)/2)
pxpg*=lambda;
pypg*=lambda;
pzpg*=lambda;
pxq=pxFraction*pxpg;
pyq=pyFraction*pypg;
pzq=pzFraction*pzpg;
Eq=sqrt(pxq*pxq+pyq*pyq+pzq*pzq+mf*mf);
pxqbar=(1-pxFraction)*pxpg;
pyqbar=(1-pyFraction)*pypg;
pzqbar=(1-pzFraction)*pzpg;
Eqbar=sqrt(pxqbar*pxqbar+pyqbar*pyqbar+pzqbar*pzqbar+mf*mf);
pxg*=lambda;
pyg*=lambda;
pzg*=lambda;
Eg=sqrt(pxg*pxg+pyg*pyg+pzg*pzg);
}
//
//Update the 4-vectors:
//
//the Quark:
theQuark=TLorentzVector(pxq, pyq, pzq, Eq);
//the Anti-Quark
theAntiQuark=TLorentzVector(pxqbar, pyqbar, pzqbar, Eqbar);
//the Gluon:
theGluon=TLorentzVector(pxg, pyg, pzg, Eg);
}
else{
cout<<"InclusiveFinalStateGenerator::generate: unknown fockstate: "<<fockstate<<" ending program."<<endl;
exit(9);
}
//
// Decorrelations ended
//
//
// Add particles to event record
//
double ifock;
if(fockstate==QQ)
ifock=0;
else
ifock=1;
event->particles.resize(2+5+ifock);
unsigned int eOut = 2;
unsigned int gamma = 3;
unsigned int hOut = 4;
unsigned int quark = 5;
unsigned int antiquark = 6;
unsigned int gluon = 7;
// Global indices
event->particles[eOut].index = eOut;
event->particles[gamma].index = gamma;
event->particles[hOut].index = hOut;
event->particles[quark].index = quark;
event->particles[antiquark].index = antiquark;
if(fockstate==QQG)
event->particles[antiquark].index = gluon;
// 4-vectors
event->particles[eOut].p = theScatteredElectron;
event->particles[hOut].p = theScatteredProton;
event->particles[gamma].p = theVirtualPhoton;
event->particles[quark].p = theQuark;
event->particles[antiquark].p = theAntiQuark;
if(fockstate==QQG)
event->particles[gluon].p = theGluon;
// PDG Ids
event->particles[eOut].pdgId = event->particles[ePos].pdgId; // same as incoming
event->particles[hOut].pdgId = event->particles[hPos].pdgId; // same as incoming (breakup happens somewhere else)
event->particles[gamma].pdgId = 22;
event->particles[quark].pdgId = event->quarkSpecies+1;
event->particles[antiquark].pdgId = -event->particles[quark].pdgId;
if(fockstate==QQG)
event->particles[gluon].pdgId = 21;
// status
//
// HepMC conventions (February 2009).
// 0 : an empty entry, with no meaningful information
// 1 : a final-state particle, i.e. a particle that is not decayed further by
// the generator (may also include unstable particles that are to be decayed later);
// 2 : a decayed hadron or tau or mu lepton
// 3 : a documentation entry (not used in PYTHIA);
// 4 : an incoming beam particle;
// 11 - 200 : an intermediate (decayed/branched/...) particle that does not
// fulfill the criteria of status code 2
event->particles[ePos].status = 4;
event->particles[hPos].status = 4;
event->particles[eOut].status = 1;
event->particles[hOut].status = mIsIncoherent ? 2 : 1;
event->particles[gamma].status = 2;
event->particles[quark].status = 2;
event->particles[antiquark].status = 2;
if(fockstate==QQG)
event->particles[gluon].status = 2;
// parents (ignore dipole)
event->particles[eOut].parents.push_back(ePos);
event->particles[gamma].parents.push_back(ePos);
event->particles[hOut].parents.push_back(hPos);
event->particles[quark].parents.push_back(gamma);
event->particles[antiquark].parents.push_back(gamma);
if(fockstate==QQG){
event->particles[gluon].parents.push_back(quark);
event->particles[gluon].parents.push_back(antiquark);
}
// daughters (again ignore dipole)
event->particles[ePos].daughters.push_back(eOut);
event->particles[ePos].daughters.push_back(gamma);
event->particles[gamma].daughters.push_back(quark);
event->particles[gamma].daughters.push_back(antiquark);
event->particles[hPos].daughters.push_back(hOut);
if(fockstate==QQG){
event->particles[quark].daughters.push_back(gluon);
event->particles[antiquark].daughters.push_back(gluon);
}
//fill event structure
double y=mHadronBeam*theVirtualPhoton/(mHadronBeam*mElectronBeam);
W2=(theVirtualPhoton+mHadronBeam).M2();
mQ2=-theVirtualPhoton.M2();
MX2=(theQuark+theAntiQuark).M2();
double Delta=thePomeron.Pt();
event->t=-Delta*Delta;
event->Q2=mQ2;
event->W=sqrt(W2);
event->y=y;
event->MX=sqrt(MX2);
//
// Now the afterburner 'Pythia8' part for hadronization
//
mPythiaEvent->reset();
// Pythia starts with u = 1, Sartre with u = 0
int quarkID = event->quarkSpecies + 1;
int status = 23;
int color = 101; // 101 102 103
if(fockstate==QQ){
mPythiaEvent->append( quarkID, status, color, 0,
theQuark.Px(), theQuark.Py(), theQuark.Pz(),
theQuark.E(), theQuark.M() );
mPythiaEvent->append(-quarkID, status, 0, color,
theAntiQuark.Px(), theAntiQuark.Py(), theAntiQuark.Pz(),
theAntiQuark.E(), theAntiQuark.M());
}
else if(fockstate==QQG){
int acolor=102;
mPythiaEvent->append( quarkID, status, color, 0,
theQuark.Px(), theQuark.Py(), theQuark.Pz(),
theQuark.E(), theQuark.M() );
mPythiaEvent->append(-quarkID, status, 0, acolor,
theAntiQuark.Px(), theAntiQuark.Py(), theAntiQuark.Pz(),
theAntiQuark.E(), theAntiQuark.M());
mPythiaEvent->append(21, status, acolor, color,
theGluon.Px(), theGluon.Py(), theGluon.Pz(),
theGluon.E(), theGluon.M());
}
else{
cout<<"Fockstate is not recognised, ending program."<<endl;
exit(1);
}
//
// Generate event. Abort on failure.
//
cout.setstate(std::ios::failbit); // suppress all Pythia print-out
bool ok = mPythia->next();
cout.clear();
if (!ok || mPythiaEvent->size() == 0) {
if (settings->verbose() && settings->verboseLevel() > 2) {
cout << "InclusiveFinalStateGenerator::generate(): Pythia8 event generation aborted prematurely, owing to error!" << endl;
}
return false;
}
// mPythiaEvent->list(); //Print out the pythia event to the screen
//
// Loop over pythia particles and fill our particle list
//
int offset = 3+ifock; //pythia particles start at i=3(QQ), or i=4(QQG) in pythia list
event->numberOfPythiaParticlesStored = 0;
for (int i = offset; i < mPythiaEvent->size(); i++) {
Particle pypart;
pypart.index = event->particles.size();
pypart.pdgId = mPythiaEvent->at(i).id();
if (mPythiaEvent->at(i).status() < 0)
pypart.status = 2;
else
pypart.status = 1;
pypart.p = TLorentzVector(mPythiaEvent->at(i).px(),
mPythiaEvent->at(i).py(),
mPythiaEvent->at(i).pz(),
mPythiaEvent->at(i).e());
if (mPythiaEvent->at(i).mother1()) pypart.parents.push_back(mPythiaEvent->at(i).mother1()+offset+1);
if (mPythiaEvent->at(i).mother2()) pypart.parents.push_back(mPythiaEvent->at(i).mother2()+offset+1);
if (mPythiaEvent->at(i).daughter1()) pypart.daughters.push_back(mPythiaEvent->at(i).daughter1()+offset+1);
if (mPythiaEvent->at(i).daughter2()) pypart.daughters.push_back(mPythiaEvent->at(i).daughter2()+offset+1);
event->particles.push_back(pypart);
event->numberOfPythiaParticlesStored++;
}
return true;
}
// Function to calculate the scaling factor lambda numerically (from ChatGPT)
double InclusiveFinalStateGenerator::computeScalingFactor(double px1, double py1, double pz1, double px2, double py2, double pz2, double m_1, double m_2, double M, double lambda_min=0, double lambda_max=100) {
// Define the function to compute the total energy after scaling
auto totalEnergy = [&](double lambda) {
double E1_prime = std::sqrt(lambda*lambda*(px1*px1 + py1*py1 + pz1*pz1) + m_1*m_1);
double E2_prime = std::sqrt(lambda*lambda*(px2*px2 + py2*py2 + pz2*pz2) + m_2*m_2);
return E1_prime + E2_prime;
};
// Function to compute the invariant mass condition for a given lambda
auto invariantMassCondition = [&](double lambda) {
double px_sum = lambda * (px1 + px2);
double py_sum = lambda * (py1 + py2);
double pz_sum = lambda * (pz1 + pz2);
double energy_sum = totalEnergy(lambda);
return energy_sum*energy_sum - px_sum*px_sum - py_sum*py_sum - pz_sum*pz_sum - M*M;
};
// Solve for lambda using a numerical method (bisection method for simplicity)
double tolerance = 1e-6;
while (lambda_max - lambda_min > tolerance) {
double lambda_mid = 0.5 * (lambda_min + lambda_max);
if (invariantMassCondition(lambda_mid) > 0)
lambda_max = lambda_mid;
else
lambda_min = lambda_mid;
}
return 0.5 * (lambda_min + lambda_max);
}
-
-// Function that computes f(phi_totg) = (pseudo gluon mass)^2 - Mqq2
-double InclusiveFinalStateGenerator::evaluate(double phi_totg, const TLorentzVector& theXparticle, double z, double Mqq2, double MX2, double PgPlus, double mg2=0) {
- double pt = theXparticle.Pt();
- double b = 2 * (1 - z) * cos(phi_totg) * pt;
- double c = (1 - z) * (1 - z) * pt * pt + (1 - z) * Mqq2 - z * (1 - z) * MX2 + z * mg2;
- double discriminant = 1 - 4 * c / (b * b);
-
- if (discriminant < 0) {
- return 1e9; // Large value if not physical
- }
-
- double ptg = -b / 2 * (1 - sqrt(discriminant));
- double phi_tot = acos(theXparticle.Px() / pt);
-
- double PgMinus = ptg * ptg / PgPlus;
- double E = (PgPlus + PgMinus) / 2;
- double pz = (PgPlus - PgMinus) / 2;
- double px = ptg * cos(phi_tot + phi_totg);
- double py = ptg * sin(phi_tot + phi_totg);
-
- TLorentzVector theGluon(px, py, pz, E);
- TLorentzVector thePseudoGluon = theXparticle - theGluon;
-
- return thePseudoGluon.M2() - Mqq2;
-}
-
-bool InclusiveFinalStateGenerator::bisection(double phi_min, double phi_max, const TLorentzVector& theXparticle, double z, double Mqq2, double MX2, double PgPlus,
- double tol, double& phi_root, double mg2=0) {
-
- double f_min = evaluate(phi_min, theXparticle, z, Mqq2, MX2, PgPlus, mg2);
- double f_max = evaluate(phi_max, theXparticle, z, Mqq2, MX2, PgPlus, mg2);
-
- if (f_min * f_max > 0) {
- // No sign change, cannot use bisection
- return false;
- }
-
- int max_iter = 100;
- for (int iter = 0; iter < max_iter; ++iter) {
- double phi_mid = 0.5 * (phi_min + phi_max);
- double f_mid = evaluate(phi_mid, theXparticle, z, Mqq2, MX2, PgPlus);
-
- if (std::abs(f_mid) < tol) {
- phi_root = phi_mid;
- return true;
- }
-
- if (f_min * f_mid < 0) {
- phi_max = phi_mid;
- f_max = f_mid;
- } else {
- phi_min = phi_mid;
- f_min = f_mid;
- }
- }
-
- // If we get here, didn't converge
- return false;
-}
-
-
Index: trunk/src/InclusiveFinalStateGenerator.h
===================================================================
--- trunk/src/InclusiveFinalStateGenerator.h (revision 550)
+++ trunk/src/InclusiveFinalStateGenerator.h (revision 551)
@@ -1,53 +1,47 @@
//==============================================================================
// ExclusiveFinalStateGenerator.h
//
// Copyright (C) 2024 Tobias Toll and Thomas Ullrich
//
// This file is part of Sartre.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation.
// This program is distributed in the hope that it will be useful,
// but without any warranty; without even the implied warranty of
// merchantability or fitness for a particular purpose. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Author: Tobias Toll
// Last update:
// $Date: 2024-06-03 11:27:05 -0400 (Mon, 03 Jun 2024) $
// $Author: ullrich $
//==============================================================================
#ifndef InclusiveFinalStateGenerator_h
#define InclusiveFinalStateGenerator_h
#include "Pythia8/Pythia.h"
#include "FinalStateGenerator.h"
#include "Constants.h"
#include "Enumerations.h"
class InclusiveFinalStateGenerator : public FinalStateGenerator {
public:
InclusiveFinalStateGenerator();
~InclusiveFinalStateGenerator();
bool generate(int A, Event *event, FockState fockstate);
-
- double uiGamma_N(double* var, double* par) const;
- double gamma_N(unsigned int i, double val);
-
+
private:
Pythia8::Pythia *mPythia;
Pythia8::Event *mPythiaEvent;
Pythia8::ParticleData *mPdt;
double computeScalingFactor(double px1, double py1, double pz1, double px2, double py2, double pz2, double m_1, double m_2, double M, double, double);
- bool decayPseudoParticle(TLorentzVector, TLorentzVector&, TLorentzVector&, double, double, double);
+ bool decayPseudoParticle(TLorentzVector, TLorentzVector&, TLorentzVector&, double, double, double, bool);
- double evaluate(double phi_totg, const TLorentzVector& theXparticle, double z, double Mqq2, double MX2, double PgPlus, double mf2);
- bool bisection(double phi_min, double phi_max, const TLorentzVector& theXparticle, double z, double Mqq2, double MX2, double PgPlus,
- double tol, double& phi_root, double mf2);
};
#endif

File Metadata

Mime Type
application/octet-stream
Expires
Wed, May 14, 10:52 AM (2 d)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5111280
Default Alt Text
(84 KB)

Event Timeline