diff --git a/analyses/pluginALICE/ALICE_2012_I1127497.info b/analyses/pluginALICE/ALICE_2012_I1127497.info --- a/analyses/pluginALICE/ALICE_2012_I1127497.info +++ b/analyses/pluginALICE/ALICE_2012_I1127497.info @@ -1,42 +1,41 @@ Name: ALICE_2012_I1127497 Year: 2012 Summary: Centrality dependence of charged particle production at large transverse momentum in Pb-Pb collisions at $\sqrt{s_{\rm{NN}}} = 2.76$ TeV Experiment: ALICE Collider: LHC SpireID: InspireID: 1127497 -Status: VALIDATED +Status: VALIDATED REENTRANT Authors: - Przemyslaw Karczmarczyk - Jan Fiete Grosse-Oetringhaus - Jochen Klein References: - arXiv:1208.2711 [hep-ex] RunInfo: NumEvents: Options: - cent=REF,GEN,IMP,USR Beams: [[p, p], [1000822080, 1000822080]] # This is _total_ energy of beams, so this becomes 208*2760=574080 Energies: [2760, 574080] Description: 'The inclusive transverse momentum ($p_T$) distributions of primary charged particles are measured in the pseudo-rapidity range $|\eta| < 0.8$ as a function of event centrality in Pb--Pb collisions at $\sqrt{s_{nn}} = 2.76$ TeV with ALICE at the LHC. The data are presented in the $p_T$ range $0.15 30$ GeV/c. In peripheral collisions (70-80%), the suppression is weaker with $R_{AA} \approx 0.7$ almost independently of $p_T$. The measured nuclear modification factors are compared to other measurements and model calculations.' BibKey: Abelev:2012hxa BibTeX: '@article{Abelev:2012hxa, author = "Abelev, Betty and others", title = "{Centrality Dependence of Charged Particle Production at Large Transverse Momentum in Pb--Pb Collisions at $\sqrt{s_{\rm{NN}}} = 2.76$ TeV}", collaboration = "ALICE", journal = "Phys. Lett.", volume = "B720", year = "2013", pages = "52-62", doi = "10.1016/j.physletb.2013.01.051", eprint = "1208.2711", archivePrefix = "arXiv", primaryClass = "hep-ex", reportNumber = "CERN-PH-EP-2012-233", SLACcitation = "%%CITATION = ARXIV:1208.2711;%%" }' -Reentrant: True diff --git a/analyses/pluginALICE/ALICE_2012_I930312.info b/analyses/pluginALICE/ALICE_2012_I930312.info --- a/analyses/pluginALICE/ALICE_2012_I930312.info +++ b/analyses/pluginALICE/ALICE_2012_I930312.info @@ -1,42 +1,41 @@ Name: ALICE_2012_I930312 Year: 2012 Summary: Particle-yield modification in jet-like azimuthal di-hadron correlations in Pb-Pb collisions at $\sqrt{s_\rm{NN}} = 2.76$ TeV Experiment: ALICE Collider: LHC SpireID: InspireID: 930312 -Status: VALIDATED +Status: VALIDATED REENTRANT Authors: - Przemyslaw Karczmarczyk - Jan Fiete Grosse-Oetringhaus - Jochen Klein References: - arXiv:1110.0121 [nucl-ex] RunInfo: NumEvents: Options: - cent=REF,GEN,IMP,USR Beams: [[p, p], [1000822080, 1000822080]] # This is _total_ energy of beams, so this becomes 208*2760=574080 Energies: [2760, 574080] Description: 'The yield of charged particles associated with high-pT trigger particles ($8 < p_{\perp} < 15$ GeV/c) is measured with the ALICE detector in Pb-Pb collisions at $\sqrt{s_{NN}} = 2.76$ TeV relative to proton-proton collisions at the same energy. The conditional per-trigger yields are extracted from the narrow jet-like correlation peaks in azimuthal di-hadron correlations. In the 5\% most central collisions, we observe that the yield of associated charged particles with transverse momenta $p_\perp > 3$ GeV/c on the away-side drops to about 60\% of that observed in pp collisions, while on the near-side a moderate enhancement of 20-30\% is found.' BibKey: Aamodt:2011vg BibTeX: '@article{Aamodt:2011vg, author = "Aamodt, K. and others", title = "{Particle-yield modification in jet-like azimuthal di-hadron correlations in Pb-Pb collisions at $\sqrt{s_{NN}} = 2.76$ TeV}", collaboration = "ALICE", journal = "Phys. Rev. Lett.", volume = "108", year = "2012", pages = "092301", doi = "10.1103/PhysRevLett.108.092301", eprint = "1110.0121", archivePrefix = "arXiv", primaryClass = "nucl-ex", reportNumber = "CERN-PH-EP-2011-161", SLACcitation = "%%CITATION = ARXIV:1110.0121;%%" }' -Reentrant: True diff --git a/analyses/pluginALICE/ALICE_2016_I1471838.info b/analyses/pluginALICE/ALICE_2016_I1471838.info --- a/analyses/pluginALICE/ALICE_2016_I1471838.info +++ b/analyses/pluginALICE/ALICE_2016_I1471838.info @@ -1,47 +1,46 @@ Name: ALICE_2016_I1471838 Year: 2016 Summary: Enhanced production of multi-strange hadrons in high-multiplicity proton-proton collisions. Experiment: ALICE Collider: LHC InspireID: 1471838 -Status: UNVALIDATED +Status: UNVALIDATED REENTRANT Authors: - Christian Bierlich References: - Nature.Phys.13(2017)535-539 - arXiv:1606.07424 RunInfo: Minimum bias pp NeedCrossSection: no Beams: [p+, p+] Energies: [7000] Options: - cent=REF,GEN,IMP,USR -Reentrant: True Description: 'Measurements of pT spectra and yields of (multi)strange hadrons, as well as protons and pions (yields only) in forward multiplicity classes at 7 TeV. Ratios of yields to pion yields are constructed. The analysis takes care of particle reconstruction as the experiment does, so no finite lifetime should be imposed on generator level. Experimental results are scaled to inelastic cross section, and generator setup should be adjusted accordingly.' Keywords: [QCD, Minimum bias, collectivity, small systems] BibKey: ALICE:2017jyt BibTeX: '@article{ALICE:2017jyt, author = "Adam, Jaroslav and others", title = "{Enhanced production of multi-strange hadrons in high-multiplicity proton-proton collisions}", collaboration = "ALICE", journal = "Nature Phys.", volume = "13", year = "2017", pages = "535-539", doi = "10.1038/nphys4111", eprint = "1606.07424", archivePrefix = "arXiv", primaryClass = "nucl-ex", reportNumber = "CERN-EP-2016-153", SLACcitation = "%%CITATION = ARXIV:1606.07424;%%" }' ToDo: - Implement the analysis, test it, remove this ToDo, and mark as VALIDATED :-) diff --git a/analyses/pluginATLAS/ATLAS_2010_S8817804.info b/analyses/pluginATLAS/ATLAS_2010_S8817804.info --- a/analyses/pluginATLAS/ATLAS_2010_S8817804.info +++ b/analyses/pluginATLAS/ATLAS_2010_S8817804.info @@ -1,41 +1,40 @@ Name: ATLAS_2010_S8817804 Year: 2010 Summary: Inclusive jet cross section and di-jet mass and chi spectra at 7 TeV in ATLAS Experiment: ATLAS Collider: LHC 7TeV SpiresID: 8817804 InspireID: 871366 -Status: VALIDATED -Reentrant: True +Status: VALIDATED REENTRANT Authors: - James Monk References: - arXiv:1009.5908 RunInfo: pp QCD jet production with a minimum jet pT of 60 GeV (inclusive) or 30 GeV (di-jets) at 7 TeV. NumEvents: 10000000 Beams: [p+, p+] Energies: [7000] PtCuts: [30, 60] Description: The first jet cross section measurement made with the ATLAS detector at the LHC. Anti-kt jets with $R=0.4$ and $R=0.6$ are resconstructed within $|y|<2.8$ and above 60~GeV for the inclusive jet cross section plots. For the di-jet plots the second jet must have pT>30~GeV. Jet pT and di-jet mass spectra are plotted in bins of rapidity between $|y| = $ 0.3, 0.8, 1.2, 2.1, and 2.8. Di-jet $\chi$ spectra are plotted in bins of di-jet mass between 340 GeV, 520 GeV, 800 GeV and 1200 GeV. NeedCrossSection: yes BibKey: Aad:2010wv BibTeX: '@Article{Aad:2010wv, author = "Aad, G. and others", collaboration = "ATLAS", title = "{Measurement of inclusive jet and dijet cross sections in proton-proton collisions at 7 TeV centre-of-mass energy with the ATLAS detector}", year = "2010", eprint = "1009.5908", archivePrefix = "arXiv", primaryClass = "hep-ex", SLACcitation = "%%CITATION = 1009.5908;%%" }' diff --git a/analyses/pluginMC/MC_Cent_pPb_Eta.info b/analyses/pluginMC/MC_Cent_pPb_Eta.info --- a/analyses/pluginMC/MC_Cent_pPb_Eta.info +++ b/analyses/pluginMC/MC_Cent_pPb_Eta.info @@ -1,43 +1,42 @@ Name: MC_Cent_pPb_Eta Summary: Template analysis for ontaining eta distributions binned in centrality -Status: UNVALIDATED +Status: UNVALIDATED REENTRANT Authors: - Leif Lönnblad Options: - cent=REF,GEN,IMP NumEvents: 50000 -Reentrant: True References: - arXiv:1508.00848 [hep-ex], Eur.Phys.J. C76 (2016) no.4, 199 RunInfo: Any! Description: Template analysis for obtaining eta distributions binned in centrality using the CentralityProjection and Percentile<> classes. The example is pPb collisions at 5 TeV and is based on the ATLAS analysis arXiv:1508.00848 [hep-ex]. The reference YODA file contains the corresponding plots from HepData. The generator should be run in minimum-bias mode with a cut on the transverse momentum of charged particles of 0.1 GeV, and setting particles with tcau>10 fm stable. Note that a calibration histogram for the generated centrality may be preloaded with the output of a corresponding MC_Cent_pPb_Calib analysis. BibKey: Aad:2015zza BibTeX: '@article{Aad:2015zza, author = "Aad, Georges and others", title = "{Measurement of the centrality dependence of the charged-particle pseudorapidity distribution in proton–lead collisions at $\sqrt{s_{_\text {NN}}} = 5.02$ TeV with the ATLAS detector}", collaboration = "ATLAS", journal = "Eur. Phys. J.", volume = "C76", year = "2016", number = "4", pages = "199", doi = "10.1140/epjc/s10052-016-4002-3", eprint = "1508.00848", archivePrefix = "arXiv", primaryClass = "hep-ex", reportNumber = "CERN-PH-EP-2015-160", SLACcitation = "%%CITATION = ARXIV:1508.00848;%%" }' diff --git a/include/Rivet/AnalysisInfo.hh b/include/Rivet/AnalysisInfo.hh --- a/include/Rivet/AnalysisInfo.hh +++ b/include/Rivet/AnalysisInfo.hh @@ -1,298 +1,329 @@ // -*- C++ -*- #ifndef RIVET_AnalysisInfo_HH #define RIVET_AnalysisInfo_HH #include "Rivet/Config/RivetCommon.hh" #include namespace Rivet { class AnalysisInfo { public: /// Static factory method: returns null pointer if no metadata found static unique_ptr make(const std::string& name); /// @name Standard constructors and destructors. //@{ /// The default constructor. AnalysisInfo() { clear(); } /// The destructor. ~AnalysisInfo() { } //@} public: /// @name Metadata /// Metadata is used for querying from the command line and also for /// building web pages and the analysis pages in the Rivet manual. //@{ /// Get the name of the analysis. By default this is computed using the /// experiment, year and Inspire/Spires ID metadata methods. std::string name() const { if (!_name.empty()) return _name; if (!experiment().empty() && !year().empty()) { if (!inspireId().empty()) { return experiment() + "_" + year() + "_I" + inspireId(); } else if (!spiresId().empty()) { return experiment() + "_" + year() + "_S" + spiresId(); } } return ""; } /// Set the name of the analysis. void setName(const std::string& name) { _name = name; } /// Get the reference data name of the analysis (if different from plugin name). std::string getRefDataName() const { if (!_refDataName.empty()) return _refDataName; return name(); } /// Set the reference data name of the analysis (if different from plugin name). void setRefDataName(const std::string& name) { _refDataName = name; } /// Get the Inspire (SPIRES replacement) ID code for this analysis. const std::string& inspireId() const { return _inspireId; } /// Set the Inspire (SPIRES replacement) ID code for this analysis. void setInspireId(const std::string& inspireId) { _inspireId = inspireId; } /// Get the SPIRES ID code for this analysis. const std::string& spiresId() const { return _spiresId; } /// Set the SPIRES ID code for this analysis. void setSpiresId(const std::string& spiresId) { _spiresId = spiresId; } /// @brief Names & emails of paper/analysis authors. /// Names and email of authors in 'NAME \' format. The first /// name in the list should be the primary contact person. const std::vector& authors() const { return _authors; } /// Set the author list. void setAuthors(const std::vector& authors) { _authors = authors; } /// @brief Get a short description of the analysis. /// Short (one sentence) description used as an index entry. /// Use @a description() to provide full descriptive paragraphs /// of analysis details. const std::string& summary() const { return _summary; } /// Set the short description for this analysis. void setSummary(const std::string& summary) { _summary = summary; } /// @brief Get a full description of the analysis. /// Full textual description of this analysis, what it is useful for, /// what experimental techniques are applied, etc. Should be treated /// as a chunk of restructuredText (http://docutils.sourceforge.net/rst.html), /// with equations to be rendered as LaTeX with amsmath operators. const std::string& description() const { return _description; } /// Set the full description for this analysis. void setDescription(const std::string& description) { _description = description; } /// @brief Information about the events needed as input for this analysis. /// Event types, energies, kinematic cuts, particles to be considered /// stable, etc. etc. Should be treated as a restructuredText bullet list /// (http://docutils.sourceforge.net/rst.html) const std::string& runInfo() const { return _runInfo; } /// Set the full description for this analysis. void setRunInfo(const std::string& runInfo) { _runInfo = runInfo; } /// Beam particle types const std::vector& beams() const { return _beams; } /// Set beam particle types void setBeams(const std::vector& beams) { _beams = beams; } /// Sets of valid beam energies const std::vector >& energies() const { return _energies; } /// Set the valid beam energies void setEnergies(const std::vector >& energies) { _energies = energies; } /// Experiment which performed and published this analysis. const std::string& experiment() const { return _experiment; } /// Set the experiment which performed and published this analysis. void setExperiment(const std::string& experiment) { _experiment = experiment; } /// Collider on which the experiment ran. const std::string& collider() const { return _collider; } /// Set the collider on which the experiment ran. void setCollider(const std::string& collider) { _collider = collider; } /// @brief When the original experimental analysis was published. /// When the refereed paper on which this is based was published, /// according to SPIRES. const std::string& year() const { return _year; } /// Set the year in which the original experimental analysis was published. void setYear(const std::string& year) { _year = year; } /// The integrated data luminosity of the data set const std::string& luminosityfb() const { return _luminosityfb; } /// Set the integrated data luminosity of the data set void setLuminosityfb(const std::string& luminosityfb) { _luminosityfb = luminosityfb; } /// Journal and preprint references. const std::vector& references() const { return _references; } /// Set the journal and preprint reference list. void setReferences(const std::vector& references) { _references = references; } /// Analysis Keywords for grouping etc const std::vector& keywords() const { return _keywords; } /// BibTeX citation key for this article. const std::string& bibKey() const { return _bibKey;} /// Set the BibTeX citation key for this article. void setBibKey(const std::string& bibKey) { _bibKey = bibKey; } /// BibTeX citation entry for this article. const std::string& bibTeX() const { return _bibTeX; } /// Set the BibTeX citation entry for this article. void setBibTeX(const std::string& bibTeX) { _bibTeX = bibTeX; } /// Whether this analysis is trusted (in any way!) const std::string& status() const { return _status; } /// Set the analysis code status. void setStatus(const std::string& status) { _status = status; } /// Any work to be done on this analysis. const std::vector& todos() const { return _todos; } /// Set the to-do list. void setTodos(const std::vector& todos) { _todos = todos; } /// Get the option list. const std::vector& options() const { return _options; } /// Check if the given option is valid. bool validOption(std::string key, std::string val) const; /// Set the option list. void setOptions(const std::vector& opts) { _options = opts; buildOptionMap(); } /// Build a map of options to facilitate checking. void buildOptionMap(); + /// List a series of command lines to be used for valdation + const vector & validation(); /// Return true if this analysis needs to know the process cross-section. bool needsCrossSection() const { return _needsCrossSection; } /// Return true if this analysis needs to know the process cross-section. void setNeedsCrossSection(bool needXsec) { _needsCrossSection = needXsec; } /// Return true if finalize() can be run multiple times for this analysis. bool reentrant() const { return _reentrant; } /// setReentrant void setReentrant(bool ree = true) { _reentrant = ree; } + /// Return true if validated + bool validated() const { + return _status.find("VALIDATED") != string::npos && + _status.find("UNVALIDATED") == string::npos; + } + + /// Return true if preliminary + bool preliminary() const { + return _status.find("PRELIMINARY") != string::npos; + } + + /// Return true if obsolete + bool obsolete() const { + return _status.find("OBSOLETE") != string::npos; + } + + /// Return true if unvalidated + bool unvalidated() const { + return _status.find("UNVALIDATED") != string::npos; + } + + /// Return true if includes random variations + bool random() const { + return _status.find("RANDOM") != string::npos; + } + //@} private: std::string _name; std::string _refDataName; std::string _spiresId, _inspireId; std::vector _authors; std::string _summary; std::string _description; std::string _runInfo; std::string _experiment; std::string _collider; std::vector > _beams; std::vector > _energies; std::string _year; std::string _luminosityfb; std::vector _references; std::vector _keywords; std::string _bibKey; std::string _bibTeX; //std::string _bibTeXBody; ///< Was thinking of avoiding duplication of BibKey... std::string _status; std::vector _todos; bool _needsCrossSection; std::vector _options; std::map< std::string, std::set > _optionmap; + + std::vector _validation; bool _reentrant; void clear() { _name = ""; _refDataName = ""; _spiresId = ""; _inspireId = ""; _authors.clear(); _summary = ""; _description = ""; _runInfo = ""; _experiment = ""; _collider = ""; _beams.clear(); _energies.clear(); _year = ""; _luminosityfb = ""; _references.clear(); _keywords.clear(); _bibKey = ""; _bibTeX = ""; //_bibTeXBody = ""; _status = ""; _todos.clear(); _needsCrossSection = false; _options.clear(); _optionmap.clear(); + _validation.clear(); _reentrant = false; } }; /// String representation std::string toString(const AnalysisInfo& ai); /// Stream an AnalysisInfo as a text description inline std::ostream& operator<<(std::ostream& os, const AnalysisInfo& ai) { os << toString(ai); return os; } } #endif diff --git a/src/Core/AnalysisHandler.cc b/src/Core/AnalysisHandler.cc --- a/src/Core/AnalysisHandler.cc +++ b/src/Core/AnalysisHandler.cc @@ -1,583 +1,583 @@ // -*- C++ -*- #include "Rivet/Config/RivetCommon.hh" #include "Rivet/AnalysisHandler.hh" #include "Rivet/Analysis.hh" #include "Rivet/Tools/ParticleName.hh" #include "Rivet/Tools/BeamConstraint.hh" #include "Rivet/Tools/Logging.hh" #include "Rivet/Projections/Beam.hh" #include "YODA/IO.h" namespace Rivet { AnalysisHandler::AnalysisHandler(const string& runname) : _runname(runname), _eventcounter("/_EVTCOUNT"), _xs(NAN), _xserr(NAN), _initialised(false), _ignoreBeams(false), _dumpPeriod(0), _dumping(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"); _eventcounter.reset(); // Check that analyses are beam-compatible, and remove those that aren't const size_t num_anas_requested = analysisNames().size(); vector anamestodelete; 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()); } } 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 for (const AnaHandle a : analyses()) { - if (toUpper(a->status()) == "PRELIMINARY") { + if ( a->info().preliminary() ) { MSG_WARNING("Analysis '" << a->name() << "' is preliminary: be careful, it may change and/or be renamed!"); - } else if (toUpper(a->status()) == "OBSOLETE") { + } else if ( a->info().obsolete() ) { MSG_WARNING("Analysis '" << a->name() << "' is obsolete: please update!"); - } else if (toUpper(a->status()).find("UNVALIDATED") != string::npos) { + } else if (( a->info().unvalidated() ) ) { MSG_WARNING("Analysis '" << a->name() << "' is unvalidated: be careful, it may be broken!"); } } // Initialize the remaining 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 (if we're checking beams) if ( !_ignoreBeams ) { 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 _eventcounter.fill(event.weight()); MSG_DEBUG("Event #" << _eventcounter.numEntries() << " weight = " << event.weight()); // Cross-section #if defined ENABLE_HEPMC_3 if (ge.cross_section()) { //@todo HepMC3::GenCrossSection methods aren't const accessible :( RivetHepMC::GenCrossSection gcs = *(event.genEvent()->cross_section()); _xs = gcs.xsec(); _xserr = gcs.xsec_err(); } #elif defined 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 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()); } if ( _dumpPeriod > 0 && numEvents()%_dumpPeriod == 0 ) { MSG_INFO("Dumping intermediate results to " << _dumpFile << "."); _dumping = true; finalize(); _dumping = false; writeData(_dumpFile); } } void AnalysisHandler::analyze(const GenEvent* ge) { if (ge == nullptr) { MSG_ERROR("AnalysisHandler received null pointer to GenEvent"); //throw Error("AnalysisHandler received null pointer to GenEvent"); } analyze(*ge); } void AnalysisHandler::finalize() { if (!_initialised) return; // First we make copies of all analysis objects. map backupAOs; for (auto ao : getData(false, true) ) backupAOs[ao->path()] = AnalysisObjectPtr(ao->newclone()); // Now we run the (re-entrant) finalize() functions for all analyses. MSG_INFO("Finalising analyses"); for (AnaHandle a : _analyses) { a->setCrossSection(_xs); try { if ( !_dumping || a->info().reentrant() ) a->finalize(); else if ( _dumping ) MSG_INFO("Skipping periodic dump of " << a->name() << " as it is not declared reentrant."); } catch (const Error& err) { cerr << "Error in " << a->name() << "::finalize method: " << err.what() << endl; exit(1); } } // Now we copy all analysis objects to the list of finalized // ones, and restore the value to their original ones. _finalizedAOs.clear(); for ( auto ao : getData() ) _finalizedAOs.push_back(AnalysisObjectPtr(ao->newclone())); for ( auto ao : getData(false, true) ) { // TODO: This should be possible to do in a nicer way, with a flag etc. if (ao->path().find("/FINAL") != std::string::npos) continue; auto aoit = backupAOs.find(ao->path()); if ( aoit == backupAOs.end() ) { AnaHandle ana = analysis(split(ao->path(), "/")[0]); if ( ana ) ana->removeAnalysisObject(ao->path()); } else copyao(aoit->second, ao); } // Print out number of events processed const int nevts = _eventcounter.numEntries(); MSG_INFO("Processed " << nevts << " event" << (nevts != 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, std::map pars) { // Make an option handle. std::string parHandle = ""; for (map::iterator par = pars.begin(); par != pars.end(); ++par) { parHandle +=":"; parHandle += par->first + "=" + par->second; } return addAnalysis(analysisname + parHandle); } 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. string ananame = analysisname; vector anaopt = split(analysisname, ":"); if ( anaopt.size() > 1 ) ananame = anaopt[0]; AnaHandle analysis( AnalysisLoader::getAnalysis(ananame) ); if (analysis.get() != 0) { // < Check for null analysis. MSG_DEBUG("Adding analysis '" << analysisname << "'"); map opts; for ( int i = 1, N = anaopt.size(); i < N; ++i ) { vector opt = split(anaopt[i], "="); if ( opt.size() != 2 ) { MSG_WARNING("Error in option specification. Skipping analysis " << analysisname); return *this; } if ( !analysis->info().validOption(opt[0], opt[1]) ) { MSG_WARNING("Cannot set option '" << opt[0] << "' to '" << opt[1] << "'. Skipping analysis " << analysisname); return *this; } opts[opt[0]] = opt[1]; } for ( auto opt: opts) { analysis->_options[opt.first] = opt.second; analysis->_optstring += ":" + opt.first + "=" + opt.second; } for (const AnaHandle& a : _analyses) { if (a->name() == analysis->name() ) { MSG_WARNING("Analysis '" << analysisname << "' already registered: skipping duplicate"); return *this; } } analysis->_analysishandler = this; _analyses.insert(analysis); } else { MSG_WARNING("Analysis '" << analysisname << "' not found."); } // MSG_WARNING(_analyses.size()); // for (const AnaHandle& a : _analyses) MSG_WARNING(a->name()); return *this; } AnalysisHandler& AnalysisHandler::removeAnalysis(const string& analysisname) { std::shared_ptr toremove; 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; } ///////////////////////////// void AnalysisHandler::addData(const std::vector& aos) { for (const AnalysisObjectPtr ao : aos) { string path = ao->path(); if ( path.substr(0, 5) != "/RAW/" ) { _orphanedPreloads.push_back(ao); continue; } path = path.substr(4); ao->setPath(path); if (path.size() > 1) { // path > "/" try { const string ananame = split(path, "/")[0]; AnaHandle a = analysis(ananame); a->addAnalysisObject(ao); /// @todo Need to statistically merge... } catch (const Error& e) { MSG_TRACE("Adding analysis object " << path << " to the list of orphans."); _orphanedPreloads.push_back(ao); } } } } void AnalysisHandler::stripOptions(AnalysisObjectPtr ao, const vector & delopts) const { string path = ao->path(); string ananame = split(path, "/")[0]; vector anaopts = split(ananame, ":"); for ( int i = 1, N = anaopts.size(); i < N; ++i ) for ( auto opt : delopts ) if ( opt == "*" || anaopts[i].find(opt + "=") == 0 ) path.replace(path.find(":" + anaopts[i]), (":" + anaopts[i]).length(), ""); ao->setPath(path); } void AnalysisHandler:: mergeYodas(const vector & aofiles, const vector & delopts, bool equiv) { vector< vector > aosv; vector xsecs; vector xsecerrs; vector sows; set ananames; _eventcounter.reset(); // First scan all files and extract analysis objects and add the // corresponding anayses.. for ( auto file : aofiles ) { Scatter1DPtr xsec; CounterPtr sow; // For each file make sure that cross section and sum-of-weights // objects are present and stor all RAW ones in a vector; vector aos; try { /// @todo Use new YODA SFINAE to fill the smart ptr vector directly vector aos_raw; YODA::read(file, aos_raw); for (AnalysisObject* aor : aos_raw) { AnalysisObjectPtr ao = AnalysisObjectPtr(aor); if ( ao->path().substr(0, 5) != "/RAW/" ) continue; ao->setPath(ao->path().substr(4)); if ( ao->path() == "/_XSEC" ) xsec = dynamic_pointer_cast(ao); else if ( ao->path() == "/_EVTCOUNT" ) sow = dynamic_pointer_cast(ao); else { stripOptions(ao, delopts); string ananame = split(ao->path(), "/")[0]; if ( ananames.insert(ananame).second ) addAnalysis(ananame); aos.push_back(ao); } } if ( !xsec || !sow ) { MSG_ERROR( "Error in AnalysisHandler::mergeYodas: The file " << file << " did not contain weights and cross section info."); exit(1); } xsecs.push_back(xsec->point(0).x()); sows.push_back(sow); xsecerrs.push_back(sqr(xsec->point(0).xErrAvg())); _eventcounter += *sow; sows.push_back(sow); aosv.push_back(aos); } catch (...) { //< YODA::ReadError& throw UserError("Unexpected error in reading file: " + file); } } // Now calculate the scale to be applied for all bins in a file // and get the common cross section and sum of weights. _xs = _xserr = 0.0; for ( int i = 0, N = sows.size(); i < N; ++i ) { double effnent = sows[i]->effNumEntries(); _xs += (equiv? effnent: 1.0)*xsecs[i]; _xserr += (equiv? sqr(effnent): 1.0)*xsecerrs[i]; } vector scales(sows.size(), 1.0); if ( equiv ) { _xs /= _eventcounter.effNumEntries(); _xserr = sqrt(_xserr)/_eventcounter.effNumEntries(); } else { _xserr = sqrt(_xserr); for ( int i = 0, N = sows.size(); i < N; ++i ) scales[i] = (_eventcounter.sumW()/sows[i]->sumW())*(xsecs[i]/_xs); } // Initialize the analyses allowing them to book analysis objects. for (AnaHandle a : _analyses) { MSG_DEBUG("Initialising analysis: " << a->name()); if ( !a->info().reentrant() ) MSG_WARNING("Analysis " << a->name() << " has not been validated to have " << "a reentrant finalize method. The result is unpredictable."); try { // Allow projection registration in the init phase onwards a->_allowProjReg = true; cerr << "sqrtS " << sqrtS() << endl; 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; // Get a list of all anaysis objects to handle. map current; for ( auto ao : getData(false, true) ) current[ao->path()] = ao; // Go through all objects to be merged and add them to current // after appropriate scaling. for ( int i = 0, N = aosv.size(); i < N; ++i) for ( auto ao : aosv[i] ) { if ( ao->path() == "/_XSEC" || ao->path() == "_EVTCOUNT" ) continue; auto aoit = current.find(ao->path()); if ( aoit == current.end() ) { MSG_WARNING("" << ao->path() << " was not properly booked."); continue; } if ( !addaos(aoit->second, ao, scales[i]) ) MSG_WARNING("Cannot merge objects with path " << ao->path() <<" of type " << ao->annotation("Type") ); } // Now we can simply finalize() the analysis, leaving the // controlling program to write it out some yoda-file. finalize(); } void AnalysisHandler::readData(const string& filename) { vector aos; try { /// @todo Use new YODA SFINAE to fill the smart ptr vector directly vector aos_raw; YODA::read(filename, aos_raw); for (AnalysisObject* aor : aos_raw) aos.push_back(AnalysisObjectPtr(aor)); } catch (...) { //< YODA::ReadError& throw UserError("Unexpected error in reading file: " + filename); } if (!aos.empty()) addData(aos); } vector AnalysisHandler:: getData(bool includeorphans, bool includetmps) const { vector rtn; // Event counter rtn.push_back( make_shared(_eventcounter) ); // Cross-section + err as scatter YODA::Scatter1D::Points pts; pts.insert(YODA::Point1D(_xs, _xserr)); rtn.push_back( make_shared(pts, "/_XSEC") ); // Analysis histograms for (const AnaHandle a : analyses()) { vector aos = a->analysisObjects(); // MSG_WARNING(a->name() << " " << aos.size()); for (const AnalysisObjectPtr ao : aos) { // Exclude paths from final write-out if they contain a "TMP" layer (i.e. matching "/TMP/") /// @todo This needs to be much more nuanced for re-entrant histogramming if ( !includetmps && ao->path().find("/TMP/" ) != string::npos) continue; rtn.push_back(ao); } } // Sort histograms alphanumerically by path before write-out sort(rtn.begin(), rtn.end(), [](AnalysisObjectPtr a, AnalysisObjectPtr b) {return a->path() < b->path();}); if ( includeorphans ) rtn.insert(rtn.end(), _orphanedPreloads.begin(), _orphanedPreloads.end()); return rtn; } void AnalysisHandler::writeData(const string& filename) const { vector out = _finalizedAOs; out.reserve(2*out.size()); vector aos = getData(false, true); for ( auto ao : aos ) { ao = AnalysisObjectPtr(ao->newclone()); ao->setPath("/RAW" + ao->path()); out.push_back(ao); } try { YODA::write(filename, out.begin(), out.end()); } catch (...) { //< YODA::WriteError& throw UserError("Unexpected error in writing file: " + filename); } } std::vector AnalysisHandler::analysisNames() const { std::vector rtn; for (AnaHandle a : _analyses) { rtn.push_back(a->name()); } return rtn; } const AnaHandle AnalysisHandler::analysis(const std::string& analysisname) const { 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& analysisnames) { for (const string& aname : analysisnames) { //MSG_DEBUG("Adding analysis '" << aname << "'"); addAnalysis(aname); } return *this; } AnalysisHandler& AnalysisHandler::removeAnalyses(const std::vector& analysisnames) { for (const string& aname : analysisnames) { removeAnalysis(aname); } return *this; } bool AnalysisHandler::needCrossSection() const { bool rtn = false; for (const AnaHandle a : _analyses) { if (!rtn) rtn = a->needsCrossSection(); if (rtn) break; } return rtn; } AnalysisHandler& AnalysisHandler::setCrossSection(double xs, double xserr) { _xs = xs; _xserr = xserr; 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/AnalysisInfo.cc b/src/Core/AnalysisInfo.cc --- a/src/Core/AnalysisInfo.cc +++ b/src/Core/AnalysisInfo.cc @@ -1,183 +1,188 @@ #include "Rivet/Config/RivetCommon.hh" #include "Rivet/AnalysisInfo.hh" #include "Rivet/Tools/RivetPaths.hh" #include "Rivet/Tools/Utils.hh" #include "Rivet/Tools/Logging.hh" #include "yaml-cpp/yaml.h" #include #include #include #ifdef YAML_NAMESPACE #define YAML YAML_NAMESPACE #endif namespace Rivet { namespace { Log& getLog() { return Log::getLog("Rivet.AnalysisInfo"); } } /// Static factory method unique_ptr AnalysisInfo::make(const std::string& ananame) { // Returned AI, in semi-null state unique_ptr ai( new AnalysisInfo ); ai->_beams += make_pair(PID::ANY, PID::ANY); ai->_name = ananame; /// If no ana data file found, return null AI const string datapath = findAnalysisInfoFile(ananame + ".info"); if (datapath.empty()) { MSG_DEBUG("No datafile " << ananame + ".info found"); return ai; } // Read data from YAML document MSG_DEBUG("Reading analysis data from " << datapath); YAML::Node doc; try { doc = YAML::LoadFile(datapath); } catch (const YAML::ParserException& ex) { MSG_ERROR("Parse error when reading analysis data from " << datapath << " (" << ex.what() << ")"); return ai; } #define THROW_INFOERR(KEY) throw InfoError("Problem in info parsing while accessing key " + string(KEY) + " in file " + datapath) // Simple scalars (test for nullness before casting) #define TRY_GETINFO(KEY, VAR) try { if (doc[KEY] && !doc[KEY].IsNull()) ai->_ ## VAR = doc[KEY].as(); } catch (...) { THROW_INFOERR(KEY); } TRY_GETINFO("Name", name); TRY_GETINFO("Summary", summary); TRY_GETINFO("Status", status); TRY_GETINFO("RunInfo", runInfo); TRY_GETINFO("Description", description); TRY_GETINFO("Experiment", experiment); TRY_GETINFO("Collider", collider); TRY_GETINFO("Year", year); TRY_GETINFO("Luminosity_fb", luminosityfb); TRY_GETINFO("SpiresID", spiresId); TRY_GETINFO("InspireID", inspireId); TRY_GETINFO("BibKey", bibKey); TRY_GETINFO("BibTeX", bibTeX); #undef TRY_GETINFO + ai->_status = toUpper(ai->_status); + // Sequences (test the seq *and* each entry for nullness before casting) #define TRY_GETINFO_SEQ(KEY, VAR) try { \ if (doc[KEY] && !doc[KEY].IsNull()) { \ const YAML::Node& VAR = doc[KEY]; \ for (size_t i = 0; i < VAR.size(); ++i) \ if (!VAR[i].IsNull()) ai->_ ## VAR += VAR[i].as(); \ } } catch (...) { THROW_INFOERR(KEY); } TRY_GETINFO_SEQ("Authors", authors); TRY_GETINFO_SEQ("References", references); TRY_GETINFO_SEQ("ToDo", todos); TRY_GETINFO_SEQ("Keywords", keywords); TRY_GETINFO_SEQ("Options", options); + TRY_GETINFO_SEQ("Validation", validation); #undef TRY_GETINFO_SEQ // Build the option map ai->buildOptionMap(); // A boolean with some name flexibility try { if (doc["NeedsCrossSection"]) ai->_needsCrossSection = doc["NeedsCrossSection"].as(); else if (doc["NeedCrossSection"]) ai->_needsCrossSection = doc["NeedCrossSection"].as(); if (doc["Reentrant"]) ai->_reentrant = doc["Reentrant"].as(); } catch (...) { THROW_INFOERR("NeedsCrossSection|NeedCrossSection|Reentrant"); } + // Check if reentrant + if ( ai->_status.find("REENTRANT") != string::npos ) ai->_reentrant = true; // Beam particle identities try { if (doc["Beams"]) { const YAML::Node& beams = doc["Beams"]; vector beam_pairs; if (beams.size() == 2 && beams[0].IsScalar() && beams[0].IsScalar()) { beam_pairs += PID::make_pdgid_pair(beams[0].as(), beams[1].as()); } else { for (size_t i = 0; i < beams.size(); ++i) { const YAML::Node& bp = beams[i]; if (bp.size() != 2 || !bp[0].IsScalar() || !bp[0].IsScalar()) throw InfoError("Beam ID pairs have to be either a 2-tuple or a list of 2-tuples of particle names"); beam_pairs += PID::make_pdgid_pair(bp[0].as(), bp[1].as()); } } ai->_beams = beam_pairs; } } catch (...) { THROW_INFOERR("Beams"); } // Beam energies try { if (doc["Energies"]) { vector< pair > beam_energy_pairs; for (size_t i = 0; i < doc["Energies"].size(); ++i) { const YAML::Node& be = doc["Energies"][i]; if (be.IsScalar()) { // If beam energy is a scalar, then assume symmetric beams each with half that energy beam_energy_pairs += make_pair(be.as()/2.0, be.as()/2.0); } else if (be.IsSequence()) { if (be.size() != 2) throw InfoError("Beam energies have to be a list of either numbers or pairs of numbers"); beam_energy_pairs += make_pair(be[0].as(), be[1].as()); } else { throw InfoError("Beam energies have to be a list of either numbers or pairs of numbers"); } } ai->_energies = beam_energy_pairs; } } catch (...) { THROW_INFOERR("Energies"); } #undef THROW_INFOERR MSG_TRACE("AnalysisInfo pointer = " << ai.get()); return ai; } string toString(const AnalysisInfo& ai) { std::stringstream ss; ss << ai.name(); ss << " - " << ai.summary(); // ss << " - " << ai.beams(); // ss << " - " << ai.energies(); ss << " (" << ai.status() << ")"; return ss.str(); } void AnalysisInfo::buildOptionMap() { _optionmap.clear(); for ( auto opttag : _options ) { std::vector optv = split(opttag, "="); std::string optname = optv[0]; for ( auto opt : split(optv[1], ",") ) _optionmap[optname].insert(opt); } } bool AnalysisInfo::validOption(std::string key, std::string val) const { auto opt = _optionmap.find(key); // The option is required to be defined in the .info file. if ( opt == _optionmap.end() ) return false; // If the selection option is among the range of given options, // we are fine. if ( opt->second.find(val) != opt->second.end() ) return true; // Wild card selection option for value types is #. if ( opt->second.size() == 1 && *opt->second.begin() == "#" ) { std::istringstream ss(val); double test; if ( ss >> test ) return true; } // Wild card selection option for any type is *. if ( opt->second.size() == 1 && *opt->second.begin() == "*" ) return true; return false; } }