Page Menu
Home
HEPForge
Search
Configure Global Search
Log In
Files
F7878368
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
50 KB
Subscribers
None
View Options
Index: trunk/src/InclusiveFinalStateGenerator.h
===================================================================
--- trunk/src/InclusiveFinalStateGenerator.h (revision 0)
+++ trunk/src/InclusiveFinalStateGenerator.h (revision 490)
@@ -0,0 +1,45 @@
+//==============================================================================
+// 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: $
+// $Author: ullrich $
+//==============================================================================
+#ifndef InclusiveFinalStateGenerator_h
+#define InclusiveFinalStateGenerator_h
+#include "Pythia8/Pythia.h"
+#include "FinalStateGenerator.h"
+
+class InclusiveFinalStateGenerator : public FinalStateGenerator {
+public:
+ InclusiveFinalStateGenerator();
+ ~InclusiveFinalStateGenerator();
+
+ bool generate(int A, Event *event);
+
+ 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;
+
+
+};
+#endif
Index: trunk/src/InclusiveFinalStateGenerator.cpp
===================================================================
--- trunk/src/InclusiveFinalStateGenerator.cpp (revision 0)
+++ trunk/src/InclusiveFinalStateGenerator.cpp (revision 490)
@@ -0,0 +1,440 @@
+//==============================================================================
+// ExclusiveFinalStateGenerator.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: $
+// $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"
+
+
+#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)
+ //
+ mPythiaEvent = &(mPythia.event);
+ mPdt = &(mPythia.particleData);
+ mPythia.readString("ProcessLevel:all = off"); // needed
+ mPythia.readString("Check:event = off");
+ if (!mPythia.init()) {
+ cout << "Error initializing Pythia8. Stop here." << endl;
+ exit(1);
+ }
+ else {
+ cout << "Pythia8 initialized (" << PYTHIA_VERSION_INTEGER << ")" << endl;
+ }
+}
+
+InclusiveFinalStateGenerator::~InclusiveFinalStateGenerator() {/* no op */}
+
+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::generate(int A, Event *event)
+{
+ //
+ // 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;
+ 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
+ //
+ E=(1-xpom)*mHadronBeam.Pz()*mHadronBeam.Pz()-mT/2.+0.5*hMass2+0.5*mMY2;
+ E=E/mHadronBeam.E();
+ pz = (1-xpom)*mHadronBeam.Pz();
+ pt = sqrt(E*E-pz*pz-mMY2);
+ if(pt!=pt){
+ if(settings->verboseLevel() > 2)
+ cout<<"No phase-space for scattered proton pt"<<endl;
+ return false;
+ }
+ 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
+ //
+ double mf=event->quarkMass;
+
+ double z=event->z;
+ double pt2=z*(1-z)*Q2-mf*mf;
+ if(4*(mf*mf+pt2)/MX2>1) pt2=MX2/4.-mf*mf;
+ //generate angle
+ phi = rndm->Uniform(twopi);
+ double pxq=sqrt(pt2)*sin(phi);
+ double pyq=sqrt(pt2)*cos(phi);
+ double pxqbar=-pxq;
+ double pyqbar=-pyq;
+ double z0=0.5*(1-sqrt(1-4*(mf*mf+pt2)/MX2));
+ if(mElectronBeam.Pz()<0 and z<0.5) z0=1-z0;
+ if(mElectronBeam.Pz()>0 and z>0.5) z0=1-z0;
+
+ double qPplus=z0*(theXparticle.E()+theXparticle.Pz());
+ double qPminus=(1-z0)*(theXparticle.E()-theXparticle.Pz());
+ double qBarPplus=(1-z0)*(theXparticle.E()+theXparticle.Pz());
+ double qBarPminus=z0*(theXparticle.E()-theXparticle.Pz());
+
+ //
+ // Generate decorrelation between the qqbar system.
+ //
+
+ //
+ // First find which twist we are operating at
+ //
+ int Twist=0;
+ double Omega=event->Omega;
+ double eps=1e-3;
+ 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;
+ }
+ }
+ //
+ // Now generate 2*Twist number of gluons:
+ // Do they need a pz as well??
+ //
+ double eps2=z*(1-z)*(Q2+MX2);
+ double r_average=1./sqrt(eps2);
+ double Qs=sqrt(2)*Omega/r_average;
+ 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++){
+ //#TT This part does not seem quite right yet.
+ double p_x=rndm->Gaus(0., Qs);
+ double p_y=rndm->Gaus(0., Qs);
+ double p_z=rndm->Gaus(0., Qs);
+ pom_px.push_back(thePomeron.Px()+p_x);
+ pom_py.push_back(thePomeron.Py()+p_y);
+ pom_pz.push_back(thePomeron.Pz()+p_z);
+ pom_px_tot+=px;
+ pom_py_tot+=py;
+ pom_pz_tot+=pz;
+ }
+ //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);
+ //Distribute the recoils to the quark and antiquark:
+ double Eq=(qPplus+qPminus)/2;
+ double Eqbar=(qBarPplus+qBarPminus)/2;
+ double pzq=(qPplus-qPminus)/2;
+ double pzqbar=(qBarPplus-qBarPminus)/2;
+
+ for(unsigned int i=0; i<pom_px.size(); i++){
+ //first calculate the relative velocity between q, qbar and pomeron
+ 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];
+ pxqbar+=(1-fraction)*pom_px[i];
+ pyqbar+=(1-fraction)*pom_py[i];
+ //Update the energies
+ Eq=sqrt(pxq*pxq+pyq*pyq+pzq*pzq+mf*mf);
+ Eqbar=sqrt(pxqbar*pxqbar+pyqbar*pyqbar+pzqbar*pzqbar+mf*mf);
+ }
+ //Fill the 4-vectors:
+
+ //the Quark:
+ TLorentzVector theQuark(pxq, pyq, pzq, Eq);
+
+ //the Anti-Quark
+ TLorentzVector theAntiQuark(pxqbar, pyqbar, pzqbar, Eqbar);
+
+ theXparticle=theQuark+theAntiQuark;
+
+ //
+ // Add particles to event record
+ //
+ event->particles.resize(2+7);
+ unsigned int eOut = 2;
+ unsigned int gamma = 3;
+ unsigned int X = 4;
+ unsigned int pomeron = 5;
+ unsigned int hOut = 6;
+ unsigned int quark = 7;
+ unsigned int antiquark = 8;
+
+ // Global indices
+ event->particles[eOut].index = eOut;
+ event->particles[gamma].index = gamma;
+ event->particles[X].index = X;
+ event->particles[pomeron].index = pomeron;
+ event->particles[hOut].index = hOut;
+ event->particles[quark].index = quark;
+ event->particles[antiquark].index = antiquark;
+
+ // 4-vectors
+ event->particles[eOut].p = theScatteredElectron;
+ event->particles[hOut].p = theScatteredProton;
+ event->particles[gamma].p = theVirtualPhoton;
+ event->particles[X].p = theXparticle;
+ event->particles[pomeron].p = thePomeron;
+ event->particles[quark].p = theQuark;
+ event->particles[antiquark].p = theAntiQuark;
+
+ // 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[X].pdgId = 90; //pseudoparticle
+ int iquark=event->quarkSpecies;
+ if(iquark<=1)
+ event->particles[pomeron].pdgId = 113;
+ if(iquark==2)
+ event->particles[pomeron].pdgId = 333;
+ if(iquark==3)
+ event->particles[pomeron].pdgId = 443;
+ if(iquark==4)
+ event->particles[pomeron].pdgId = 553;
+ event->particles[quark].pdgId = iquark+1;
+ event->particles[antiquark].pdgId = -event->particles[quark].pdgId;
+
+ // 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[X].status = 90;
+ event->particles[pomeron].status = 2;
+ event->particles[quark].status = 1;
+ event->particles[antiquark].status = 1;
+
+ // 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[hOut].parents.push_back(pomeron);
+ event->particles[pomeron].parents.push_back(gamma);
+ event->particles[pomeron].parents.push_back(gamma);
+ event->particles[X].parents.push_back(gamma);
+ event->particles[quark].parents.push_back(gamma);
+ event->particles[antiquark].parents.push_back(gamma);
+
+ // daughters (again ignore dipole)
+ event->particles[ePos].daughters.push_back(eOut);
+ event->particles[ePos].daughters.push_back(gamma);
+ event->particles[gamma].daughters.push_back(X);
+ event->particles[hPos].daughters.push_back(pomeron);
+ event->particles[gamma].daughters.push_back(quark);
+ event->particles[gamma].daughters.push_back(antiquark);
+ event->particles[pomeron].daughters.push_back(hOut);
+ event->particles[hPos].daughters.push_back(hOut);
+
+ //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);
+
+ return true;
+}
+
Index: trunk/src/SartreInclusiveDiffraction.h
===================================================================
--- trunk/src/SartreInclusiveDiffraction.h (revision 0)
+++ trunk/src/SartreInclusiveDiffraction.h (revision 490)
@@ -0,0 +1,110 @@
+//==============================================================================
+// SartreInclusiveDiffraction.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: $
+// $Author: ullrich $
+//==============================================================================
+#ifndef SartreInclusiveDiffraction_h
+#define SartreInclusiveDiffraction_h
+#include "Event.h"
+#include "EventGeneratorSettings.h"
+#include "InclusiveFinalStateGenerator.h"
+#include "InclusiveDiffractiveCrossSections.h"
+#include "FrangibleNucleus.h"
+#include "Enumerations.h"
+#include "TLorentzVector.h"
+#include "Math/Functor.h"
+#include "TUnuran.h"
+#include <ctime>
+#include <iostream>
+#include <vector>
+
+using namespace std;
+
+class TUnuranMultiContDist;
+
+class SartreInclusiveDiffraction {
+public:
+ SartreInclusiveDiffraction();
+ virtual ~SartreInclusiveDiffraction();
+
+ virtual bool init(const char* = 0);
+ virtual bool init(const string&);
+
+ virtual Event* generateEvent();
+
+ virtual double totalCrossSection(); // in kinematic limits used for generation
+ virtual double totalCrossSection(double lower[4], double upper[4]); // beta, Q2, W, z
+ virtual double integrationVolume();
+ EventGeneratorSettings* runSettings();
+
+ const FrangibleNucleus* nucleus() const;
+
+ void listStatus(ostream& os=cout) const;
+ time_t runTime() const;
+
+ vector<pair<double,double> > kinematicLimits(); // t, Q2, W
+
+private:
+ virtual double calculateTotalCrossSection(double lower[4], double upper[4]); //beta, Q2, W, z
+
+private:
+ SartreInclusiveDiffraction(const SartreInclusiveDiffraction&);
+ SartreInclusiveDiffraction operator=(const SartreInclusiveDiffraction&);
+
+ bool mIsInitialized;
+ time_t mStartTime;
+
+ unsigned long mEvents;
+ unsigned long mTries;
+
+ double mTotalCrossSection;
+
+ TLorentzVector mElectronBeam;
+ TLorentzVector mHadronBeam;
+ double mS;
+ unsigned int mA;
+ int mVmID;
+ DipoleModelType mDipoleModelType;
+ DipoleModelParameterSet mDipoleModelParameterSet;
+
+ Event *mCurrentEvent;
+ FrangibleNucleus *mNucleus;
+ FrangibleNucleus *mUpcNucleus;
+ InclusiveDiffractiveCrossSections *mCrossSection;
+ EventGeneratorSettings *mSettings;
+
+ double mLowerLimit[4]; // beta, Q2, W2, z
+ double mUpperLimit[4]; // beta, Q2, W2, z
+
+ ROOT::Math::Functor *mPDF_Functor;
+ TUnuran *mUnuran;
+ TUnuranMultiContDist *mPDF;
+
+ unsigned long mEventCounter;
+ unsigned long mTriesCounter;
+
+ InclusiveFinalStateGenerator mFinalStateGenerator;
+ double cross_section_wrapper(double*, double*);
+
+};
+
+ostream& operator<<(ostream& os, const TLorentzVector&);
+
+#endif
Property changes on: trunk/src/SartreInclusiveDiffraction.h
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
Index: trunk/src/SartreInclusiveDiffraction.cpp
===================================================================
--- trunk/src/SartreInclusiveDiffraction.cpp (revision 0)
+++ trunk/src/SartreInclusiveDiffraction.cpp (revision 490)
@@ -0,0 +1,761 @@
+//==============================================================================
+// 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: $
+// $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 "SartreInclusiveDiffraction.h"
+#include "ModeFinderFunctor.h"
+#include "Math/BrentMinimizer1D.h"
+#include "Math/IntegratorMultiDim.h"
+#include "Math/GSLMinimizer.h"
+#include "Math/Functor.h"
+#include "TUnuranMultiContDist.h"
+#include <string>
+#include <limits>
+#include <cmath>
+#include <iomanip>
+#include "TH2D.h"
+#include "TFile.h"
+#include "TF3.h"
+
+using namespace std;
+
+#define PR(x) cout << #x << " = " << (x) << endl;
+
+SartreInclusiveDiffraction::SartreInclusiveDiffraction()
+{
+ mIsInitialized = false;
+ mCurrentEvent = 0;
+ mNucleus = 0;
+ mUpcNucleus= 0;
+ mSettings = 0;
+ mPDF_Functor = 0;
+ mPDF = 0;
+ mEventCounter = 0;
+ mTriesCounter = 0;
+ mTotalCrossSection = 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;
+
+ //
+ // 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
+
+ //
+ // 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;
+
+ //
+ // 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 << "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;
+ mCrossSection = new InclusiveDiffractiveCrossSections;
+
+ //
+ // Here we need to put inQ2 > mu02.
+ // The calculation goes haywire for Q2 becoming too small
+ //
+ double mu02 = mCrossSection->dipoleModel()->getParameters()->mu02();
+ double mf = mCrossSection->dipoleModel()->getParameters()->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 and W2,
+ // not beta (for now) and 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);
+ }
+
+ //
+ // 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 kinemtaically 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<1; i++) {
+ mUnuran->SampleMulti(xrandom);
+ }
+
+ mEventCounter = 0;
+ mTriesCounter = 0;
+ mIsInitialized = true;
+ cout << "Sartre for InclusiveDiffraction is initialized." << endl << endl;
+
+ 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;
+ }
+
+ double xrandom[4];
+
+ //
+ // Generate one event
+ //
+ while (true) {
+ mTriesCounter++;
+ delete mCurrentEvent;
+ mCurrentEvent = new Event;
+ //
+ // xrandom[i]
+ // i=0: beta
+ // i=1: Q2
+ // i=2: W2
+ // i=3: z
+ //
+ mUnuran->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);
+ mCurrentEvent->polarization = mCrossSection->polarizationOfLastCall();
+ // mCurrentEvent->diffractiveMode = mCrossSection->diffractiveModeOfLastCall();
+ mCurrentEvent->quarkSpecies = mCrossSection->quarkSpeciesOfLastCall();
+ mCurrentEvent->quarkMass=mCrossSection->quarkMassOfLastCall();
+ mCurrentEvent->crossSection=mCrossSection->crossSectionOfLastCall();
+ double dsigmadb2=0;
+ double z=xrandom[3];
+ double eps2=z*(1-z)*(mCurrentEvent->Q2+MX2);
+ double r_average=1./sqrt(eps2);
+ if(mA==1)
+ dsigmadb2=mCrossSection->dipoleModel()->dsigmadb2ep(r_average*hbarc, 0, mCurrentEvent->xpom);
+ else{
+ dsigmadb2=mCrossSection->dipoleModel()->coherentDsigmadb2(r_average*hbarc, 0, mCurrentEvent->xpom);
+ if(mCurrentEvent->xpom<1e-2){
+ cout<<"==============="<<endl;
+ PR(dsigmadb2);
+ PR(mCrossSection->dipoleModel()->dsigmadb2ep(r_average*hbarc, 0, mCurrentEvent->xpom));
+ PR(mCurrentEvent->xpom);
+ PR(r_average*hbarc);
+ cout<<"==============="<<endl;
+ }
+ }
+ mCurrentEvent->Omega=-2.*log(1-dsigmadb2/2.);
+ 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 (mSettings->UPC()) {
+ // also fills some of the undefined event variables
+ ok = mFinalStateGenerator.generate(mA, mCurrentEvent);
+ }
+ else {
+ ok = mFinalStateGenerator.generate(mA, mCurrentEvent);
+ }
+ 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) or log(xpom).
+ //
+ xmin[1] = exp(xmin[1]); // log Q2 limit -> Q2 limit or equivalent xpom for UPC
+ xmax[1] = exp(xmax[1]); // log Q2 limit -> 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::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 = 1e-1;//5e-4;
+ unsigned int numberofcalls = 10000;//1000000;
+ ROOT::Math::Functor wfL((*mCrossSection), 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;
+}
+
+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;
+}
Property changes on: trunk/src/SartreInclusiveDiffraction.cpp
___________________________________________________________________
Added: svn:executable
## -0,0 +1 ##
+*
\ No newline at end of property
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Nov 19, 6:01 PM (1 d, 17 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3805478
Default Alt Text
(50 KB)
Attached To
rSARTRESVN sartresvn
Event Timeline
Log In to Comment