rivet is hosted by Hepforge, IPPP Durham
AnalysisHandler.cc
Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #include "Rivet/Config/RivetCommon.hh"
00003 #include "Rivet/AnalysisHandler.hh"
00004 #include "Rivet/Analysis.hh"
00005 #include "Rivet/ParticleName.hh"
00006 #include "Rivet/BeamConstraint.hh"
00007 #include "Rivet/Tools/Logging.hh"
00008 #include "Rivet/Projections/Beam.hh"
00009 #include "YODA/WriterYODA.h"
00010 
00011 namespace Rivet {
00012 
00013 
00014   /// @todo Replace with a lambda when we can use C++11
00015   namespace {
00016     inline bool cmpAOByPath(const AnalysisObjectPtr a, const AnalysisObjectPtr b) {
00017       return a->path() < b->path();
00018     }
00019   }
00020 
00021 
00022   AnalysisHandler::AnalysisHandler(const string& runname)
00023     : _runname(runname), _numEvents(0),
00024       _sumOfWeights(0.0), _xs(NAN),
00025       _initialised(false), _ignoreBeams(false)
00026   {}
00027 
00028 
00029   AnalysisHandler::~AnalysisHandler()
00030   {}
00031 
00032 
00033   Log& AnalysisHandler::getLog() const {
00034     return Log::getLog("Rivet.Analysis.Handler");
00035   }
00036 
00037 
00038   void AnalysisHandler::init(const GenEvent& ge) {
00039     if (_initialised)
00040       throw UserError("AnalysisHandler::init has already been called: cannot re-initialize!");
00041 
00042     setRunBeams(Rivet::beams(ge));
00043     MSG_DEBUG("Initialising the analysis handler");
00044     _numEvents = 0;
00045     _sumOfWeights = 0.0;
00046     _sumOfWeightsSq = 0.0;
00047 
00048     // Check that analyses are beam-compatible, and remove those that aren't
00049     const size_t num_anas_requested = analysisNames().size();
00050     vector<string> anamestodelete;
00051     foreach (const AnaHandle a, _analyses) {
00052       if (!_ignoreBeams && !a->isCompatible(beams())) {
00053         //MSG_DEBUG(a->name() << " requires beams " << a->requiredBeams() << " @ " << a->requiredEnergies() << " GeV");
00054         anamestodelete.push_back(a->name());
00055       }
00056     }
00057     foreach (const string& aname, anamestodelete) {
00058       MSG_WARNING("Analysis '" << aname << "' is incompatible with the provided beams: removing");
00059       removeAnalysis(aname);
00060     }
00061     if (num_anas_requested > 0 && analysisNames().empty()) {
00062       cerr << "All analyses were incompatible with the first event's beams\n"
00063            << "Exiting, since this probably wasn't intentional!" << endl;
00064       exit(1);
00065     }
00066 
00067     // Warn if any analysis' status is not unblemished
00068     foreach (const AnaHandle a, analyses()) {
00069       if (toUpper(a->status()) == "PRELIMINARY") {
00070         MSG_WARNING("Analysis '" << a->name() << "' is preliminary: be careful, it may change and/or be renamed!");
00071       } else if (toUpper(a->status()) == "OBSOLETE") {
00072         MSG_WARNING("Analysis '" << a->name() << "' is obsolete: please update!");
00073       } else if (toUpper(a->status()).find("UNVALIDATED") != string::npos) {
00074         MSG_WARNING("Analysis '" << a->name() << "' is unvalidated: be careful, it may be broken!");
00075       }
00076     }
00077 
00078     // Initialize the remaining analyses
00079     foreach (AnaHandle a, _analyses) {
00080       MSG_DEBUG("Initialising analysis: " << a->name());
00081       try {
00082         // Allow projection registration in the init phase onwards
00083         a->_allowProjReg = true;
00084         a->init();
00085         //MSG_DEBUG("Checking consistency of analysis: " << a->name());
00086         //a->checkConsistency();
00087       } catch (const Error& err) {
00088         cerr << "Error in " << a->name() << "::init method: " << err.what() << endl;
00089         exit(1);
00090       }
00091       MSG_DEBUG("Done initialising analysis: " << a->name());
00092     }
00093     _initialised = true;
00094     MSG_DEBUG("Analysis handler initialised");
00095   }
00096 
00097 
00098   void AnalysisHandler::analyze(const GenEvent& ge) {
00099     // Call init with event as template if not already initialised
00100     if (!_initialised) init(ge);
00101     assert(_initialised);
00102 
00103     // Ensure that beam details match those from the first event
00104     const PdgIdPair beams = Rivet::beamIds(ge);
00105     const double sqrts = Rivet::sqrtS(ge);
00106     if (!compatible(beams, _beams) || !fuzzyEquals(sqrts, sqrtS())) {
00107       cerr << "Event beams mismatch: "
00108            << PID::toBeamsString(beams) << " @ " << sqrts/GeV << " GeV" << " vs. first beams "
00109            << this->beams() << " @ " << this->sqrtS()/GeV << " GeV" << endl;
00110       exit(1);
00111     }
00112 
00113     // Create the Rivet event wrapper
00114     /// @todo Filter/normalize the event here
00115     Event event(ge);
00116 
00117     // Weights
00118     /// @todo Drop this / just report first weight when we support multiweight events
00119     _numEvents += 1;
00120     _sumOfWeights += event.weight();
00121     _sumOfWeightsSq += sqr(event.weight());
00122     MSG_DEBUG("Event #" << _numEvents << " weight = " << event.weight());
00123 
00124     // Cross-section
00125     #ifdef HEPMC_HAS_CROSS_SECTION
00126     if (ge.cross_section()) {
00127       _xs = ge.cross_section()->cross_section();
00128       _xserr = ge.cross_section()->cross_section_error();
00129     }
00130     #endif
00131 
00132     // Run the analyses
00133     foreach (AnaHandle a, _analyses) {
00134       MSG_TRACE("About to run analysis " << a->name());
00135       try {
00136         a->analyze(event);
00137       } catch (const Error& err) {
00138         cerr << "Error in " << a->name() << "::analyze method: " << err.what() << endl;
00139         exit(1);
00140       }
00141       MSG_TRACE("Finished running analysis " << a->name());
00142     }
00143   }
00144 
00145 
00146   void AnalysisHandler::finalize() {
00147     if (!_initialised) return;
00148     MSG_INFO("Finalising analyses");
00149     foreach (AnaHandle a, _analyses) {
00150       a->setCrossSection(_xs);
00151       try {
00152         a->finalize();
00153       } catch (const Error& err) {
00154         cerr << "Error in " << a->name() << "::finalize method: " << err.what() << endl;
00155         exit(1);
00156       }
00157     }
00158 
00159     // Print out number of events processed
00160     MSG_INFO("Processed " << _numEvents << " event" << (_numEvents == 1 ? "" : "s"));
00161 
00162     // // Delete analyses
00163     // MSG_DEBUG("Deleting analyses");
00164     // _analyses.clear();
00165 
00166     // Print out MCnet boilerplate
00167     cout << endl;
00168     cout << "The MCnet usage guidelines apply to Rivet: see http://www.montecarlonet.org/GUIDELINES" << endl;
00169     cout << "Please acknowledge plots made with Rivet analyses, and cite arXiv:1003.0694 (http://arxiv.org/abs/1003.0694)" << endl;
00170   }
00171 
00172 
00173   AnalysisHandler& AnalysisHandler::addAnalysis(const string& analysisname) {
00174     // Check for a duplicate analysis
00175     /// @todo Might we want to be able to run an analysis twice, with different params?
00176     ///       Requires avoiding histo tree clashes, i.e. storing the histos on the analysis objects.
00177     foreach (const AnaHandle& a, _analyses) {
00178       if (a->name() == analysisname) {
00179         MSG_WARNING("Analysis '" << analysisname << "' already registered: skipping duplicate");
00180         return *this;
00181       }
00182     }
00183     AnaHandle analysis( AnalysisLoader::getAnalysis(analysisname) );
00184     if (analysis.get() != 0) { // < Check for null analysis.
00185       MSG_DEBUG("Adding analysis '" << analysisname << "'");
00186       analysis->_analysishandler = this;
00187       _analyses.insert(analysis);
00188     } else {
00189       MSG_WARNING("Analysis '" << analysisname << "' not found.");
00190     }
00191     // MSG_WARNING(_analyses.size());
00192     // foreach (const AnaHandle& a, _analyses) MSG_WARNING(a->name());
00193     return *this;
00194   }
00195 
00196 
00197   AnalysisHandler& AnalysisHandler::removeAnalysis(const string& analysisname) {
00198     shared_ptr<Analysis> toremove;
00199     foreach (const AnaHandle a, _analyses) {
00200       if (a->name() == analysisname) {
00201         toremove = a;
00202         break;
00203       }
00204     }
00205     if (toremove.get() != 0) {
00206       MSG_DEBUG("Removing analysis '" << analysisname << "'");
00207       _analyses.erase(toremove);
00208     }
00209     return *this;
00210   }
00211 
00212 
00213   vector<AnalysisObjectPtr> AnalysisHandler::getData() const {
00214     vector<AnalysisObjectPtr> rtn;
00215     rtn.push_back( AnalysisObjectPtr(new Counter(YODA::Dbn0D(_numEvents, _sumOfWeights, _sumOfWeightsSq), "/_EVTCOUNT")) );
00216     YODA::Scatter1D::Points pts; pts.insert(YODA::Point1D(_xs, _xserr));
00217     rtn.push_back( AnalysisObjectPtr(new Scatter1D(pts, "/_XSEC")) );
00218     foreach (const AnaHandle a, analyses()) {
00219       vector<AnalysisObjectPtr> aos = a->analysisObjects();
00220       // MSG_WARNING(a->name() << " " << aos.size());
00221       foreach (const AnalysisObjectPtr ao, aos) {
00222         // Exclude paths starting with /TMP/ from final write-out
00223         /// @todo This needs to be much more nuanced for re-entrant histogramming
00224         if (ao->path().find("/TMP/") != string::npos) continue;
00225         rtn.push_back(ao);
00226       }
00227     }
00228     /// @todo Use lambda in C++11
00229     sort(rtn.begin(), rtn.end(), cmpAOByPath);
00230     return rtn;
00231   }
00232 
00233 
00234   void AnalysisHandler::writeData(const string& filename) const {
00235     const vector<AnalysisObjectPtr> aos = getData();
00236     try {
00237       YODA::WriterYODA::write(filename, aos.begin(), aos.end());
00238     } catch (...) { /// @todo Move to specific YODA::WriteError type when YODA >= 1.5.0 is well-established
00239       throw UserError("Unexpected error in writing file to: " + filename);
00240     }
00241   }
00242 
00243 
00244   string AnalysisHandler::runName() const { return _runname; }
00245   size_t AnalysisHandler::numEvents() const { return _numEvents; }
00246   double AnalysisHandler::sumOfWeights() const { return _sumOfWeights; }
00247 
00248 
00249   void AnalysisHandler::setSumOfWeights(const double& sum) {
00250     _sumOfWeights=sum;
00251   }
00252 
00253 
00254   std::vector<std::string> AnalysisHandler::analysisNames() const {
00255     std::vector<std::string> rtn;
00256     foreach (AnaHandle a, _analyses) {
00257       rtn.push_back(a->name());
00258     }
00259     return rtn;
00260   }
00261 
00262 
00263   AnalysisHandler& AnalysisHandler::addAnalyses(const std::vector<std::string>& analysisnames) {
00264     foreach (const string& aname, analysisnames) {
00265       //MSG_DEBUG("Adding analysis '" << aname << "'");
00266       addAnalysis(aname);
00267     }
00268     return *this;
00269   }
00270 
00271 
00272   AnalysisHandler& AnalysisHandler::removeAnalyses(const std::vector<std::string>& analysisnames) {
00273     foreach (const string& aname, analysisnames) {
00274       removeAnalysis(aname);
00275     }
00276     return *this;
00277   }
00278 
00279 
00280   bool AnalysisHandler::needCrossSection() const {
00281     bool rtn = false;
00282     foreach (const AnaHandle a, _analyses) {
00283       if (!rtn) rtn = a->needsCrossSection();
00284       if (rtn) break;
00285     }
00286     return rtn;
00287   }
00288 
00289 
00290   AnalysisHandler& AnalysisHandler::setCrossSection(double xs) {
00291     _xs = xs;
00292     return *this;
00293   }
00294 
00295 
00296   bool AnalysisHandler::hasCrossSection() const {
00297     return (!std::isnan(crossSection()));
00298   }
00299 
00300 
00301   AnalysisHandler& AnalysisHandler::addAnalysis(Analysis* analysis) {
00302     analysis->_analysishandler = this;
00303     _analyses.insert(AnaHandle(analysis));
00304     return *this;
00305   }
00306 
00307 
00308   PdgIdPair AnalysisHandler::beamIds() const {
00309     return Rivet::beamIds(beams());
00310   }
00311 
00312 
00313   double AnalysisHandler::sqrtS() const {
00314     return Rivet::sqrtS(beams());
00315   }
00316 
00317   void AnalysisHandler::setIgnoreBeams(bool ignore) {
00318     _ignoreBeams=ignore;
00319   }
00320 
00321 
00322 }