#include #include #include #include #include #include "TROOT.h" #include "TFile.h" #include "TH1D.h" #include "TH2D.h" #include "TProfile.h" #include "TMath.h" #include "km3net-dataformat/offline/Head.hh" #include "km3net-dataformat/offline/Evt.hh" #include "km3net-dataformat/online/JDAQModuleIdentifier.hh" #include "km3net-dataformat/definitions/reconstruction.hh" #include "km3net-dataformat/definitions/fitparameters.hh" #include "km3net-dataformat/tools/time_converter.hh" #include "JAAnet/JHead.hh" #include "JAAnet/JHeadToolkit.hh" #include "JAAnet/JAAnetToolkit.hh" #include "JAAnet/JVolume.hh" #include "JDAQ/JDAQEventIO.hh" #include "JPhysics/JConstants.hh" #include "JTools/JQuantile.hh" #include "JSupport/JTriggeredFileScanner.hh" #include "JSupport/JMonteCarloFileSupportkit.hh" #include "JSupport/JSupport.hh" #include "JReconstruction/JEvt.hh" #include "JReconstruction/JEvtToolkit.hh" #include "JLang/JPredicate.hh" #include "JPhysics/JGeanz.hh" #include "JGeometry3D/JShower3E.hh" #include "JGeometry3D/JCylinder3D.hh" #include "Jeep/JParser.hh" #include "Jeep/JMessage.hh" namespace { using KM3NETDAQ::JDAQEvent; /** * Count number of unique identifiers in event. * * \param __begin begin of data * \param __end end of data * \return number of unique identifiers */ template inline int getCount(JDAQEvent::const_iterator __begin, JDAQEvent::const_iterator __end) { using namespace std; using namespace KM3NETDAQ; typedef JDAQModuleIdentifier JType_t; set buffer; for (JDAQEvent::const_iterator i = __begin; i != __end; ++i) { buffer.insert(JType_t(*i)); } return buffer.size(); } } /** * \file * * Example program to histogram shower fit results: it handles both neutrino and muon productions. * * \author mdejong, adomi */ int main(int argc, char **argv) { using namespace std; using namespace JPP; using namespace KM3NETDAQ; typedef JTriggeredFileScanner JTriggeredFileScanner_t; typedef JTriggeredFileScanner_t::multi_pointer_type multi_pointer_type; JTriggeredFileScanner_t inputFile; JLimit_t& numberOfEvents = inputFile.getLimit(); string outputFile; size_t numberOfPrefits; JQualitySorter quality_sorter; JAtmosphericMuon atmosphere; double Emin_GeV; double NPE; int application; string option; bool isMuon; bool wrtNeutrino; double radius; JRange height; int debug; try { JParser<> zap("Example program to histogram fit results."); zap['f'] = make_field(inputFile); zap['o'] = make_field(outputFile) = "postfit.root"; zap['n'] = make_field(numberOfEvents) = JLimit::max(); zap['N'] = make_field(numberOfPrefits) = 1; zap['L'] = make_field(quality_sorter) = JPARSER::initialised(); zap['E'] = make_field(Emin_GeV) = 0.0; zap['M'] = make_field(NPE) = 0.0; zap['A'] = make_field(application) = JSHOWERPREFIT, JSHOWERPOINTSIMPLEX, JSHOWERPOSITIONFIT, JSHOWERENERGYPREFIT, JSHOWERDIRECTIONPREFIT, JSHOWERCOMPLETEFIT, JSHOWER_BJORKEN_Y; zap['a'] = make_field(atmosphere) = JAtmosphericMuon(90.0, 90.0); zap['O'] = make_field(option) = "E", "N", "LINE", "LOGE"; zap['I'] = make_field(isMuon); zap['w'] = make_field(wrtNeutrino); zap['r'] = make_field(radius) = JLimit::max(); // no containment zap['z'] = make_field(height) = JRange(-JLimit::max(), JLimit::max()); // no containment zap['d'] = make_field(debug) = 2; zap(argc, argv); } catch(const exception& error) { FATAL(error.what() << endl); } cout << "APPLICATION " << application << endl; JHead head; try { head = getHeader(inputFile); } catch(const JException& error) { FATAL(error); } const JVolume volume(head, option != "LINE"); const JPosition3D center = get(head); JCylinder3D cylinder = get(head); cylinder.add(center); NOTICE("Center: " << center << endl); NOTICE("Reposition can [m]: " << cylinder << endl); const double EMIN_GEV = 10e3; // MUPAGE TFile out(outputFile.c_str(), "recreate"); TH1D job("job", NULL, 100, 0.5, 100.5); TH1D hn("hn", "NDF (Number of Degrees of Freedom)", 250, -0.5, 249.5); TH1D hq("hq", "Fit Quality", 300, 0.0, 600.0); TH1D hi("hi", "Fit Index vs Best Resolution", 101, -0.5, 100.5); // to see if the fit with the best quality has also the best resolution TH1D hd("hd", "Square Distance between Reco and True position", 2000, 0.0, 400.0); // [m] TH1D hz("hz", "Longitudinal Distance between Reco and MC lepton position", 100, -200.0, 200.0); // [m] TH1D ht("ht", "Time difference between Reco and MC lepton", 100, -100.0, 100.0); // [ns] TH1D ha("ha", "Angle between reco direction and true direction", 1000, 0.0, 180.0); // [ns] TH1D e0("e0", "True lepton energy", 100, volume.getXmin(), volume.getXmax()); // [log(E)] TH1D e2("e2", "Reconstructed energy", 100, volume.getXmin(), volume.getXmax()); // [log(E)] TH1D er("er", "Ratio of reconstructed energy and true energy", 100, -5.0, +5.0); // [log(E/E)] TH1D ea("ea", "Distribution of Energy error for all the events; E_{reco}-E_{true}", 100, -30, +30); TH1D ed3_5GeV("ed3_5GeV", "Distribution of Energy error for events with MC energy #in [3,5] GeV; E_{reco}-E_{true}", 100, -30, +30); TH1D ed8_11GeV("ed8_11GeV", "Distribution of Energy error for events with MC energy #in [8,11] GeV; E_{reco}-E_{true}", 100, -30, +30); TH1D ed15_21GeV("ed15_21GeV", "Distribution of Energy error for events with MC energy #in [15,21] GeV; E_{reco}-E_{true}", 100, -30, +30); TH2D ee("ee", "; E_{true} [GeV]; E_{reco} [GeV]", 40, volume.getXmin(), volume.getXmax(), 40, volume.getXmin(), volume.getXmax()); const Int_t ny = 28800; const Double_t ymin = 0.0; // [deg] const Double_t ymax = 180.0; // [deg] vector X; if (option.find('E') != string::npos) { for (double x = volume.getXmin(); x <= volume.getXmax(); x += (volume.getXmax() - volume.getXmin()) / 20) { X.push_back(x); } } else { double x = -0.5; for ( ; x <= 15.5; x += 1.0) { X.push_back(x); } for ( ; x <= 25.5; x += 2.0) { X.push_back(x); } for ( ; x <= 50.5; x += 5.0) { X.push_back(x); } for ( ; x <= 100.5; x += 10.0) { X.push_back(x); } for ( ; x <= 250.5; x += 20.0) { X.push_back(x); } } TH2D h2("h2", NULL, X.size() - 1, X.data(), ny, ymin, ymax); TProfile herrorE("herrorE", "Energy Error", X.size() - 1, X.data()); TProfile hfracE( "hfracE", "Fractional Energy Error", X.size() - 1, X.data()); TProfile he("he","Reconstruction Efficiency", X.size() - 1, X.data()); TProfile htheta_nu_lep("htheta_nu_lep", "Angle between neutrino and primary lepton", X.size() - 1, X.data()); TH2D hb("hb", NULL, 100, 0.0, 0.5e6, 100, -100.0, +900.0); TH2S hmca("hmca", NULL, X.size() - 1, X.data(), 1000, 0, 180); TH2D hzenith("hzenith", "Reco Zenith vs MC Energy", 100, 0, 100, ny, ymin, ymax); TH2D hY("hY", "Reco Bjorken Y vs MC Bjorken Y", 40, 0, 1, 40, 0, 1); TH2D hby3_5GeV("hby3_5GeV", "Reco Bjorken Y vs MC Bjorken Y for events with Reco E #in [3, 5] GeV", 40, 0, 1, 40, 0, 1); TH2D hby8_10GeV("hby8_10GeV", "Reco Bjorken Y vs MC Bjorken Y for events with Reco E #in [8, 10] GeV", 40, 0, 1, 40, 0, 1); JQuantile Q("Angle", true); JQuantile O("Omega", true); while (inputFile.hasNext()) { STATUS("event: " << setw(10) << inputFile.getCounter() << '\r'); DEBUG(endl); multi_pointer_type ps = inputFile.next(); JDAQEvent* tev = ps; JEvt* evt = ps; // Reco event Evt* event = ps; // MC event const time_converter converter(*event, *tev); job.Fill(1.0); double Enu = 0.0; double Elep = 0.0; double Emax = 0.0; Trk neutrino; if(has_neutrino(*event)){ neutrino = get_neutrino(*event); Enu = neutrino.E; } vector::const_iterator lepton = event->mc_trks.end(); // can be electron or muon for (vector::const_iterator mc_evt = event->mc_trks.begin(); mc_evt != event->mc_trks.end(); ++mc_evt) { if (!isMuon && is_electron(*mc_evt)) { Elep += mc_evt->E; if (mc_evt->E > Emax) { lepton = mc_evt; Emax = mc_evt->E; } } else if(isMuon && is_muon(*mc_evt)){ Elep += mc_evt->E; if (mc_evt->E > Emax) { lepton = mc_evt; Emax = mc_evt->E; } } } if (lepton == event->mc_trks.end()) { continue; } job.Fill(3.0); double true_BjY = (Enu - Elep) / Enu; // abscissa Double_t x = 0.0; if (option.find('E') != string::npos){ if(wrtNeutrino == true && !isMuon) x = volume.getX(neutrino.E); else if(!wrtNeutrino && !isMuon) x = volume.getX(lepton->E); } else{ x = getCount(tev->begin(), tev->end()); } // weight for efficiency determination Double_t W = 0.0; if (!evt->empty()) { JEvt::iterator __end = partition(evt->begin(), evt->end(), JHistory::is_event(application)); if (evt->begin() == __end) { continue; } job.Fill(4.0); //if (numberOfPrefits > 0 && numberOfPrefits < (size_t) distance(evt->begin(), evt->end())) { if (numberOfPrefits > 0 ) { JEvt::iterator __q = __end; advance(__end = evt->begin(), min(numberOfPrefits, (size_t) distance(evt->begin(), __q))); partial_sort(evt->begin(), __end, __q, quality_sorter); } else { sort(evt->begin(), __end, quality_sorter); } JEvt::iterator best = evt->begin(); // the best shower is the one with the best quality JPosition position(getPosition(lepton->pos)); // by default the lepton pos is the interaction vertex JPointing pointing(getDirection(*lepton)); JShowerEnergy energy(lepton->E); JVector3D showerBrightPoint(getPosition(lepton->pos) + center + getPosition(lepton->dir) * geanz.getMaximum(lepton->E)); if(!wrtNeutrino && !isMuon){ position = JPosition(showerBrightPoint); // wrt the em shower brightest point } else if(wrtNeutrino == true && !isMuon){ position = JPosition(getPosition(neutrino)); pointing = JPointing(getDirection(neutrino)); energy = JShowerEnergy(Enu); } JEvt::iterator fit_with_smallest_error = best; if(application == JSHOWERPREFIT || application == JSHOWERPOINTSIMPLEX || application == JSHOWERPOSITIONFIT){ fit_with_smallest_error = position(evt->begin(), __end); } else if (application == JSHOWERENERGYPREFIT){ fit_with_smallest_error = energy(evt->begin(), __end); } else { fit_with_smallest_error = pointing(evt->begin(), __end); } const Double_t beta = pointing.getAngle(*best); const double Efit = best->getE(); JCylinder3D containment(JCircle2D(getPosition(*best), radius), height.getLowerLimit(), height.getUpperLimit()); if(containment.is_inside(getPosition(*best))){ // selection of fit result bool ok = (Efit >= Emin_GeV); if (ok) { W = 1.0; job.Fill(5.0); hn.Fill((Double_t) best->getNDF()); hq.Fill(best->getQ()); hi.Fill((Double_t) distance(evt->begin(), fit_with_smallest_error)); Q.put(beta); JShower3E ta; if(wrtNeutrino){ ta = getTrack(neutrino); } else { ta = getTrack(*lepton); // MC event } JShower3E tb = getTrack(*best); // Reco event double zenith = tb.getDirection().getTheta() * 180 / PI; // take into account the difefrent MC/Reco geometry and time ta.add(center); tb.sub(converter.putTime()); if(!isMuon && !wrtNeutrino){ double distance_m = (tb.getPosition() - JPosition3D(showerBrightPoint)).getLength(); hd.Fill(distance_m * distance_m); } else { double distance_m = (tb.getPosition() - ta.getPosition()).getLength(); hd.Fill(distance_m * distance_m); } if (has_neutrino(*event)) { job.Fill(6.0); JPosition3D mc_vx = getPosition(neutrino); // mc vertex mc_vx.add(center); if (cylinder.is_inside(mc_vx)) { job.Fill(7.0); hz.Fill(tb.getIntersection(mc_vx)); } } if (best->getE() >= EMIN_GEV) { hb.Fill(best->getX()*best->getX() + best->getY()*best->getY(), best->getZ()); } ht.Fill(tb.getT() - ta.getT()); ha.Fill(getAngle(ta.getDirection(), tb.getDirection())); htheta_nu_lep.Fill(x, getAngle(getDirection(neutrino), getDirection(*lepton))); hmca.Fill(x, getAngle(ta.getDirection(), tb.getDirection())); if(application == JSHOWER_BJORKEN_Y) { hY.Fill(true_BjY, best->getW(5)); if(Efit > 2.5 && Efit < 6) hby3_5GeV.Fill(true_BjY, best->getW(5)); else if(Efit > 7.5 && Efit < 11) hby8_10GeV.Fill(true_BjY, best->getW(5)); } if(wrtNeutrino){ e0.Fill(volume.getX(Enu, true)); er.Fill(volume.getX(Efit) - volume.getX(Enu)); ee.Fill(volume.getX(Enu), volume.getX(Efit)); ea.Fill(Efit - Enu); hzenith.Fill(Enu, zenith); herrorE.Fill(x, volume.getX(Efit) - volume.getX(Enu)); hfracE.Fill(x, fabs(volume.getX(Efit) - volume.getX(Enu))/volume.getX(Enu)); if(Enu >= 3 && Enu <= 5) ed3_5GeV.Fill(Efit - Enu); else if(Enu >= 8 && Enu <= 11) ed8_11GeV.Fill(Efit - Enu); else if(Enu >= 15 && Enu <= 21) ed15_21GeV.Fill(Efit - Enu); } else { e0.Fill(volume.getX(Elep, true)); er.Fill(volume.getX(Efit) - volume.getX(Elep)); ee.Fill(volume.getX(Elep), volume.getX(Efit)); ea.Fill(Efit - Elep); hzenith.Fill(Elep, zenith); herrorE.Fill(x, volume.getX(Efit) - volume.getX(Elep)); hfracE.Fill(x, fabs(volume.getX(Efit) - volume.getX(Elep))/volume.getX(Elep)); if(Elep >= 3 && Elep <= 5) ed3_5GeV.Fill(Efit - Elep); else if(Elep >= 8 && Elep <= 11) ed8_11GeV.Fill(Efit - Elep); else if(Elep >= 15 && Elep <= 21) ed15_21GeV.Fill(Efit - Elep); } e2.Fill(volume.getX(Efit, true)); h2.Fill(x, beta); } } } he.Fill(x, W);} STATUS(endl); NOTICE("Number of events input " << setw(8) << right << job.GetBinContent(1) << endl); if(!isMuon){ NOTICE("Number of events with electron " << setw(8) << right << job.GetBinContent(3) << endl); } else{ NOTICE("Number of events with muon " << setw(8) << right << job.GetBinContent(3) << endl); } NOTICE("Number of events with fit " << setw(8) << right << job.GetBinContent(4) << endl); NOTICE("Number of events selected " << setw(8) << right << job.GetBinContent(5) << endl); NOTICE("Number of events with neutrino " << setw(8) << right << job.GetBinContent(6) << endl); NOTICE("Number of events contained " << setw(8) << right << job.GetBinContent(7) << endl); if (Q.getCount() != 0) { NOTICE("Median space angle [deg] " << FIXED (6,3) << Q.getQuantile(0.5) << endl); } out.Write(); out.Close(); }