#include #include #include #include #include #include "TROOT.h" #include "TApplication.h" #include "TCanvas.h" #include "TStyle.h" #include "TH2D.h" #include "TArrow.h" #include "TLatex.h" #include "TMarker.h" #include "km3net-dataformat/offline/Head.hh" #include "km3net-dataformat/offline/Evt.hh" #include "km3net-dataformat/online/JDAQ.hh" #include "km3net-dataformat/tools/time_converter.hh" #include "JROOT/JStyle.hh" #include "JROOT/JCanvas.hh" #include "JROOT/JRootToolkit.hh" #include "JDAQ/JDAQEventIO.hh" #include "JDAQ/JDAQSummarysliceIO.hh" #include "JDetector/JDetector.hh" #include "JDetector/JDetectorToolkit.hh" #include "JDetector/JModuleRouter.hh" #include "JTrigger/JHitL0.hh" #include "JTrigger/JBuildL0.hh" #include "JSupport/JSupport.hh" #include "JSupport/JParallelFileScanner.hh" #include "JSupport/JSummaryFileRouter.hh" #include "JSupport/JMonteCarloFileSupportkit.hh" #include "JAAnet/JAAnetToolkit.hh" #include "JPhysics/JPDF_t.hh" #include "JFit/JLine1Z.hh" #include "JFit/JModel.hh" #include "JReconstruction/JHitW0.hh" #include "JReconstruction/JEvt.hh" #include "JReconstruction/JEvtToolkit.hh" #include "JReconstruction/JMuonGandalfParameters_t.hh" #include "JLang/JPredicate.hh" #include "JLang/JComparator.hh" #include "JMath/JMathToolkit.hh" #include "JSystem/JKeypress.hh" #include "JSystem/JProcess.hh" #include "Jeep/JFunctionAdaptor.hh" #include "Jeep/JPrint.hh" #include "Jeep/JParser.hh" #include "Jeep/JMessage.hh" namespace { /** * Wild card character for file name substition. */ const char WILDCARD = '%'; using JRECONSTRUCTION::JEvt; using JEEP::JFunctionAdaptor; /** * Event selector. * * The default constructor will accept all events.\n * A different method can dynamically be loaded from a (shared) library using class JEEP::JFunctionAdaptor. * For the definition of an alternative method, see e.g.\ event_selector.cc */ struct JEventSelector : public JFunctionAdaptor { typedef JFunctionAdaptor function_adaptor_type; /** * Default event selection. * * \param in input event * \param event pointer to Monte Carlo event * \return true */ static inline bool accept(const JEvt& in, const Evt* event) { return true; } /** * Default constructor. */ JEventSelector() : JFunctionAdaptor(accept) {} /** * Check validity of function. * * \return true if valid function; else false */ bool is_valid() const { return (libso != "" && function_adaptor_type::is_valid()); } /** * Re-load function from shared library. */ void reload() { reset(); load(libso); } /** * Read event selector from input stream. * * \param in input stream * \param object event selector * \return input stream */ friend std::istream& operator>>(std::istream& in, JEventSelector& object) { using namespace std; object.reset(); if (in >> object.libso) { if (object.libso.find(function_adaptor_type::SEPARATOR) == string::npos) { object.libso += MAKE_CSTRING(function_adaptor_type::SEPARATOR << "accept"); } object.load(object.libso); } return in; } private: std::string libso; //!< shared library : symbol }; /** * Execute command in shell. * * \param command command */ inline void execute(const std::string& command, int debug) { using namespace std; using namespace JPP; JProcess process(command); istream in(process.getInputStreamBuffer()); for (string buffer; getline(in, buffer); ) { DEBUG(buffer << endl); } } } /** * \file * * Program to display hit probabilities. * * \author mdejong */ int main(int argc, char **argv) { using namespace std; using namespace JPP; using namespace KM3NETDAQ; typedef JParallelFileScanner< JTypeList > JParallelFileScanner_t; typedef JParallelFileScanner_t::multi_pointer_type multi_pointer_type; JParallelFileScanner_t inputFile; JLimit_t& numberOfEvents = inputFile.getLimit(); string detectorFile; string pdfFile; string outputFile; JMuonGandalfParameters_t parameters; int application; JEventSelector event_selector; JCanvas canvas; bool batch; double arrowSize; string arrowType; double arrowScale; int debug; try { parameters.numberOfPrefits = 1; JParser<> zap("Program to display hit probabilities."); zap['w'] = make_field(canvas, "size of canvas x [pixels]") = JCanvas(1200, 600); zap['f'] = make_field(inputFile, "input file (output of JXXXMuonReconstruction.sh)"); zap['a'] = make_field(detectorFile); zap['n'] = make_field(numberOfEvents) = JLimit::max(); zap['P'] = make_field(pdfFile); zap['o'] = make_field(outputFile, "graphics output file name") = MAKE_STRING("display_" << WILDCARD << ".gif"); zap['@'] = make_field(parameters) = JPARSER::initialised(); zap['A'] = make_field(application) = JMUONGANDALF, JMUONENERGY, JMUONSTART; zap['L'] = make_field(event_selector) = JPARSER::initialised(); zap['S'] = make_field(arrowSize) = 0.003; zap['T'] = make_field(arrowType) = "|->"; zap['F'] = make_field(arrowScale) = 250.0; zap['B'] = make_field(batch, "batch processing"); zap['d'] = make_field(debug) = 1; zap(argc, argv); } catch(const exception& error) { FATAL(error.what() << endl); } if (batch && outputFile == "") { FATAL("Missing output file name " << outputFile << " in batch mode." << endl); } if (!batch && outputFile == "") { outputFile = MAKE_STRING(WILDCARD << ".gif"); } if (outputFile.find(WILDCARD) == string::npos) { FATAL("Output file name " << outputFile << " has no wild card '" << WILDCARD << "'" << endl); } JDetector detector; try { load(detectorFile, detector); } catch(const JException& error) { FATAL(error); } const JModuleRouter router(detector); JSummaryFileRouter summary(inputFile, parameters.R_Hz); const JMuonPDF_t pdf(pdfFile, parameters.TTS_ns); const JTimeRange T_ns(parameters.TMin_ns, parameters.TMax_ns); typedef vector JDataL0_t; typedef vector JDataW0_t; const JBuildL0 buildL0; // Monte Carlo JVector3D center(0.0, 0.0, 0.0); try { center = get(getHeader(inputFile)); } catch(const exception& error) {} // ROOT gROOT->SetBatch(batch); TApplication* tp = new TApplication("user", NULL, NULL); TCanvas* cv = new TCanvas("display", "", canvas.x, canvas.y); JSinglePointer gStyle(new JStyle("gplot", cv->GetWw(), cv->GetWh())); gROOT->SetStyle("gplot"); gROOT->ForceStyle(); const size_t NUMBER_OF_PADS = 3; cv->SetFillStyle(4000); cv->SetFillColor(kWhite); cv->Divide(NUMBER_OF_PADS, 1); const double Dmax = getMaximalDistance(detector); const double Rmin = 0.0; const double Rmax = min(parameters.roadWidth_m, 0.4 * Dmax); const double Tmin = min(parameters.TMin_ns, -10.0); const double Tmax = max(parameters.TMax_ns, +100.0); const double Amin = 0.002 * (Tmax - Tmin); // minimal arrow length [ns] const double Amax = 0.8 * (Tmax - Tmin); // maximal arrow length [ns] const double ymin = min(-Amax, Tmin - 0.3 * Amax); const double ymax = max(+Amax, Tmax + 0.5 * Amax); const string Xlabel[NUMBER_OF_PADS] = { "R [m]", "#phi [rad]", "z [m]" }; const double Xmin [NUMBER_OF_PADS] = { Rmin, -PI, -0.5 * Dmax }; const double Xmax [NUMBER_OF_PADS] = { Rmax, +PI, +0.5 * Dmax }; double Xs[NUMBER_OF_PADS]; for (size_t i = 0; i != NUMBER_OF_PADS; ++i) { Xs[i] = 0.003 * (Xmax[i] - Xmin[i]) * (0.5 * NUMBER_OF_PMTS); // x-offset arrow as function of PMT number } TH2D H2[NUMBER_OF_PADS]; TGraph G2[NUMBER_OF_PADS]; for (size_t i = 0; i != NUMBER_OF_PADS; ++i) { H2[i] = TH2D(MAKE_CSTRING("h" << i), NULL, 100, Xmin[i] - Xs[i], Xmax[i] + Xs[i], 100, ymin, ymax); H2[i].GetXaxis()->SetTitle(Xlabel[i].c_str()); H2[i].GetYaxis()->SetTitle("#Deltat [ns]"); H2[i].GetXaxis()->CenterTitle(true); H2[i].GetYaxis()->CenterTitle(true); H2[i].SetStats(kFALSE); G2[i].Set(2); G2[i].SetPoint(0, H2[i].GetXaxis()->GetXmin(), 0.0); G2[i].SetPoint(1, H2[i].GetXaxis()->GetXmax(), 0.0); cv->cd(i+1); H2[i].Draw("AXIS"); G2[i].Draw("SAME"); } for (JTreeScanner mc(inputFile); inputFile.hasNext(); ) { cout << "\revent: " << setw(8) << inputFile.getCounter() << flush; multi_pointer_type ps = inputFile.next(); JDAQEvent* tev = ps; JEvt* in = ps; Evt* event = NULL; if (mc.getEntries() != 0) { event = mc.getEntry(tev->getCounter()); // Monte Carlo true information } in->select(JHistory::is_event(application)); if (!in->empty()) { sort(in->begin(), in->end(), qualitySorter); if (!event_selector(*in, event)) { continue; } JDataL0_t dataL0; buildL0(*tev, router, true, back_inserter(dataL0)); summary.update(*tev); JFit muon; // Monte Carlo true muon if (event != NULL) { const time_converter converter = time_converter(*event, *tev); for (const auto& t1 : event->mc_trks) { if (is_muon(t1)) { if (t1.E > muon.getE()) { JTrack3E ta = getTrack(t1); ta.add(center); ta.add(converter.putTime()); muon = getFit(0, ta, 0.0, 0, t1.E, 1); muon.setW(JSTART_LENGTH_METRES, fabs(t1.len)); } } } } bool next_event = false; // goto next event bool monte_carlo = false; // show Monte Carlo true muon size_t index = 0; // index of fit while (!next_event) { JFit fit; if (!monte_carlo) fit = (*in)[index]; else fit = muon; JRotation3D R (getDirection(fit)); JLine1Z tz(getPosition (fit).rotate(R), fit.getT()); JRange Z_m; /* if (fit.hasW(JSTART_LENGTH_METRES)) { Z_m = JRange(0.0, fit.getW(JSTART_LENGTH_METRES)) + JRange(parameters.ZMin_m, parameters.ZMax_m); } */ const JModel match(tz, parameters.roadWidth_m, T_ns, Z_m); // hit selection based on fit result JDataW0_t data; for (JDataL0_t::const_iterator i = dataL0.begin(); i != dataL0.end(); ++i) { JHitW0 hit(*i, summary.getRate(i->getPMTIdentifier())); hit.rotate(R); if (match(hit)) { data.push_back(hit); } } // select first hit in PMT sort(data.begin(), data.end(), JHitW0::compare); JDataW0_t::iterator __end = unique(data.begin(), data.end(), equal_to()); double E_GeV = parameters.E_GeV; /* if (fit.getE() > 0.1) { E_GeV = fit.getE(); } */ // move fit to geometrical center of hits JRange zs(make_array(data.begin(), __end, &JHitW0::getZ)); const double z0 = tz.getZ(); const double z1 = 0.5 * (zs.getLowerLimit() + zs.getUpperLimit()); tz.setZ(z1, getSpeedOfLight()); // graphics TLatex latex [NUMBER_OF_PADS]; vector arrow [NUMBER_OF_PADS]; vector marker[NUMBER_OF_PADS]; for (int i = 0; i != NUMBER_OF_PADS; ++i) { latex[i].SetTextAlign(12); latex[i].SetTextFont(42); latex[i].SetTextSize(0.06); latex[i].SetX(H2[i].GetXaxis()->GetXmin() + 0.05 * (H2[i].GetXaxis()->GetXmax() - H2[i].GetXaxis()->GetXmin())); latex[i].SetY(H2[i].GetYaxis()->GetXmax() - 0.05 * (H2[i].GetYaxis()->GetXmax() - H2[i].GetYaxis()->GetXmin())); } if (fit.hasW(JSTART_LENGTH_METRES) && fit.getW(JSTART_LENGTH_METRES) > 0.0) { marker[2].push_back(TMarker(z0 - tz.getZ(), 0.0, kFullCircle)); marker[2].push_back(TMarker(z0 - tz.getZ() + fit.getW(JSTART_LENGTH_METRES), 0.0, kFullCircle)); static_cast(marker[2][0]) = TAttMarker(kRed, kFullCircle, 0.7); static_cast(marker[2][1]) = TAttMarker(kRed, kFullCircle, 0.7); } double chi2 = 0; for (JDataW0_t::const_iterator hit = data.begin(); hit != __end; ++hit) { const double x = hit->getX() - tz.getX(); const double y = hit->getY() - tz.getY(); const double z = hit->getZ() - tz.getZ(); const double R = sqrt(x*x + y*y); const double t1 = tz.getT() + (z + R * getTanThetaC()) * getInverseSpeedOfLight(); JDirection3D dir(hit->getDX(), hit->getDY(), hit->getDZ()); // PMT orientation dir.rotate(JRotation3Z(-atan2(y,x))); // rotate PMT axis to x-z plane const double theta = dir.getTheta(); const double phi = fabs(dir.getPhi()); // rotational symmetry of Cherenkov cone //const double E = gWater.getE(E_GeV, z); // correct for energy loss const double E = E_GeV; const double dt = T_ns.constrain(hit->getT() - t1); JMuonPDF_t::result_type H1 = pdf.calculate(E, R, theta, phi, dt); JMuonPDF_t::result_type H0(hit->getR() * 1e-9, 0.0, T_ns); if (H1.V >= parameters.VMax_npe) { H1 *= parameters.VMax_npe / H1.V; } H1 += H0; // signal + background chi2 += H1.getChi2() - H0.getChi2(); const double derivative = H1.getDerivativeOfChi2() - H0.getDerivativeOfChi2(); double size = derivative * arrowScale; // size of arrow if (fabs(size) < Amin) { size = (size > 0.0 ? +Amin : -Amin); } else if (fabs(size) > Amax) { size = (size > 0.0 ? +Amax : -Amax); } const double X[NUMBER_OF_PADS] = { R, atan2(y,x), z - R/getTanThetaC() }; const double xs = (double) (NUMBER_OF_PMTS - 2 * hit->getPMTAddress()) / (double) NUMBER_OF_PMTS; for (size_t i = 0; i != NUMBER_OF_PADS; ++i) { arrow[i].push_back(TArrow(X[i] + xs*Xs[i], dt, X[i] + xs*Xs[i], dt + size, arrowSize, arrowType.c_str())); } } latex[0].SetTitle(MAKE_CSTRING("" << FILL(6,'0') << tev->getRunNumber() << FILL() << ":" << tev->getFrameIndex() << " " << tev->getCounter())); if (!monte_carlo) latex[1].SetTitle(MAKE_CSTRING("[" << index << "]" << " " << "Q = " << FIXED(4,0) << getQuality(chi2) << " " << "E = " << SCIENTIFIC(7,1) << fit.getE() << " [GeV]")); else latex[1].SetTitle(MAKE_CSTRING("Q = " << FIXED(4,0) << getQuality(chi2))); if (muon.getStatus() >= 0) { if (!monte_carlo) latex[2].SetTitle(MAKE_CSTRING("#Delta#alpha = " << FIXED(6,2) << getAngle(getDirection(muon), getDirection(fit)) << " [deg]")); else latex[2].SetTitle("Monte Carlo"); } // draw for (int i = 0; i != NUMBER_OF_PADS; ++i) { cv->cd(i+1); latex[i].Draw(); for (auto& a1 : arrow[i]) { a1.Draw(); } for (auto& m1 : marker[i]) { m1.Draw(); } } cv->Update(); // action if (batch) { cv->SaveAs(replace(outputFile, WILDCARD, MAKE_STRING(inputFile.getCounter())).c_str()); next_event = true; } else { for (int c = 0; c == 0; ) { if (!batch) { static bool first = true; if (first) { cout << endl << "Type '?' for possible options." << endl; } first = false; } cout << "\n> " << flush; switch((c = JKeypress(true).get())) { case '?': cout << endl; cout << "possible options: " << endl; cout << 'q' << " -> " << "exit application" << endl; cout << 'u' << " -> " << "update canvas" << endl; cout << 's' << " -> " << "save graphics to file" << endl; cout << '+' << " -> " << "next fit" << endl; cout << '-' << " -> " << "previous fit" << endl; cout << 'M' << " -> " << "Monte Carlo true muon information" << endl; cout << 'F' << " -> " << "fit information" << endl; if (event_selector.is_valid()) { cout << 'L' << " -> " << "reload event selector" << endl; } cout << 'r' << " -> " << "rewind input file" << endl; cout << 'R' << " -> " << "switch to ROOT mode (quit ROOT to continue)" << endl; cout << ' ' << " -> " << "next event (as well as any other key)" << endl; c = 0; break; case 'q': cout << endl; return 0; case 'u': cv->Update(); c = 0; break; case 's': cv->SaveAs(replace(outputFile, WILDCARD, MAKE_STRING(inputFile.getCounter())).c_str()); c = 0; break; case '+': monte_carlo = false; index = (index != in->size() - 1 ? index + 1 : 0); break; case '-': monte_carlo = false; index = (index != 0 ? index - 1 : in->size() - 1); break; case 'M': if (muon.getStatus() >= 0) monte_carlo = true; else ERROR(endl << "No Monte Carlo muon available." << endl); break; case 'F': monte_carlo = false; break; case 'r': inputFile.rewind(); next_event = true; break; case 'L': if (event_selector.is_valid()) { execute(MAKE_STRING("make -f " << getPath(argv[0]) << "/JMakeEventSelector libs"), 3); event_selector.reload(); } break; case 'R': tp->Run(kTRUE); c = 0; break; default: next_event = true; break; } } } } } } cout << endl; }