diff --git a/include/Rivet/AnalysisHandler.hh b/include/Rivet/AnalysisHandler.hh --- a/include/Rivet/AnalysisHandler.hh +++ b/include/Rivet/AnalysisHandler.hh @@ -1,242 +1,242 @@ // -*- C++ -*- #ifndef RIVET_RivetHandler_HH #define RIVET_RivetHandler_HH #include "Rivet/Config/RivetCommon.hh" #include "Rivet/Particle.hh" #include "Rivet/AnalysisLoader.hh" #include "Rivet/Tools/RivetYODA.hh" namespace Rivet { // Forward declaration and smart pointer for Analysis class Analysis; typedef std::shared_ptr<Analysis> AnaHandle; // Needed to make smart pointers compare equivalent in the STL set struct CmpAnaHandle { bool operator() (const AnaHandle& a, const AnaHandle& b) { return a.get() < b.get(); } }; /// A class which handles a number of analysis objects to be applied to /// generated events. An {@link Analysis}' AnalysisHandler is also responsible /// for handling the final writing-out of histograms. class AnalysisHandler { public: /// @name Constructors and destructors. */ //@{ /// Preferred constructor, with optional run name. AnalysisHandler(const string& runname=""); /// @brief Destructor /// The destructor is not virtual, as this class should not be inherited from. ~AnalysisHandler(); //@} private: /// Get a logger object. Log& getLog() const; public: /// @name Run properties //@{ /// Get the name of this run. string runName() const; /// Get the number of events seen. Should only really be used by external /// steering code or analyses in the finalize phase. size_t numEvents() const; /// Get the sum of the event weights seen - the weighted equivalent of the /// number of events. Should only really be used by external steering code /// or analyses in the finalize phase. double sumOfWeights() const; /// Set sum of weights. This is useful if Rivet is steered externally and /// the analyses are run for a sub-contribution of the events /// (but of course have to be normalised to the total sum of weights) void setSumOfWeights(const double& sum); /// Is cross-section information required by at least one child analysis? bool needCrossSection() const; /// Set the cross-section for the process being generated. AnalysisHandler& setCrossSection(double xs); /// Get the cross-section known to the handler. double crossSection() const { return _xs; } /// Whether the handler knows about a cross-section. bool hasCrossSection() const; - /// Set beams for this run + /// Set the beam particles for this run AnalysisHandler& setRunBeams(const ParticlePair& beams) { _beams = beams; MSG_DEBUG("Setting run beams = " << beams << " @ " << sqrtS()/GeV << " GeV"); return *this; } - /// Get beam IDs for this run, usually determined from the first event. - const ParticlePair& beams() const { - return _beams; - } + /// Get the beam particles for this run, usually determined from the first event. + const ParticlePair& beams() const { return _beams; } /// Get beam IDs for this run, usually determined from the first event. + /// @deprecated Use standalone beamIds(ah.beams()), to clean AH interface PdgIdPair beamIds() const; /// Get energy for this run, usually determined from the first event. + /// @deprecated Use standalone sqrtS(ah.beams()), to clean AH interface double sqrtS() const; /// Setter for _ignoreBeams void setIgnoreBeams(bool ignore=true); //@} /// @name Handle analyses //@{ /// Get a list of the currently registered analyses' names. std::vector<std::string> analysisNames() const; /// Get the collection of currently registered analyses. const std::set<AnaHandle, CmpAnaHandle>& analyses() const { return _analyses; } /// Get a registered analysis by name. const AnaHandle analysis(const std::string& analysisname) const; /// Add an analysis to the run list by object AnalysisHandler& addAnalysis(Analysis* analysis); /// @brief Add an analysis to the run list using its name. /// /// The actual Analysis to be used will be obtained via /// AnalysisLoader::getAnalysis(string). If no matching analysis is found, /// no analysis is added (i.e. the null pointer is checked and discarded. AnalysisHandler& addAnalysis(const std::string& analysisname); /// @brief Add analyses to the run list using their names. /// /// The actual {@link Analysis}' to be used will be obtained via /// AnalysisHandler::addAnalysis(string), which in turn uses /// AnalysisLoader::getAnalysis(string). If no matching analysis is found /// for a given name, no analysis is added, but also no error is thrown. AnalysisHandler& addAnalyses(const std::vector<std::string>& analysisnames); /// Remove an analysis from the run list using its name. AnalysisHandler& removeAnalysis(const std::string& analysisname); /// Remove analyses from the run list using their names. AnalysisHandler& removeAnalyses(const std::vector<std::string>& analysisnames); //@} /// @name Main init/execute/finalise //@{ /// Initialize a run, with the run beams taken from the example event. void init(const GenEvent& event); /// @brief Analyze the given \a event by reference. /// /// This function will call the AnalysisBase::analyze() function of all /// included analysis objects. void analyze(const GenEvent& event); /// @brief Analyze the given \a event by pointer. /// /// This function will call the AnalysisBase::analyze() function of all /// included analysis objects, after checking the event pointer validity. void analyze(const GenEvent* event); /// Finalize a run. This function calls the AnalysisBase::finalize() /// functions of all included analysis objects. void finalize(); //@} /// @name Histogram / data object access //@{ /// Get all analyses' plots as a vector of analysis objects. std::vector<AnalysisObjectPtr> getData() const; /// Write all analyses' plots to the named file. void writeData(const std::string& filename) const; //@} private: /// The collection of Analysis objects to be used. set<AnaHandle, CmpAnaHandle> _analyses; /// @name Run properties //@{ /// Run name std::string _runname; /// Number of events seen. /// @todo Replace by a counter unsigned int _numEvents; /// Sum of event weights seen. /// @todo Replace by a counter double _sumOfWeights, _sumOfWeightsSq; /// Cross-section known to AH double _xs, _xserr; /// Beams used by this run. ParticlePair _beams; /// Flag to check if init has been called bool _initialised; /// Flag whether input event beams should be ignored in compatibility check bool _ignoreBeams; //@} private: /// The assignment operator is private and must never be called. /// In fact, it should not even be implemented. AnalysisHandler& operator=(const AnalysisHandler&); /// The copy constructor is private and must never be called. In /// fact, it should not even be implemented. AnalysisHandler(const AnalysisHandler&); }; } #endif diff --git a/include/Rivet/Event.hh b/include/Rivet/Event.hh --- a/include/Rivet/Event.hh +++ b/include/Rivet/Event.hh @@ -1,136 +1,156 @@ // -*- C++ -*- #ifndef RIVET_Event_HH #define RIVET_Event_HH #include "Rivet/Config/RivetCommon.hh" +#include "Rivet/Particle.hh" #include "Rivet/Projection.hh" +#include "Rivet/Math/Vector3.hh" +#include "Rivet/Math/LorentzTrans.hh" namespace Rivet { /// Rivet wrapper for HepMC event and Projection references. /// - /// Event is a concrete class representing an generated event in - /// Rivet. It is constructed given a HepMC::GenEvent, a pointer to - /// which is kept by the Event object throughout its lifetime. The user - /// must therefore make sure that the corresponding HepMC::GenEvent - /// will persist at least as long as the Event object. + /// Event is a concrete class representing an generated event in Rivet. It is + /// constructed given a HepMC::GenEvent, a pointer to which is kept by the + /// Event object throughout its lifetime. The user must therefore make sure + /// that the corresponding HepMC::GenEvent will persist at least as long as + /// the Event object. /// - /// In addition to the HepMC::GenEvent object the Event also keeps - /// track of all Projections object which have been applied to the - /// Event so far. + /// In addition to the HepMC::GenEvent object the Event also keeps track of + /// all Projection objects which have been applied to the Event so far. class Event { public: - /// @name Standard constructors and destructors. + /// @name Constructors and destructors. //@{ - /// Constructor from a HepMC GenEvent reference - Event(const GenEvent& ge) - : _originalGenEvent(&ge), _genEvent(ge) - { _init(ge); } - /// Constructor from a HepMC GenEvent pointer Event(const GenEvent* ge) : _originalGenEvent(ge), _genEvent(*ge) { assert(ge); _init(*ge); } + /// Constructor from a HepMC GenEvent reference + /// @deprecated HepMC uses pointers, so we should talk to HepMC via pointers + Event(const GenEvent& ge) + : _originalGenEvent(&ge), _genEvent(ge) + { _init(ge); } + /// Copy constructor Event(const Event& e) : _originalGenEvent(e._originalGenEvent), _genEvent(e._genEvent) { } //@} - public: + /// @name Major event properties + //@{ + + // /// Get the beam particles + // ParticlePair beams() const; + + // /// Get the beam centre-of-mass energy + // double sqrtS() const; + + // /// Get the beam centre-of-mass energy per nucleon + // double asqrtS() const; + + // /// Get the boost to the beam centre-of-mass + // Vector3 beamCMSBoost() const; + + // /// Get the boost to the beam centre-of-mass + // LorentzTransform beamCMSTransform(); /// The generated event obtained from an external event generator - const GenEvent* genEvent() const { - return &_genEvent; - } + const GenEvent* genEvent() const { return &_genEvent; } /// @brief The generation weight associated with the event /// /// @todo This needs to be revisited when we finally add the mechanism to /// support NLO counter-events and weight vectors. double weight() const { return (!_genEvent.weights().empty()) ? _genEvent.weights()[0] : 1.0; } + //@} - public: + + /// @name Projection running + //@{ /// @brief Add a projection @a p to this Event. /// /// If an equivalent Projection has been applied before, the /// Projection::project(const Event&) of @a p is not called and a reference /// to the previous equivalent projection is returned. If no previous /// Projection was found, the Projection::project(const Event&) of @a p is /// called and a reference to @a p is returned. template <typename PROJ> const PROJ& applyProjection(PROJ& p) const { const Projection* cpp(&p); std::set<const Projection*>::const_iterator old = _projections.find(cpp); if (old != _projections.end()) { const Projection& pRef = **old; return pcast<PROJ>(pRef); } // Add the projection via the Projection base class (only // possible because Event is a friend of Projection) Projection* pp = const_cast<Projection*>(cpp); pp->project(*this); _projections.insert(pp); return p; } - + /// @brief Add a projection @a p to this Event by pointer. template <typename PROJ> const PROJ& applyProjection(PROJ* pp) const { if (!pp) throw Error("Event::applyProjection(PROJ*): Projection pointer is null."); return applyProjection(*pp); } + //@} + private: /// @brief Actual (shared) implementation of the constructors from GenEvents - /// - /// @todo When we can require C++11, constructors can call each other and - /// this can be removed. void _init(const GenEvent& ge); - /// @brief Convert the GenEvent to use conventional alignment - /// - /// For example, FHerwig only produces DIS events in the unconventional - /// hadron-lepton orientation and has to be corrected for DIS analysis - /// portability. - void _geNormAlignment(); + // /// @brief Convert the GenEvent to use conventional alignment + // /// + // /// For example, FHerwig only produces DIS events in the unconventional + // /// hadron-lepton orientation and has to be corrected for DIS analysis + // /// portability. + // void _geNormAlignment(); /// @brief The generated event, as obtained from an external generator. /// /// This is the original GenEvent. In practise the version seen by users /// will often/always be a modified one. /// /// @todo Provide access to this via an Event::originalGenEvent() method? If requested... const GenEvent* _originalGenEvent; /// @brief The GenEvent used by Rivet analysis projections etc. /// /// This version may be rotated to a "normal" alignment, have /// generator-specific particles stripped out, etc. If an analysis is /// affected by these modifications, it is probably an unphysical analysis! /// /// Stored as a non-pointer since it may get overwritten, and memory for - /// copying and cleanup is much neater this way. + /// copying and cleanup is neater this way. + /// @todo Change needed for HepMC3? GenEvent _genEvent; /// The set of Projection objects applied so far. mutable std::set<ConstProjectionPtr> _projections; }; } #endif diff --git a/include/Rivet/Particle.hh b/include/Rivet/Particle.hh --- a/include/Rivet/Particle.hh +++ b/include/Rivet/Particle.hh @@ -1,384 +1,384 @@ // -*- C++ -*- #ifndef RIVET_Particle_HH #define RIVET_Particle_HH #include "Rivet/Particle.fhh" #include "Rivet/ParticleBase.hh" #include "Rivet/Config/RivetCommon.hh" #include "Rivet/Tools/Utils.hh" // NOTE: Rivet/Tools/ParticleUtils.hh included at the end #include "fastjet/PseudoJet.hh" namespace Rivet { - /// Representation of particles from a HepMC::GenEvent. + /// Particle representation, either from a HepMC::GenEvent or reconstructed. class Particle : public ParticleBase { public: /// Default constructor. /// @note A particle without info is useless. This only exists to keep STL containers happy. Particle() : ParticleBase(), _original(0), _id(0) { } /// Constructor without GenParticle. Particle(PdgId pid, const FourMomentum& mom, const FourVector& pos=FourVector()) : ParticleBase(), _original(0), _id(pid), _momentum(mom), _origin(pos) { } /// Constructor from a HepMC GenParticle. Particle(const GenParticle& gp) : ParticleBase(), _original(&gp), _id(gp.pdg_id()), _momentum(gp.momentum()) { const GenVertex* vprod = gp.production_vertex(); if (vprod != NULL) setOrigin(vprod->position().t(), vprod->position().x(), vprod->position().y(), vprod->position().z()); } /// Constructor from a HepMC GenParticle pointer. Particle(const GenParticle* gp) : ParticleBase(), _original(gp), _id(gp->pdg_id()), _momentum(gp->momentum()) { const GenVertex* vprod = gp->production_vertex(); if (vprod != NULL) setOrigin(vprod->position().t(), vprod->position().x(), vprod->position().y(), vprod->position().z()); } public: /// Converter to FastJet3 PseudoJet virtual fastjet::PseudoJet pseudojet() const { return fastjet::PseudoJet(mom().px(), mom().py(), mom().pz(), mom().E()); } /// Cast operator to FastJet3 PseudoJet operator PseudoJet () const { return pseudojet(); } public: /// @name Basic particle specific properties //@{ /// Get a const pointer to the original GenParticle. const GenParticle* genParticle() const { return _original; } /// Cast operator for conversion to GenParticle* operator const GenParticle* () const { return genParticle(); } /// The momentum. const FourMomentum& momentum() const { return _momentum; } /// Set the momentum. Particle& setMomentum(const FourMomentum& momentum) { _momentum = momentum; return *this; } /// Set the momentum via components. Particle& setMomentum(double E, double px, double py, double pz) { _momentum = FourMomentum(E, px, py, pz); return *this; } /// The origin position. const FourVector& origin() const { return _origin; } /// Set the origin position. Particle& setOrigin(const FourVector& position) { _origin = position; return *this; } /// Set the origin position via components. Particle& setOrigin(double t, double x, double y, double z) { _origin = FourMomentum(t, x, y, z); return *this; } //@} /// @name Particle ID code accessors //@{ /// This Particle's PDG ID code. PdgId pid() const { return _id; } /// Absolute value of the PDG ID code. PdgId abspid() const { return std::abs(_id); } /// This Particle's PDG ID code (alias). /// @deprecated Prefer the pid/abspid form PdgId pdgId() const { return _id; } //@} /// @name Particle species properties //@{ /// The charge of this Particle. double charge() const { return PID::charge(pid()); } /// Three times the charge of this Particle (i.e. integer multiple of smallest quark charge). int charge3() const { return PID::charge3(pid()); } /// The abs charge of this Particle. double abscharge() const { return PID::abscharge(pid()); } /// Three times the abs charge of this Particle (i.e. integer multiple of smallest quark charge). int abscharge3() const { return PID::abscharge3(pid()); } /// Alias for charge3 /// @deprecated Use charge3 int threeCharge() const { return charge3(); } /// Is this a hadron? bool isHadron() const { return PID::isHadron(pid()); } /// Is this a meson? bool isMeson() const { return PID::isMeson(pid()); } /// Is this a baryon? bool isBaryon() const { return PID::isBaryon(pid()); } /// Is this a lepton? bool isLepton() const { return PID::isLepton(pid()); } /// Is this a neutrino? bool isNeutrino() const { return PID::isNeutrino(pid()); } /// Does this (hadron) contain a b quark? bool hasBottom() const { return PID::hasBottom(pid()); } /// Does this (hadron) contain a c quark? bool hasCharm() const { return PID::hasCharm(pid()); } /// Is this particle potentially visible in a detector? bool isVisible() const { // Charged particles are visible if ( PID::threeCharge(pid()) != 0 ) return true; // Neutral hadrons are visible if ( PID::isHadron(pid()) ) return true; // Photons are visible if ( pid() == PID::PHOTON ) return true; // Gluons are visible (for parton level analyses) if ( pid() == PID::GLUON ) return true; // Everything else is invisible return false; } // /// Does this (hadron) contain an s quark? // bool hasStrange() const { return PID::hasStrange(pid()); } //@} /// @name Ancestry properties //@{ /// Check whether a given PID is found in the GenParticle's ancestor list /// /// @note This question is valid in MC, but may not be answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool hasAncestor(PdgId pdg_id) const; /// @brief Determine whether the particle is from a b-hadron decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromBottom() const; /// @brief Determine whether the particle is from a c-hadron decay /// /// @note If a hadron contains b and c quarks it is considered a bottom /// hadron and NOT a charm hadron. /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromCharm() const; // /// @brief Determine whether the particle is from a s-hadron decay // /// // /// @note If a hadron contains b or c quarks as well as strange it is // /// considered a b or c hadron, but NOT a strange hadron. // /// // /// @note This question is valid in MC, but may not be perfectly answerable // /// experimentally -- use this function with care when replicating // /// experimental analyses! // bool fromStrange() const; /// @brief Determine whether the particle is from a hadron decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromHadron() const; /// @brief Determine whether the particle is from a tau decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromTau(bool prompt_taus_only=false) const; /// @brief Determine whether the particle is from a prompt tau decay /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromPromptTau() const { return fromTau(true); } /// @brief Determine whether the particle is from a hadron or tau decay /// /// Specifically, walk up the ancestor chain until a status 2 hadron or /// tau is found, if at all. /// /// @note This question is valid in MC, but may not be perfectly answerable /// experimentally -- use this function with care when replicating /// experimental analyses! bool fromDecay() const { return fromHadron() || fromPromptTau(); } //@} /// @name Decay info //@{ /// Whether this particle is stable according to the generator bool isStable() const { return genParticle() != NULL && genParticle()->status() == 1 && genParticle()->end_vertex() == NULL; } /// Get a list of the direct descendants from the current particle vector<Particle> children() const { vector<Particle> rtn; if (isStable()) return rtn; /// @todo Remove this const mess crap when HepMC doesn't suck HepMC::GenVertex* gv = const_cast<HepMC::GenVertex*>( genParticle()->end_vertex() ); /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticle* gp, gv->particles(HepMC::children)) // rtn += Particle(gp); for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::children); it != gv->particles_end(HepMC::children); ++it) rtn += Particle(*it); return rtn; } /// Get a list of all the descendants (including duplication of parents and children) from the current particle /// @todo Use recursion through replica-avoiding MCUtils functions to avoid bookkeeping duplicates /// @todo Insist that the current particle is post-hadronization, otherwise throw an exception? vector<Particle> allDescendants() const { vector<Particle> rtn; if (isStable()) return rtn; /// @todo Remove this const mess crap when HepMC doesn't suck HepMC::GenVertex* gv = const_cast<HepMC::GenVertex*>( genParticle()->end_vertex() ); /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticle* gp, gv->particles(HepMC::descendants)) // rtn += Particle(gp); for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::descendants); it != gv->particles_end(HepMC::descendants); ++it) rtn += Particle(*it); return rtn; } /// Get a list of all the stable descendants from the current particle /// @todo Use recursion through replica-avoiding MCUtils functions to avoid bookkeeping duplicates /// @todo Insist that the current particle is post-hadronization, otherwise throw an exception? vector<Particle> stableDescendants() const { vector<Particle> rtn; if (isStable()) return rtn; /// @todo Remove this const mess crap when HepMC doesn't suck HepMC::GenVertex* gv = const_cast<HepMC::GenVertex*>( genParticle()->end_vertex() ); /// @todo Would like to do this, but the range objects are broken // foreach (const GenParticle* gp, gv->particles(HepMC::descendants)) // if (gp->status() == 1 && gp->end_vertex() == NULL) // rtn += Particle(gp); for (GenVertex::particle_iterator it = gv->particles_begin(HepMC::descendants); it != gv->particles_end(HepMC::descendants); ++it) if ((*it)->status() == 1 && (*it)->end_vertex() == NULL) rtn += Particle(*it); return rtn; } /// Flight length (divide by mm or cm to get the appropriate units) double flightLength() const { if (isStable()) return -1; if (genParticle() == NULL) return 0; if (genParticle()->production_vertex() == NULL) return 0; const HepMC::FourVector v1 = genParticle()->production_vertex()->position(); const HepMC::FourVector v2 = genParticle()->end_vertex()->position(); return sqrt(sqr(v2.x()-v1.x()) + sqr(v2.y()-v1.y()) + sqr(v2.z()-v1.z())); } //@} private: /// A pointer to the original GenParticle from which this Particle is projected. const GenParticle* _original; /// The PDG ID code for this Particle. PdgId _id; /// The momentum of this particle. FourMomentum _momentum; /// The creation position of this particle. FourVector _origin; }; /// @name Unbound functions for filtering particles //@{ /// Filter a jet collection in-place to the subset that passes the supplied Cut Particles& filterBy(Particles& particles, const Cut& c); /// Get a subset of the supplied particles that passes the supplied Cut Particles filterBy(const Particles& particles, const Cut& c); //@} /// @name String representation //@{ /// Print a ParticlePair as a string. inline std::string to_str(const ParticlePair& pair) { stringstream out; out << "[" << PID::toParticleName(pair.first.pid()) << " @ " << pair.first.momentum().E()/GeV << " GeV, " << PID::toParticleName(pair.second.pid()) << " @ " << pair.second.momentum().E()/GeV << " GeV]"; return out.str(); } /// Allow ParticlePair to be passed to an ostream. inline std::ostream& operator<<(std::ostream& os, const ParticlePair& pp) { os << to_str(pp); return os; } //@} } #include "Rivet/Tools/ParticleUtils.hh" #endif diff --git a/include/Rivet/Projections/Beam.hh b/include/Rivet/Projections/Beam.hh --- a/include/Rivet/Projections/Beam.hh +++ b/include/Rivet/Projections/Beam.hh @@ -1,137 +1,147 @@ // -*- C++ -*- #ifndef RIVET_Beam_HH #define RIVET_Beam_HH #include "Rivet/Projection.hh" #include "Rivet/Event.hh" #include "Rivet/Particle.hh" +#include "Rivet/Math/LorentzTrans.hh" namespace Rivet { /// @name Standalone beam kinematics functions //@{ /// Get beam particles from an event ParticlePair beams(const Event& e); /// Get beam particle IDs from a pair of Particles inline PdgIdPair beamIds(const ParticlePair& beams) { return make_pair(beams.first.pid(), beams.second.pid()); } /// Get beam particle IDs from an event inline PdgIdPair beamIds(const Event& e) { return beamIds(beams(e)); } /// Get beam centre-of-mass energy from a pair of beam momenta double sqrtS(const FourMomentum& pa, const FourMomentum& pb); /// Get beam centre-of-mass energy from a pair of Particles inline double sqrtS(const ParticlePair& beams) { return sqrtS(beams.first.momentum(), beams.second.momentum()); } /// Get beam centre-of-mass energy from an Event inline double sqrtS(const Event& e) { return sqrtS(beams(e)); } /// Get per-nucleon beam centre-of-mass energy from a pair of beam momenta /// @note Uses a nominal nucleon mass of 0.939 GeV to convert masses to A double asqrtS(const FourMomentum& pa, const FourMomentum& pb); /// Get per-nucleon beam centre-of-mass energy from a pair of Particles /// @note Uses the sum of nuclear mass numbers A for each beam double asqrtS(const ParticlePair& beams); /// Get per-nucleon beam centre-of-mass energy from an Event inline double asqrtS(const Event& e) { return asqrtS(beams(e)); } - /// Get an active Lorentz boost to the beam centre-of-mass from a pair of beam momenta - Vector3 comBoost(const FourMomentum& pa, const FourMomentum& pb); + /// Get the Lorentz boost to the beam centre-of-mass system (CMS) from a pair of beam momenta + Vector3 cmsBoost(const FourMomentum& pa, const FourMomentum& pb); - /// Get an active Lorentz boost to the beam centre-of-mass from a pair of Particles - inline Vector3 comBoost(const ParticlePair& beams) { - return comBoost(beams.first, beams.second); + /// Get the Lorentz boost to the beam centre-of-mass system (CMS) from a pair of Particles + inline Vector3 cmsBoost(const ParticlePair& beams) { + return cmsBoost(beams.first, beams.second); } - /// Get an active Lorentz boost to the beam centre-of-mass from an Event - inline Vector3 comBoost(const Event& e) { - return comBoost(beams(e)); + /// Get the Lorentz boost to the beam centre-of-mass system (CMS) from an Event + inline Vector3 beamCMSBoost(const Event& e) { + return cmsBoost(beams(e)); + } + + + /// Get the Lorentz transformation to the beam centre-of-mass system (CMS) from a pair of beam momenta + LorentzTransform cmsTransform(const FourMomentum& pa, const FourMomentum& pb); + + /// Get the Lorentz transformation to the beam centre-of-mass system (CMS) from a pair of Particles + inline LorentzTransform cmsTransform(const ParticlePair& beams) { + return cmsTransform(beams.first, beams.second); + } + + /// Get the Lorentz transformation to the beam centre-of-mass system (CMS) from an Event + inline LorentzTransform beamCMSTransform(const Event& e) { + return cmsTransform(beams(e)); } //@} /// @brief Project out the incoming beams class Beam : public Projection { public: /// The default constructor Beam() { setName("Beam"); } /// Clone on the heap virtual unique_ptr<Projection> clone() const { return unique_ptr<Projection>(new Beam(*this)); } public: /// The pair of beam particles in the current collision - const ParticlePair& beams() const { - return _theBeams; - } + const ParticlePair& beams() const { return _theBeams; } /// The pair of beam particle PDG codes in the current collision - const PdgIdPair beamIds() const { - return Rivet::beamIds(beams()); - } + const PdgIdPair beamIds() const { return Rivet::beamIds(beams()); } /// Get centre of mass energy, \f$ \sqrt{s} \f$ double sqrtS() const { return Rivet::sqrtS(beams()); } /// Get per-nucleon centre of mass energy, \f$ \sqrt{s}/(A_1 + A_2) \f$ double asqrtS() const { return Rivet::asqrtS(beams()); } - /// Get an active Lorentz boost to the beam centre-of-mass - Vector3 comBoost() const { return Rivet::comBoost(beams()); } + /// Get the Lorentz boost to the beam centre-of-mass + Vector3 cmsBoost() const { return Rivet::cmsBoost(beams()); } + + /// Get the Lorentz transform to the beam centre-of-mass + LorentzTransform cmsTransform() const { return Rivet::cmsTransform(beams()); } /// Get the beam interaction primary vertex (PV) position FourVector pv() const; - public: - /// Project on to the Event virtual void project(const Event& e); private: /// Compare with other projections -- it's always the same, since there are no params - virtual int compare(const Projection& UNUSED(p)) const { - return EQUIVALENT; - } + virtual int compare(const Projection& UNUSED(p)) const { return EQUIVALENT; } /// The beam particles in the current collision ParticlePair _theBeams; }; } #endif diff --git a/src/Core/AnalysisHandler.cc b/src/Core/AnalysisHandler.cc --- a/src/Core/AnalysisHandler.cc +++ b/src/Core/AnalysisHandler.cc @@ -1,332 +1,332 @@ // -*- C++ -*- #include "Rivet/Config/RivetCommon.hh" #include "Rivet/AnalysisHandler.hh" #include "Rivet/Analysis.hh" #include "Rivet/ParticleName.hh" #include "Rivet/BeamConstraint.hh" #include "Rivet/Tools/Logging.hh" #include "Rivet/Projections/Beam.hh" #include "YODA/WriterYODA.h" namespace Rivet { AnalysisHandler::AnalysisHandler(const string& runname) : _runname(runname), _numEvents(0), _sumOfWeights(0.0), _xs(NAN), _initialised(false), _ignoreBeams(false) {} AnalysisHandler::~AnalysisHandler() {} Log& AnalysisHandler::getLog() const { return Log::getLog("Rivet.Analysis.Handler"); } void AnalysisHandler::init(const GenEvent& ge) { if (_initialised) throw UserError("AnalysisHandler::init has already been called: cannot re-initialize!"); setRunBeams(Rivet::beams(ge)); MSG_DEBUG("Initialising the analysis handler"); _numEvents = 0; _sumOfWeights = 0.0; _sumOfWeightsSq = 0.0; // Check that analyses are beam-compatible, and remove those that aren't const size_t num_anas_requested = analysisNames().size(); vector<string> anamestodelete; - foreach (const AnaHandle a, _analyses) { + for (const AnaHandle a : _analyses) { if (!_ignoreBeams && !a->isCompatible(beams())) { //MSG_DEBUG(a->name() << " requires beams " << a->requiredBeams() << " @ " << a->requiredEnergies() << " GeV"); anamestodelete.push_back(a->name()); } } - foreach (const string& aname, anamestodelete) { + for (const string& aname : anamestodelete) { MSG_WARNING("Analysis '" << aname << "' is incompatible with the provided beams: removing"); removeAnalysis(aname); } if (num_anas_requested > 0 && analysisNames().empty()) { cerr << "All analyses were incompatible with the first event's beams\n" << "Exiting, since this probably wasn't intentional!" << endl; exit(1); } // Warn if any analysis' status is not unblemished - foreach (const AnaHandle a, analyses()) { + for (const AnaHandle a : analyses()) { if (toUpper(a->status()) == "PRELIMINARY") { MSG_WARNING("Analysis '" << a->name() << "' is preliminary: be careful, it may change and/or be renamed!"); } else if (toUpper(a->status()) == "OBSOLETE") { MSG_WARNING("Analysis '" << a->name() << "' is obsolete: please update!"); } else if (toUpper(a->status()).find("UNVALIDATED") != string::npos) { MSG_WARNING("Analysis '" << a->name() << "' is unvalidated: be careful, it may be broken!"); } } // Initialize the remaining analyses - foreach (AnaHandle a, _analyses) { + for (AnaHandle a : _analyses) { MSG_DEBUG("Initialising analysis: " << a->name()); try { // Allow projection registration in the init phase onwards a->_allowProjReg = true; a->init(); //MSG_DEBUG("Checking consistency of analysis: " << a->name()); //a->checkConsistency(); } catch (const Error& err) { cerr << "Error in " << a->name() << "::init method: " << err.what() << endl; exit(1); } MSG_DEBUG("Done initialising analysis: " << a->name()); } _initialised = true; MSG_DEBUG("Analysis handler initialised"); } void AnalysisHandler::analyze(const GenEvent& ge) { // Call init with event as template if not already initialised if (!_initialised) init(ge); assert(_initialised); // Ensure that beam details match those from the first event const PdgIdPair beams = Rivet::beamIds(ge); const double sqrts = Rivet::sqrtS(ge); if (!compatible(beams, _beams) || !fuzzyEquals(sqrts, sqrtS())) { cerr << "Event beams mismatch: " << PID::toBeamsString(beams) << " @ " << sqrts/GeV << " GeV" << " vs. first beams " << this->beams() << " @ " << this->sqrtS()/GeV << " GeV" << endl; exit(1); } // Create the Rivet event wrapper /// @todo Filter/normalize the event here Event event(ge); // Weights /// @todo Drop this / just report first weight when we support multiweight events _numEvents += 1; _sumOfWeights += event.weight(); _sumOfWeightsSq += sqr(event.weight()); MSG_DEBUG("Event #" << _numEvents << " weight = " << event.weight()); // Cross-section #ifdef HEPMC_HAS_CROSS_SECTION if (ge.cross_section()) { _xs = ge.cross_section()->cross_section(); _xserr = ge.cross_section()->cross_section_error(); } #endif // Run the analyses - foreach (AnaHandle a, _analyses) { + for (AnaHandle a : _analyses) { MSG_TRACE("About to run analysis " << a->name()); try { a->analyze(event); } catch (const Error& err) { cerr << "Error in " << a->name() << "::analyze method: " << err.what() << endl; exit(1); } MSG_TRACE("Finished running analysis " << a->name()); } } void AnalysisHandler::analyze(const GenEvent* ge) { if (ge == NULL) { MSG_ERROR("AnalysisHandler received null pointer to GenEvent"); //throw Error("AnalysisHandler received null pointer to GenEvent"); } analyze(*ge); } void AnalysisHandler::finalize() { if (!_initialised) return; MSG_INFO("Finalising analyses"); - foreach (AnaHandle a, _analyses) { + for (AnaHandle a : _analyses) { a->setCrossSection(_xs); try { a->finalize(); } catch (const Error& err) { cerr << "Error in " << a->name() << "::finalize method: " << err.what() << endl; exit(1); } } // Print out number of events processed MSG_INFO("Processed " << _numEvents << " event" << (_numEvents == 1 ? "" : "s")); // // Delete analyses // MSG_DEBUG("Deleting analyses"); // _analyses.clear(); // Print out MCnet boilerplate cout << endl; cout << "The MCnet usage guidelines apply to Rivet: see http://www.montecarlonet.org/GUIDELINES" << endl; cout << "Please acknowledge plots made with Rivet analyses, and cite arXiv:1003.0694 (http://arxiv.org/abs/1003.0694)" << endl; } AnalysisHandler& AnalysisHandler::addAnalysis(const string& analysisname) { // Check for a duplicate analysis /// @todo Might we want to be able to run an analysis twice, with different params? /// Requires avoiding histo tree clashes, i.e. storing the histos on the analysis objects. - foreach (const AnaHandle& a, _analyses) { + for (const AnaHandle& a : _analyses) { if (a->name() == analysisname) { MSG_WARNING("Analysis '" << analysisname << "' already registered: skipping duplicate"); return *this; } } AnaHandle analysis( AnalysisLoader::getAnalysis(analysisname) ); if (analysis.get() != 0) { // < Check for null analysis. MSG_DEBUG("Adding analysis '" << analysisname << "'"); analysis->_analysishandler = this; _analyses.insert(analysis); } else { MSG_WARNING("Analysis '" << analysisname << "' not found."); } // MSG_WARNING(_analyses.size()); - // foreach (const AnaHandle& a, _analyses) MSG_WARNING(a->name()); + // for (const AnaHandle& a : _analyses) MSG_WARNING(a->name()); return *this; } AnalysisHandler& AnalysisHandler::removeAnalysis(const string& analysisname) { std::shared_ptr<Analysis> toremove; - foreach (const AnaHandle a, _analyses) { + for (const AnaHandle a : _analyses) { if (a->name() == analysisname) { toremove = a; break; } } if (toremove.get() != 0) { MSG_DEBUG("Removing analysis '" << analysisname << "'"); _analyses.erase(toremove); } return *this; } vector<AnalysisObjectPtr> AnalysisHandler::getData() const { vector<AnalysisObjectPtr> rtn; rtn.push_back( make_shared<Counter>(YODA::Dbn0D(_numEvents, _sumOfWeights, _sumOfWeightsSq), "/_EVTCOUNT") ); YODA::Scatter1D::Points pts; pts.insert(YODA::Point1D(_xs, _xserr)); rtn.push_back( make_shared<Scatter1D>(pts, "/_XSEC") ); - foreach (const AnaHandle a, analyses()) { + for (const AnaHandle a : analyses()) { vector<AnalysisObjectPtr> aos = a->analysisObjects(); // MSG_WARNING(a->name() << " " << aos.size()); - foreach (const AnalysisObjectPtr ao, aos) { + for (const AnalysisObjectPtr ao : aos) { // Exclude paths starting with /TMP/ from final write-out /// @todo This needs to be much more nuanced for re-entrant histogramming if (ao->path().find("/TMP/") != string::npos) continue; rtn.push_back(ao); } } - sort(rtn.begin(), rtn.end(), + sort(rtn.begin(), rtn.end(), [](AnalysisObjectPtr a, AnalysisObjectPtr b) { return a->path() < b->path(); } ); return rtn; } void AnalysisHandler::writeData(const string& filename) const { const vector<AnalysisObjectPtr> aos = getData(); try { YODA::WriterYODA::write(filename, aos.begin(), aos.end()); } catch (...) { /// @todo Move to specific YODA::WriteError type when YODA >= 1.5.0 is well-established throw UserError("Unexpected error in writing file to: " + filename); } } string AnalysisHandler::runName() const { return _runname; } size_t AnalysisHandler::numEvents() const { return _numEvents; } double AnalysisHandler::sumOfWeights() const { return _sumOfWeights; } void AnalysisHandler::setSumOfWeights(const double& sum) { _sumOfWeights=sum; } std::vector<std::string> AnalysisHandler::analysisNames() const { std::vector<std::string> rtn; - foreach (AnaHandle a, _analyses) { + for (AnaHandle a : _analyses) { rtn.push_back(a->name()); } return rtn; } const AnaHandle AnalysisHandler::analysis(const std::string& analysisname) const { - foreach (const AnaHandle a, analyses()) + for (const AnaHandle a : analyses()) if (a->name() == analysisname) return a; throw Error("No analysis named '" + analysisname + "' registered in AnalysisHandler"); } AnalysisHandler& AnalysisHandler::addAnalyses(const std::vector<std::string>& analysisnames) { - foreach (const string& aname, analysisnames) { + for (const string& aname : analysisnames) { //MSG_DEBUG("Adding analysis '" << aname << "'"); addAnalysis(aname); } return *this; } AnalysisHandler& AnalysisHandler::removeAnalyses(const std::vector<std::string>& analysisnames) { - foreach (const string& aname, analysisnames) { + for (const string& aname : analysisnames) { removeAnalysis(aname); } return *this; } bool AnalysisHandler::needCrossSection() const { bool rtn = false; - foreach (const AnaHandle a, _analyses) { + for (const AnaHandle a : _analyses) { if (!rtn) rtn = a->needsCrossSection(); if (rtn) break; } return rtn; } AnalysisHandler& AnalysisHandler::setCrossSection(double xs) { _xs = xs; return *this; } bool AnalysisHandler::hasCrossSection() const { return (!std::isnan(crossSection())); } AnalysisHandler& AnalysisHandler::addAnalysis(Analysis* analysis) { analysis->_analysishandler = this; _analyses.insert(AnaHandle(analysis)); return *this; } PdgIdPair AnalysisHandler::beamIds() const { return Rivet::beamIds(beams()); } double AnalysisHandler::sqrtS() const { return Rivet::sqrtS(beams()); } void AnalysisHandler::setIgnoreBeams(bool ignore) { _ignoreBeams=ignore; } } diff --git a/src/Core/Event.cc b/src/Core/Event.cc --- a/src/Core/Event.cc +++ b/src/Core/Event.cc @@ -1,85 +1,97 @@ #include "Rivet/Event.hh" #include "Rivet/Tools/Logging.hh" #include "Rivet/Projections/Beam.hh" #include "Rivet/BeamConstraint.hh" #include "HepMC/GenEvent.h" namespace Rivet { + // ParticlePair Event::beams() const { return Rivet::beams(*this); } + + // double Event::sqrtS() const { return Rivet::sqrtS(*this); } + + // double Event::asqrtS() const { return Rivet::asqrtS(*this); } + + // Vector3 Event::beamCMSBoost() const { return Rivet::beamCMSBoost(*this); } + + // LorentzTransform Event::beamCMSTransform() const { return Rivet::beamCMSTransform(*this); } + + + void Event::_init(const GenEvent& ge) { // Use Rivet's preferred units if possible #ifdef HEPMC_HAS_UNITS _genEvent.use_units(HepMC::Units::GEV, HepMC::Units::MM); #endif // Use the conventional alignment // _geNormAlignment(); /// @todo Filter the event to remove generator-specific particles: optional /// behaviour? Maybe disableable in an inconvenient way, e.g. with an env /// var, to communicate the appropriate distaste for this sort of truth /// analysis ;-) // Debug printout to check that copying/mangling has worked /// @todo Enable this when HepMC has been fixed to allow printing to a stream like the Rivet logger. //_genEvent.print(); } // namespace { // unnamed namespace for hiding // // void _geRot180x(GenEvent& ge) { // /// @todo Use nicer iterators over HepMC particles // for (HepMC::GenEvent::particle_iterator ip = ge.particles_begin(); ip != ge.particles_end(); ++ip) { // const HepMC::FourVector& mom = (*ip)->momentum(); // (*ip)->set_momentum(HepMC::FourVector(mom.px(), -mom.py(), -mom.pz(), mom.e())); // } // /// @todo Use nicer iterators over HepMC vertices // for (HepMC::GenEvent::vertex_iterator iv = ge.vertices_begin(); iv != ge.vertices_end(); ++iv) { // const HepMC::FourVector& pos = (*iv)->position(); // (*iv)->set_position(HepMC::FourVector(pos.x(), -pos.y(), -pos.z(), pos.t())); // } // } // // } // void Event::_geNormAlignment() { // if (!_genEvent.valid_beam_particles()) return; // typedef pair<HepMC::GenParticle*, HepMC::GenParticle*> GPPair; // GPPair bps = _genEvent.beam_particles(); // // // Rotate e+- p and ppbar to put p along +z // /// @todo Is there an e+ e- convention for longitudinal boosting, e.g. at B-factories? Different from LEP? // // if (compatible(beamids, make_pdgid_pair(ELECTRON, PROTON)) || // // compatible(beamids, make_pdgid_pair(POSITRON, PROTON)) || // // compatible(beamids, make_pdgid_pair(ANTIPROTON, PROTON)) ) { // // Log::getLog("Rivet.Event") << Log::TRACE << "May need to rotate event..." << endl; // bool rot = false; // const HepMC::GenParticle* plusgp = 0; // if (bps.first->pdg_id() != PID::PROTON || bps.second->pdg_id() != PID::PROTON) { // if (bps.first->pdg_id() == PID::PROTON) { // plusgp = bps.first; // } else if (bps.second->pdg_id() == PID::PROTON) { // plusgp = bps.second; // } // if (plusgp && plusgp->momentum().pz() < 0) { // rot = true; // } // } // // // Do the rotation // if (rot) { // if (Log::getLog("Rivet.Event").isActive(Log::TRACE)) { // Log::getLog("Rivet.Event") << Log::TRACE << "Rotating event\n" // << "Before rotation: " // << bps.first->pdg_id() << "@pz=" << bps.first->momentum().pz()/GeV << ", " // << bps.second->pdg_id() << "@pz=" << bps.second->momentum().pz()/GeV << endl; // } // _geRot180x(_genEvent); // } // } } diff --git a/src/Projections/Beam.cc b/src/Projections/Beam.cc --- a/src/Projections/Beam.cc +++ b/src/Projections/Beam.cc @@ -1,74 +1,78 @@ // -*- C++ -*- #include "Rivet/Config/RivetCommon.hh" #include "Rivet/Tools/Logging.hh" #include "Rivet/Projections/Beam.hh" namespace Rivet { ParticlePair beams(const Event& e) { assert(e.genEvent()->particles_size() >= 2); if (e.genEvent()->valid_beam_particles()) { pair<HepMC::GenParticle*, HepMC::GenParticle*> beams = e.genEvent()->beam_particles(); assert(beams.first && beams.second); return ParticlePair{beams.first, beams.second}; } else if (e.genEvent()->barcode_to_particle(1) && e.genEvent()->barcode_to_particle(2)) { return ParticlePair{e.genEvent()->barcode_to_particle(1), e.genEvent()->barcode_to_particle(2)}; } return ParticlePair{Particle(PID::ANY, FourMomentum()), Particle(PID::ANY, FourMomentum())}; } double sqrtS(const FourMomentum& pa, const FourMomentum& pb) { const double mom1 = pa.pz(); const double e1 = pa.E(); const double mom2 = pb.pz(); const double e2 = pb.E(); double sqrts = sqrt( sqr(e1+e2) - sqr(mom1+mom2) ); return sqrts; } double asqrtS(const FourMomentum& pa, const FourMomentum& pb) { const static double MNUCLEON = 939*MeV; //< nominal nucleon mass return sqrtS(pa, pb) / (pa.mass()/MNUCLEON + pb.mass()/MNUCLEON); } double asqrtS(const ParticlePair& beams) { return sqrtS(beams) / (nuclA(beams.first) + nuclA(beams.second)); } - Vector3 comBoost(const FourMomentum& pa, const FourMomentum& pb) { - Vector3 rtn = -(pa.p3() + pb.p3()) / (pa.E() + pb.E()); + Vector3 cmsBoost(const FourMomentum& pa, const FourMomentum& pb) { + Vector3 rtn = (pa.p3() + pb.p3()) / (pa.E() + pb.E()); return rtn; } + LorentzTransform cmsTransform(const FourMomentum& pa, const FourMomentum& pb) { + return LorentzTransform(-cmsBoost(pa, pb)); + } + ///////////////////////////////////////////// void Beam::project(const Event& e) { _theBeams = Rivet::beams(e); MSG_DEBUG("Beam particles = " << _theBeams << " => sqrt(s) = " << sqrtS()/GeV << " GeV"); } FourVector Beam::pv() const { HepMC::FourVector v1, v2; const ParticlePair bpair = beams(); if (bpair.first.genParticle() && bpair.first.genParticle()->end_vertex()) v1 = bpair.first.genParticle()->end_vertex()->position(); if (bpair.second.genParticle() && bpair.second.genParticle()->end_vertex()) v2 = bpair.second.genParticle()->end_vertex()->position(); const FourVector rtn = (v1 == v2) ? FourVector(v1.t(), v1.x(), v1.y(), v1.z()) : FourVector(); MSG_DEBUG("Beam PV 4-position = " << rtn); return rtn; } }